Migrated networking and fixed networking tests
This commit is contained in:
@ -1,279 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.networking;
|
||||
|
||||
import ch.dissem.bitmessage.BitmessageContext;
|
||||
import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography;
|
||||
import ch.dissem.bitmessage.entity.CustomMessage;
|
||||
import ch.dissem.bitmessage.entity.MessagePayload;
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||
import ch.dissem.bitmessage.exception.NodeException;
|
||||
import ch.dissem.bitmessage.networking.nio.NioNetworkHandler;
|
||||
import ch.dissem.bitmessage.ports.*;
|
||||
import ch.dissem.bitmessage.testutils.TestInventory;
|
||||
import ch.dissem.bitmessage.utils.Property;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.DisableOnDebug;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.rules.Timeout;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests network handlers. This test is parametrized, so it can test both the nio and classic implementation
|
||||
* as well as their combinations. It might be slightly over the top and will most probably be cleaned up once
|
||||
* the nio implementation is deemed stable.
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class NetworkHandlerTest {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(NetworkHandlerTest.class);
|
||||
private static NetworkAddress peerAddress = new NetworkAddress.Builder().ipv4(127, 0, 0, 1).port(6001).build();
|
||||
|
||||
private TestInventory peerInventory;
|
||||
private TestInventory nodeInventory;
|
||||
|
||||
private BitmessageContext peer;
|
||||
private BitmessageContext node;
|
||||
|
||||
private final NetworkHandler peerNetworkHandler;
|
||||
private final NetworkHandler nodeNetworkHandler;
|
||||
|
||||
@Rule
|
||||
public final TestRule timeout = new DisableOnDebug(Timeout.seconds(60));
|
||||
|
||||
public NetworkHandlerTest(NetworkHandler peer, NetworkHandler node) {
|
||||
this.peerNetworkHandler = peer;
|
||||
this.nodeNetworkHandler = node;
|
||||
}
|
||||
|
||||
@Parameterized.Parameters
|
||||
@SuppressWarnings("deprecation")
|
||||
public static List<Object[]> parameters() {
|
||||
return Arrays.asList(new Object[][]{
|
||||
{new DefaultNetworkHandler(), new DefaultNetworkHandler()},
|
||||
{new DefaultNetworkHandler(), new NioNetworkHandler()},
|
||||
{new NioNetworkHandler(), new DefaultNetworkHandler()},
|
||||
{new NioNetworkHandler(), new NioNetworkHandler()}
|
||||
});
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws InterruptedException {
|
||||
peerInventory = new TestInventory();
|
||||
peer = new BitmessageContext.Builder()
|
||||
.addressRepo(mock(AddressRepository.class))
|
||||
.inventory(peerInventory)
|
||||
.messageRepo(mock(MessageRepository.class))
|
||||
.powRepo(mock(ProofOfWorkRepository.class))
|
||||
.port(peerAddress.getPort())
|
||||
.nodeRegistry(new TestNodeRegistry())
|
||||
.networkHandler(peerNetworkHandler)
|
||||
.cryptography(new BouncyCryptography())
|
||||
.listener(mock(BitmessageContext.Listener.class))
|
||||
.customCommandHandler(new CustomCommandHandler() {
|
||||
@Override
|
||||
public MessagePayload handle(CustomMessage request) {
|
||||
byte[] data = request.getData();
|
||||
if (data.length > 0) {
|
||||
switch (data[0]) {
|
||||
case 0:
|
||||
return null;
|
||||
case 1:
|
||||
break;
|
||||
case 3:
|
||||
data[0] = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new CustomMessage("test response", request.getData());
|
||||
}
|
||||
})
|
||||
.build();
|
||||
peer.startup();
|
||||
Thread.sleep(100);
|
||||
|
||||
nodeInventory = new TestInventory();
|
||||
node = new BitmessageContext.Builder()
|
||||
.addressRepo(mock(AddressRepository.class))
|
||||
.inventory(nodeInventory)
|
||||
.messageRepo(mock(MessageRepository.class))
|
||||
.powRepo(mock(ProofOfWorkRepository.class))
|
||||
.port(6002)
|
||||
.nodeRegistry(new TestNodeRegistry(peerAddress))
|
||||
.networkHandler(nodeNetworkHandler)
|
||||
.cryptography(new BouncyCryptography())
|
||||
.listener(mock(BitmessageContext.Listener.class))
|
||||
.build();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
shutdown(peer);
|
||||
shutdown(node);
|
||||
shutdown(nodeNetworkHandler);
|
||||
}
|
||||
|
||||
private static void shutdown(BitmessageContext ctx) {
|
||||
if (!ctx.isRunning()) return;
|
||||
|
||||
ctx.shutdown();
|
||||
do {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ignore) {
|
||||
}
|
||||
} while (ctx.isRunning());
|
||||
}
|
||||
|
||||
private static void shutdown(NetworkHandler networkHandler) {
|
||||
if (!networkHandler.isRunning()) return;
|
||||
|
||||
networkHandler.stop();
|
||||
do {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ignore) {
|
||||
if (networkHandler.isRunning()) {
|
||||
LOG.warn("Thread interrupted while waiting for network shutdown - " +
|
||||
"this could cause problems in subsequent tests.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
} while (networkHandler.isRunning());
|
||||
}
|
||||
|
||||
private Property waitForNetworkStatus(BitmessageContext ctx) throws InterruptedException {
|
||||
Property status;
|
||||
do {
|
||||
Thread.sleep(100);
|
||||
status = ctx.status().getProperty("network", "connections", "stream 1");
|
||||
} while (status == null);
|
||||
return status;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureNodesAreConnecting() throws Exception {
|
||||
node.startup();
|
||||
|
||||
Property nodeStatus = waitForNetworkStatus(node);
|
||||
Property peerStatus = waitForNetworkStatus(peer);
|
||||
|
||||
assertEquals(1, nodeStatus.getProperty("outgoing").getValue());
|
||||
assertEquals(1, peerStatus.getProperty("incoming").getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureCustomMessageIsSentAndResponseRetrieved() throws Exception {
|
||||
byte[] data = cryptography().randomBytes(8);
|
||||
data[0] = (byte) 1;
|
||||
CustomMessage request = new CustomMessage("test request", data);
|
||||
node.startup();
|
||||
|
||||
CustomMessage response = nodeNetworkHandler.send(peerAddress.toInetAddress(), peerAddress.getPort(), request);
|
||||
|
||||
assertThat(response, notNullValue());
|
||||
assertThat(response.getCustomCommand(), is("test response"));
|
||||
assertThat(response.getData(), is(data));
|
||||
}
|
||||
|
||||
@Test(expected = NodeException.class)
|
||||
public void ensureCustomMessageWithoutResponseYieldsException() throws Exception {
|
||||
byte[] data = cryptography().randomBytes(8);
|
||||
data[0] = (byte) 0;
|
||||
CustomMessage request = new CustomMessage("test request", data);
|
||||
|
||||
CustomMessage response = nodeNetworkHandler.send(peerAddress.toInetAddress(), peerAddress.getPort(), request);
|
||||
|
||||
assertThat(response, notNullValue());
|
||||
assertThat(response.getCustomCommand(), is("test response"));
|
||||
assertThat(response.getData(), is(request.getData()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureObjectsAreSynchronizedIfBothHaveObjects() throws Exception {
|
||||
peerInventory.init(
|
||||
"V4Pubkey.payload",
|
||||
"V5Broadcast.payload"
|
||||
);
|
||||
|
||||
nodeInventory.init(
|
||||
"V1Msg.payload",
|
||||
"V4Pubkey.payload"
|
||||
);
|
||||
|
||||
Future<?> future = nodeNetworkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(), 10);
|
||||
future.get();
|
||||
assertInventorySize(3, nodeInventory);
|
||||
assertInventorySize(3, peerInventory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureObjectsAreSynchronizedIfOnlyPeerHasObjects() throws Exception {
|
||||
peerInventory.init(
|
||||
"V4Pubkey.payload",
|
||||
"V5Broadcast.payload"
|
||||
);
|
||||
|
||||
nodeInventory.init();
|
||||
|
||||
Future<?> future = nodeNetworkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(), 10);
|
||||
future.get();
|
||||
assertInventorySize(2, nodeInventory);
|
||||
assertInventorySize(2, peerInventory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureObjectsAreSynchronizedIfOnlyNodeHasObjects() throws Exception {
|
||||
peerInventory.init();
|
||||
|
||||
nodeInventory.init(
|
||||
"V1Msg.payload"
|
||||
);
|
||||
|
||||
Future<?> future = nodeNetworkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(), 10);
|
||||
future.get();
|
||||
assertInventorySize(1, nodeInventory);
|
||||
assertInventorySize(1, peerInventory);
|
||||
}
|
||||
|
||||
private void assertInventorySize(int expected, TestInventory inventory) throws InterruptedException {
|
||||
long timeout = System.currentTimeMillis() + 1000;
|
||||
while (expected != inventory.getInventory().size() && System.currentTimeMillis() < timeout) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
assertEquals(expected, inventory.getInventory().size());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.networking
|
||||
|
||||
import ch.dissem.bitmessage.BitmessageContext
|
||||
import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography
|
||||
import ch.dissem.bitmessage.entity.CustomMessage
|
||||
import ch.dissem.bitmessage.entity.MessagePayload
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress
|
||||
import ch.dissem.bitmessage.exception.NodeException
|
||||
import ch.dissem.bitmessage.networking.nio.NioNetworkHandler
|
||||
import ch.dissem.bitmessage.ports.*
|
||||
import ch.dissem.bitmessage.testutils.TestInventory
|
||||
import ch.dissem.bitmessage.utils.Property
|
||||
import ch.dissem.bitmessage.utils.Singleton.cryptography
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import org.hamcrest.Matchers.`is`
|
||||
import org.hamcrest.Matchers.notNullValue
|
||||
import org.junit.After
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.DisableOnDebug
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.rules.Timeout
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
/**
|
||||
* Tests network handlers. This test is parametrized, so it can test both the nio and classic implementation
|
||||
* as well as their combinations. It might be slightly over the top and will most probably be cleaned up once
|
||||
* the nio implementation is deemed stable.
|
||||
*/
|
||||
class NetworkHandlerTest {
|
||||
|
||||
private lateinit var peerInventory: TestInventory
|
||||
private lateinit var nodeInventory: TestInventory
|
||||
|
||||
private lateinit var peer: BitmessageContext
|
||||
private lateinit var node: BitmessageContext
|
||||
|
||||
private lateinit var peerNetworkHandler: NetworkHandler
|
||||
private lateinit var nodeNetworkHandler: NetworkHandler
|
||||
|
||||
@JvmField @Rule val timeout: TestRule = DisableOnDebug(Timeout.seconds(60))
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
peerInventory = TestInventory()
|
||||
peerNetworkHandler = NioNetworkHandler()
|
||||
peer = BitmessageContext(
|
||||
cryptography = BouncyCryptography(),
|
||||
inventory = peerInventory,
|
||||
nodeRegistry = TestNodeRegistry(),
|
||||
networkHandler = peerNetworkHandler,
|
||||
addressRepository = mock<AddressRepository>(),
|
||||
messageRepository = mock<MessageRepository>(),
|
||||
proofOfWorkRepository = mock<ProofOfWorkRepository>(),
|
||||
customCommandHandler = object : CustomCommandHandler {
|
||||
override fun handle(request: CustomMessage): MessagePayload? {
|
||||
val data = request.getData()
|
||||
if (data.isNotEmpty()) {
|
||||
when (data[0]) {
|
||||
0.toByte() -> return null
|
||||
1.toByte() -> {
|
||||
}
|
||||
3.toByte() -> data[0] = 0
|
||||
}
|
||||
}
|
||||
return CustomMessage("test response", request.getData())
|
||||
}
|
||||
},
|
||||
listener = mock<BitmessageContext.Listener>(),
|
||||
port = peerAddress.port
|
||||
)
|
||||
peer.startup()
|
||||
Thread.sleep(100)
|
||||
|
||||
nodeInventory = TestInventory()
|
||||
nodeNetworkHandler = NioNetworkHandler()
|
||||
node = BitmessageContext(
|
||||
cryptography = BouncyCryptography(),
|
||||
inventory = nodeInventory,
|
||||
nodeRegistry = TestNodeRegistry(peerAddress),
|
||||
networkHandler = nodeNetworkHandler,
|
||||
addressRepository = mock<AddressRepository>(),
|
||||
messageRepository = mock<MessageRepository>(),
|
||||
proofOfWorkRepository = mock<ProofOfWorkRepository>(),
|
||||
customCommandHandler = object : CustomCommandHandler {
|
||||
override fun handle(request: CustomMessage): MessagePayload? {
|
||||
val data = request.getData()
|
||||
if (data.isNotEmpty()) {
|
||||
when (data[0]) {
|
||||
0.toByte() -> return null
|
||||
1.toByte() -> {
|
||||
}
|
||||
3.toByte() -> data[0] = 0
|
||||
}
|
||||
}
|
||||
return CustomMessage("test response", request.getData())
|
||||
}
|
||||
},
|
||||
listener = mock<BitmessageContext.Listener>(),
|
||||
port = 6002
|
||||
)
|
||||
}
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
shutdown(peer)
|
||||
shutdown(node)
|
||||
shutdown(nodeNetworkHandler)
|
||||
}
|
||||
|
||||
private fun waitForNetworkStatus(ctx: BitmessageContext): Property {
|
||||
var status: Property?
|
||||
do {
|
||||
Thread.sleep(100)
|
||||
status = ctx.status().getProperty("network", "connections", "stream 1")
|
||||
} while (status == null)
|
||||
return status
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure nodes are connecting`() {
|
||||
node.startup()
|
||||
|
||||
val nodeStatus = waitForNetworkStatus(node)
|
||||
val peerStatus = waitForNetworkStatus(peer)
|
||||
|
||||
assertEquals(1, nodeStatus.getProperty("outgoing")!!.value)
|
||||
assertEquals(1, peerStatus.getProperty("incoming")!!.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure CustomMessage is sent and response retrieved`() {
|
||||
val data = cryptography().randomBytes(8)
|
||||
data[0] = 1.toByte()
|
||||
val request = CustomMessage("test request", data)
|
||||
node.startup()
|
||||
|
||||
val response = nodeNetworkHandler.send(peerAddress.toInetAddress(), peerAddress.port, request)
|
||||
|
||||
assertThat(response, notNullValue())
|
||||
assertThat(response.customCommand, `is`("test response"))
|
||||
assertThat(response.getData(), `is`(data))
|
||||
}
|
||||
|
||||
@Test(expected = NodeException::class)
|
||||
fun `ensure CustomMessage without response yields exception`() {
|
||||
val data = cryptography().randomBytes(8)
|
||||
data[0] = 0.toByte()
|
||||
val request = CustomMessage("test request", data)
|
||||
|
||||
val response = nodeNetworkHandler.send(peerAddress.toInetAddress(), peerAddress.port, request)
|
||||
|
||||
assertThat(response, notNullValue())
|
||||
assertThat(response.customCommand, `is`("test response"))
|
||||
assertThat(response.getData(), `is`(request.getData()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure objects are synchronized if both have objects`() {
|
||||
peerInventory.init(
|
||||
"V4Pubkey.payload",
|
||||
"V5Broadcast.payload"
|
||||
)
|
||||
|
||||
nodeInventory.init(
|
||||
"V1Msg.payload",
|
||||
"V4Pubkey.payload"
|
||||
)
|
||||
|
||||
val future = nodeNetworkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.port, 10)
|
||||
future.get()
|
||||
assertInventorySize(3, nodeInventory)
|
||||
assertInventorySize(3, peerInventory)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure objects are synchronized if only peer has objects`() {
|
||||
peerInventory.init(
|
||||
"V4Pubkey.payload",
|
||||
"V5Broadcast.payload"
|
||||
)
|
||||
|
||||
nodeInventory.init()
|
||||
|
||||
val future = nodeNetworkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.port, 10)
|
||||
future.get()
|
||||
assertInventorySize(2, nodeInventory)
|
||||
assertInventorySize(2, peerInventory)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure objects are synchronized if only node has objects`() {
|
||||
peerInventory.init()
|
||||
|
||||
nodeInventory.init(
|
||||
"V1Msg.payload"
|
||||
)
|
||||
|
||||
val future = nodeNetworkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.port, 10)
|
||||
future.get()
|
||||
assertInventorySize(1, nodeInventory)
|
||||
assertInventorySize(1, peerInventory)
|
||||
}
|
||||
|
||||
private fun assertInventorySize(expected: Int, inventory: TestInventory) {
|
||||
val timeout = System.currentTimeMillis() + 1000
|
||||
while (expected != inventory.getInventory().size && System.currentTimeMillis() < timeout) {
|
||||
Thread.sleep(10)
|
||||
}
|
||||
assertEquals(expected.toLong(), inventory.getInventory().size.toLong())
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = LoggerFactory.getLogger(NetworkHandlerTest::class.java)
|
||||
private val peerAddress = NetworkAddress.Builder().ipv4(127, 0, 0, 1).port(6001).build()
|
||||
|
||||
private fun shutdown(ctx: BitmessageContext) {
|
||||
if (!ctx.isRunning) return
|
||||
|
||||
ctx.shutdown()
|
||||
do {
|
||||
try {
|
||||
Thread.sleep(100)
|
||||
} catch (ignore: InterruptedException) {
|
||||
}
|
||||
|
||||
} while (ctx.isRunning)
|
||||
}
|
||||
|
||||
private fun shutdown(networkHandler: NetworkHandler) {
|
||||
if (!networkHandler.isRunning) return
|
||||
|
||||
networkHandler.stop()
|
||||
do {
|
||||
try {
|
||||
Thread.sleep(100)
|
||||
} catch (ignore: InterruptedException) {
|
||||
if (networkHandler.isRunning) {
|
||||
LOG.warn("Thread interrupted while waiting for network shutdown - " + "this could cause problems in subsequent tests.")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
} while (networkHandler.isRunning)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user