From 0fadb40c6cca6d97c0f8aa02c8ba7695fea93663 Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Thu, 16 Jun 2016 19:47:59 +0200 Subject: [PATCH] Improved tests and fixed some --- core/build.gradle | 2 +- .../ch/dissem/bitmessage/entity/Version.java | 1 - .../ch/dissem/bitmessage/factory/Factory.java | 2 +- .../bitmessage/factory/V3MessageReader.java | 31 ++-- .../ports/AbstractCryptography.java | 2 +- cryptography-bc/build.gradle | 2 +- cryptography-sc/build.gradle | 2 +- demo/build.gradle | 2 +- .../java/ch/dissem/bitmessage/SystemTest.java | 4 +- extensions/build.gradle | 2 +- networking/build.gradle | 2 +- .../networking/AbstractConnection.java | 35 ++-- .../networking/nio/ConnectionInfo.java | 18 +- .../networking/nio/NioNetworkHandler.java | 173 ++++++++++++++---- .../networking/NetworkHandlerTest.java | 59 ++++-- wif/build.gradle | 2 +- 16 files changed, 234 insertions(+), 105 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index dd100e6..73f8fdf 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -25,7 +25,7 @@ artifacts { dependencies { compile 'org.slf4j:slf4j-api:1.7.12' - testCompile 'junit:junit:4.11' + testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-library:1.3' testCompile 'org.mockito:mockito-core:1.10.19' testCompile project(':cryptography-bc') diff --git a/core/src/main/java/ch/dissem/bitmessage/entity/Version.java b/core/src/main/java/ch/dissem/bitmessage/entity/Version.java index 48022c4..4d0fd05 100644 --- a/core/src/main/java/ch/dissem/bitmessage/entity/Version.java +++ b/core/src/main/java/ch/dissem/bitmessage/entity/Version.java @@ -24,7 +24,6 @@ import ch.dissem.bitmessage.utils.UnixTime; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; -import java.util.Random; /** * The 'version' command advertises this node's latest supported protocol version upon initiation. diff --git a/core/src/main/java/ch/dissem/bitmessage/factory/Factory.java b/core/src/main/java/ch/dissem/bitmessage/factory/Factory.java index 0597f6a..07b3161 100644 --- a/core/src/main/java/ch/dissem/bitmessage/factory/Factory.java +++ b/core/src/main/java/ch/dissem/bitmessage/factory/Factory.java @@ -40,7 +40,7 @@ import static ch.dissem.bitmessage.utils.Singleton.cryptography; * Creates {@link NetworkMessage} objects from {@link InputStream InputStreams} */ public class Factory { - public static final Logger LOG = LoggerFactory.getLogger(Factory.class); + private static final Logger LOG = LoggerFactory.getLogger(Factory.class); public static NetworkMessage getNetworkMessage(int version, InputStream stream) throws SocketTimeoutException { try { diff --git a/core/src/main/java/ch/dissem/bitmessage/factory/V3MessageReader.java b/core/src/main/java/ch/dissem/bitmessage/factory/V3MessageReader.java index 7006cdb..80220f1 100644 --- a/core/src/main/java/ch/dissem/bitmessage/factory/V3MessageReader.java +++ b/core/src/main/java/ch/dissem/bitmessage/factory/V3MessageReader.java @@ -52,23 +52,21 @@ public class V3MessageReader { state = ReaderState.HEADER; case HEADER: if (buffer.remaining() < 20) { - buffer.compact(); return; } command = getCommand(buffer); length = (int) Decode.uint32(buffer); if (length > MAX_PAYLOAD_SIZE) { - throw new NodeException("Payload of " + length + " bytes received, no more than 1600003 was expected."); + throw new NodeException("Payload of " + length + " bytes received, no more than " + + MAX_PAYLOAD_SIZE + " was expected."); } checksum = new byte[4]; buffer.get(checksum); state = ReaderState.DATA; - if (buffer.remaining() < length) { - // We need to compact the buffer to make sure the message fits even if it's really big. - buffer.compact(); - } case DATA: - if (buffer.remaining() < length) return; + if (buffer.remaining() < length) { + return; + } if (!testChecksum(buffer)) { throw new NodeException("Checksum failed for message '" + command + "'"); } @@ -95,34 +93,35 @@ public class V3MessageReader { private boolean findMagicBytes(ByteBuffer buffer) { int i = 0; while (buffer.hasRemaining()) { - if (buffer.get() == MAGIC_BYTES[i]) { + if (i == 0) { buffer.mark(); + } + if (buffer.get() == MAGIC_BYTES[i]) { i++; - if (i == MAGIC_BYTES.length) return true; + if (i == MAGIC_BYTES.length) { + return true; + } } else { i = 0; } } if (i > 0) { buffer.reset(); - buffer.compact(); - } else { - buffer.clear(); } return false; } private static String getCommand(ByteBuffer buffer) { int start = buffer.position(); - int i = 0; - while (i < 12 && buffer.get() != 0) i++; - int end = start + i; + int l = 0; + while (l < 12 && buffer.get() != 0) l++; + int i = l + 1; while (i < 12) { if (buffer.get() != 0) throw new NodeException("'\\0' padding expected for command"); i++; } try { - return new String(buffer.array(), start, end, "ASCII"); + return new String(buffer.array(), start, l, "ASCII"); } catch (UnsupportedEncodingException e) { throw new ApplicationException(e); } diff --git a/core/src/main/java/ch/dissem/bitmessage/ports/AbstractCryptography.java b/core/src/main/java/ch/dissem/bitmessage/ports/AbstractCryptography.java index f67b6d4..b02b12d 100644 --- a/core/src/main/java/ch/dissem/bitmessage/ports/AbstractCryptography.java +++ b/core/src/main/java/ch/dissem/bitmessage/ports/AbstractCryptography.java @@ -43,7 +43,7 @@ import static ch.dissem.bitmessage.utils.Numbers.max; * Implements everything that isn't directly dependent on either Spongy- or Bouncycastle. */ public abstract class AbstractCryptography implements Cryptography, InternalContext.ContextHolder { - public static final Logger LOG = LoggerFactory.getLogger(Cryptography.class); + protected static final Logger LOG = LoggerFactory.getLogger(Cryptography.class); private static final SecureRandom RANDOM = new SecureRandom(); private static final BigInteger TWO = BigInteger.valueOf(2); private static final BigInteger TWO_POW_64 = TWO.pow(64); diff --git a/cryptography-bc/build.gradle b/cryptography-bc/build.gradle index c09b0db..0b87c17 100644 --- a/cryptography-bc/build.gradle +++ b/cryptography-bc/build.gradle @@ -13,6 +13,6 @@ uploadArchives { dependencies { compile project(':core') compile 'org.bouncycastle:bcprov-jdk15on:1.52' - testCompile 'junit:junit:4.11' + testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' } diff --git a/cryptography-sc/build.gradle b/cryptography-sc/build.gradle index 16771fc..a052c2e 100644 --- a/cryptography-sc/build.gradle +++ b/cryptography-sc/build.gradle @@ -13,5 +13,5 @@ uploadArchives { dependencies { compile project(':core') compile 'com.madgag.spongycastle:prov:1.52.0.0' - testCompile 'junit:junit:4.11' + testCompile 'junit:junit:4.12' } diff --git a/demo/build.gradle b/demo/build.gradle index c1571ed..0cf1781 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -32,6 +32,6 @@ dependencies { compile 'args4j:args4j:2.32' compile 'com.h2database:h2:1.4.190' compile 'org.apache.commons:commons-lang3:3.4' - testCompile 'junit:junit:4.11' + testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' } diff --git a/demo/src/test/java/ch/dissem/bitmessage/SystemTest.java b/demo/src/test/java/ch/dissem/bitmessage/SystemTest.java index 03cbf5e..f2f02f2 100644 --- a/demo/src/test/java/ch/dissem/bitmessage/SystemTest.java +++ b/demo/src/test/java/ch/dissem/bitmessage/SystemTest.java @@ -88,7 +88,7 @@ public class SystemTest { bob.shutdown(); } - @Test + @Test(timeout = 60_000) public void ensureAliceCanSendMessageToBob() throws Exception { String originalMessage = UUID.randomUUID().toString(); alice.send(aliceIdentity, new BitmessageAddress(bobIdentity.getAddress()), "Subject", originalMessage); @@ -102,7 +102,7 @@ public class SystemTest { .markAsAcknowledged(any()); } - @Test + @Test(timeout = 30_000) public void ensureBobCanReceiveBroadcastFromAlice() throws Exception { String originalMessage = UUID.randomUUID().toString(); bob.addSubscribtion(new BitmessageAddress(aliceIdentity.getAddress())); diff --git a/extensions/build.gradle b/extensions/build.gradle index d44f900..42b175b 100644 --- a/extensions/build.gradle +++ b/extensions/build.gradle @@ -28,7 +28,7 @@ uploadArchives { dependencies { compile project(':core') - testCompile 'junit:junit:4.11' + testCompile 'junit:junit:4.12' testCompile 'org.slf4j:slf4j-simple:1.7.12' testCompile 'org.mockito:mockito-core:1.10.19' testCompile project(path: ':core', configuration: 'testArtifacts') diff --git a/networking/build.gradle b/networking/build.gradle index 984f585..49cafa5 100644 --- a/networking/build.gradle +++ b/networking/build.gradle @@ -12,7 +12,7 @@ uploadArchives { dependencies { compile project(':core') - testCompile 'junit:junit:4.11' + testCompile 'junit:junit:4.12' testCompile 'org.slf4j:slf4j-simple:1.7.12' testCompile 'org.mockito:mockito-core:1.10.19' testCompile project(path: ':core', configuration: 'testArtifacts') diff --git a/networking/src/main/java/ch/dissem/bitmessage/networking/AbstractConnection.java b/networking/src/main/java/ch/dissem/bitmessage/networking/AbstractConnection.java index 3027f04..54897cb 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/AbstractConnection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/AbstractConnection.java @@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentLinkedDeque; import static ch.dissem.bitmessage.InternalContext.NETWORK_EXTRA_BYTES; import static ch.dissem.bitmessage.InternalContext.NETWORK_NONCE_TRIALS_PER_BYTE; +import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.SERVER; import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.SYNC; import static ch.dissem.bitmessage.networking.AbstractConnection.State.*; import static ch.dissem.bitmessage.utils.Singleton.cryptography; @@ -62,6 +63,8 @@ public abstract class AbstractConnection { protected long peerNonce; protected int version; protected long[] streams; + private boolean verackSent; + private boolean verackReceived; public AbstractConnection(InternalContext context, Mode mode, NetworkAddress node, @@ -198,7 +201,7 @@ public abstract class AbstractConnection { return ivCache.containsKey(iv); } - protected void cleanupIvCache() { + private void cleanupIvCache() { Long fiveMinutesAgo = UnixTime.now(-5 * MINUTE); for (Map.Entry entry : ivCache.entrySet()) { if (entry.getValue() < fiveMinutesAgo) { @@ -213,16 +216,10 @@ public abstract class AbstractConnection { handleVersion((Version) payload); break; case VERACK: - switch (mode) { - case SERVER: - activateConnection(); - break; - case CLIENT: - case SYNC: - default: - // NO OP - break; + if (verackSent) { + activateConnection(); } + verackReceived = true; break; case CUSTOM: MessagePayload response = ctx.getCustomCommandHandler().handle((CustomMessage) payload); @@ -237,7 +234,7 @@ public abstract class AbstractConnection { } } - protected void activateConnection() { + private void activateConnection() { LOG.info("Successfully established connection with node " + node); state = ACTIVE; node.setTime(UnixTime.now()); @@ -272,17 +269,13 @@ public abstract class AbstractConnection { this.version = version.getVersion(); this.streams = version.getStreams(); + verackSent = true; send(new VerAck()); - switch (mode) { - case SERVER: - send(new Version.Builder().defaults(ctx.getClientNonce()).addrFrom(host).addrRecv(node).build()); - break; - case CLIENT: - case SYNC: - activateConnection(); - break; - default: - // NO OP + if (mode == SERVER) { + send(new Version.Builder().defaults(ctx.getClientNonce()).addrFrom(host).addrRecv(node).build()); + } + if (verackReceived) { + activateConnection(); } } else { LOG.info("Received unsupported version " + version.getVersion() + ", disconnecting."); diff --git a/networking/src/main/java/ch/dissem/bitmessage/networking/nio/ConnectionInfo.java b/networking/src/main/java/ch/dissem/bitmessage/networking/nio/ConnectionInfo.java index 223722a..36bb463 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/nio/ConnectionInfo.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/nio/ConnectionInfo.java @@ -19,6 +19,7 @@ package ch.dissem.bitmessage.networking.nio; import ch.dissem.bitmessage.InternalContext; import ch.dissem.bitmessage.entity.MessagePayload; import ch.dissem.bitmessage.entity.NetworkMessage; +import ch.dissem.bitmessage.entity.Version; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; import ch.dissem.bitmessage.factory.V3MessageReader; @@ -26,9 +27,12 @@ import ch.dissem.bitmessage.networking.AbstractConnection; import ch.dissem.bitmessage.ports.NetworkHandler; import java.nio.ByteBuffer; -import java.util.*; -import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.Iterator; +import java.util.Queue; +import java.util.Set; +import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.CLIENT; +import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.SYNC; import static ch.dissem.bitmessage.ports.NetworkHandler.MAX_MESSAGE_SIZE; /** @@ -43,6 +47,10 @@ public class ConnectionInfo extends AbstractConnection { NetworkAddress node, NetworkHandler.MessageListener listener, Set commonRequestedObjects) { super(context, mode, node, listener, commonRequestedObjects, false); + out.flip(); + if (mode == CLIENT || mode == SYNC) { + send(new Version.Builder().defaults(peerNonce).addrFrom(host).addrRecv(node).build()); + } } public State getState() { @@ -77,12 +85,8 @@ public class ConnectionInfo extends AbstractConnection { } } - public List getMessages() { - return reader.getMessages(); - } - @Override protected void send(MessagePayload payload) { - sendingQueue.addFirst(payload); + sendingQueue.add(payload); } } diff --git a/networking/src/main/java/ch/dissem/bitmessage/networking/nio/NioNetworkHandler.java b/networking/src/main/java/ch/dissem/bitmessage/networking/nio/NioNetworkHandler.java index c3689af..cfb6c2e 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/nio/NioNetworkHandler.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/nio/NioNetworkHandler.java @@ -37,11 +37,14 @@ import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.util.*; -import java.util.concurrent.Future; +import java.util.concurrent.*; +import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.CLIENT; import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.SERVER; +import static ch.dissem.bitmessage.networking.AbstractConnection.Mode.SYNC; import static ch.dissem.bitmessage.networking.AbstractConnection.State.ACTIVE; import static ch.dissem.bitmessage.utils.DebugUtils.inc; +import static ch.dissem.bitmessage.utils.ThreadFactoryBuilder.pool; import static java.nio.channels.SelectionKey.OP_READ; import static java.nio.channels.SelectionKey.OP_WRITE; @@ -51,13 +54,40 @@ import static java.nio.channels.SelectionKey.OP_WRITE; public class NioNetworkHandler implements NetworkHandler, InternalContext.ContextHolder { private static final Logger LOG = LoggerFactory.getLogger(NioNetworkHandler.class); + private final ExecutorService pool = Executors.newCachedThreadPool( + pool("network") + .lowPrio() + .daemon() + .build()); + private InternalContext ctx; private Selector selector; private ServerSocketChannel serverChannel; @Override - public Future synchronize(InetAddress server, int port, MessageListener listener, long timeoutInSeconds) { - return null; + public Future synchronize(final InetAddress server, final int port, final MessageListener listener, long timeoutInSeconds) { + return pool.submit(new Callable() { + @Override + public Void call() throws Exception { + Set requestedObjects = new HashSet<>(); + try (SocketChannel channel = SocketChannel.open(new InetSocketAddress(server, port))) { + channel.finishConnect(); + channel.configureBlocking(false); + ConnectionInfo connection = new ConnectionInfo(ctx, SYNC, + new NetworkAddress.Builder().ip(server).port(port).stream(1).build(), + listener, new HashSet()); + while (channel.isConnected() && + (connection.getState() != ACTIVE + || connection.getSendingQueue().isEmpty() + || requestedObjects.isEmpty())) { + write(requestedObjects, channel, connection); + read(channel, connection); + Thread.sleep(10); + } + } + return null; + } + }); } @Override @@ -66,7 +96,9 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex channel.configureBlocking(true); ByteBuffer buffer = ByteBuffer.allocate(MAX_MESSAGE_SIZE); new NetworkMessage(request).write(buffer); - channel.write(buffer); + while (buffer.hasRemaining()) { + channel.write(buffer); + } buffer.clear(); V3MessageReader reader = new V3MessageReader(); @@ -106,34 +138,74 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex throw new ApplicationException(e); } final Set requestedObjects = new HashSet<>(); - new Thread(new Runnable() { + start("connection listener", new Runnable() { @Override public void run() { try { serverChannel = ServerSocketChannel.open(); - serverChannel.bind(new InetSocketAddress(ctx.getPort())); - - SocketChannel accepted = serverChannel.accept(); - accepted.configureBlocking(false); - // FIXME: apparently it isn't good practice to generally listen for OP_WRITE - accepted.register(selector, OP_READ | OP_WRITE).attach( - new ConnectionInfo(ctx, SERVER, - new NetworkAddress.Builder().address(accepted.getRemoteAddress()).stream(1).build(), - listener, - requestedObjects - )); + serverChannel.socket().bind(new InetSocketAddress(ctx.getPort())); + while (selector.isOpen() && serverChannel.isOpen()) { + try { + SocketChannel accepted = serverChannel.accept(); + accepted.configureBlocking(false); + accepted.register(selector, OP_READ | OP_WRITE, + new ConnectionInfo(ctx, SERVER, + new NetworkAddress.Builder().address(accepted.getRemoteAddress()).stream(1).build(), + listener, + requestedObjects + )); + } catch (AsynchronousCloseException ignore) { + } catch (IOException e) { + LOG.error(e.getMessage(), e); + } + } } catch (ClosedSelectorException | AsynchronousCloseException ignore) { } catch (IOException e) { throw new ApplicationException(e); + } catch (RuntimeException e) { + e.printStackTrace(); + throw e; } } - }, "Server").start(); - new Thread(new Runnable() { + }); + + start("connection starter", new Runnable() { + @Override + public void run() { + while (selector.isOpen()) { + List addresses = ctx.getNodeRegistry().getKnownAddresses( + 2, ctx.getStreams()); + for (NetworkAddress address : addresses) { + try { + SocketChannel channel = SocketChannel.open( + new InetSocketAddress(address.toInetAddress(), address.getPort())); + channel.configureBlocking(false); + channel.register(selector, OP_READ | OP_WRITE, + new ConnectionInfo(ctx, CLIENT, + address, + listener, + requestedObjects + )); + } catch (AsynchronousCloseException ignore) { + } catch (IOException e) { + LOG.error(e.getMessage(), e); + } + } + try { + Thread.sleep(30_000); + } catch (InterruptedException e) { + return; + } + } + } + }); + + start("processor", new Runnable() { @Override public void run() { try { while (selector.isOpen()) { - // TODO: establish outgoing connections + selector.select(1000); Iterator keyIterator = selector.selectedKeys().iterator(); while (keyIterator.hasNext()) { @@ -141,22 +213,16 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex if (key.attachment() instanceof ConnectionInfo) { SocketChannel channel = (SocketChannel) key.channel(); ConnectionInfo connection = (ConnectionInfo) key.attachment(); - if (key.isWritable()) { - if (connection.getOutBuffer().hasRemaining()) { - channel.write(connection.getOutBuffer()); - } - while (!connection.getOutBuffer().hasRemaining() && !connection.getSendingQueue().isEmpty()) { - MessagePayload payload = connection.getSendingQueue().poll(); - if (payload instanceof GetData) { - requestedObjects.addAll(((GetData) payload).getInventory()); - } - new NetworkMessage(payload).write(connection.getOutBuffer()); - } + write(requestedObjects, channel, connection); } if (key.isReadable()) { - channel.read(connection.getInBuffer()); - connection.updateReader(); + read(channel, connection); + } + if (connection.getSendingQueue().isEmpty()) { + key.interestOps(OP_READ); + } else { + key.interestOps(OP_READ | OP_WRITE); } } keyIterator.remove(); @@ -168,13 +234,52 @@ public class NioNetworkHandler implements NetworkHandler, InternalContext.Contex throw new ApplicationException(e); } } - }, "Connections").start(); + }); + } + + private static void write(Set requestedObjects, SocketChannel channel, ConnectionInfo connection) + throws IOException { + if (!connection.getSendingQueue().isEmpty()) { + ByteBuffer buffer = connection.getOutBuffer(); + if (buffer.hasRemaining()) { + channel.write(buffer); + } + while (!buffer.hasRemaining() + && !connection.getSendingQueue().isEmpty()) { + buffer.clear(); + MessagePayload payload = connection.getSendingQueue().poll(); + if (payload instanceof GetData) { + requestedObjects.addAll(((GetData) payload).getInventory()); + } + new NetworkMessage(payload).write(buffer); + buffer.flip(); + if (buffer.hasRemaining()) { + channel.write(buffer); + } + } + } + } + + private static void read(SocketChannel channel, ConnectionInfo connection) throws IOException { + ByteBuffer buffer = connection.getInBuffer(); + while (channel.read(buffer) > 0) { + buffer.flip(); + connection.updateReader(); + buffer.compact(); + } + } + + private void start(String threadName, Runnable runnable) { + Thread thread = new Thread(runnable, threadName); + thread.setDaemon(true); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); } @Override public void stop() { try { - serverChannel.close(); + serverChannel.socket().close(); for (SelectionKey key : selector.keys()) { key.channel().close(); } diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index 2cadfb0..1b7994a 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -22,14 +22,23 @@ 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.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; @@ -42,6 +51,7 @@ import static org.mockito.Mockito.mock; /** * FIXME: there really should be sensible tests for the network handler */ +@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(); @@ -51,7 +61,27 @@ public class NetworkHandlerTest { private BitmessageContext peer; private BitmessageContext node; - private NetworkHandler networkHandler; + + private final NetworkHandler peerNetworkHandler; + private final NetworkHandler nodeNetworkHandler; + + @Rule + public final TestRule timeout = new DisableOnDebug(Timeout.seconds(5)); + + public NetworkHandlerTest(NetworkHandler peer, NetworkHandler node) { + this.peerNetworkHandler = peer; + this.nodeNetworkHandler = node; + } + + @Parameterized.Parameters + public static List 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() { @@ -63,7 +93,7 @@ public class NetworkHandlerTest { .powRepo(mock(ProofOfWorkRepository.class)) .port(peerAddress.getPort()) .nodeRegistry(new TestNodeRegistry()) - .networkHandler(new DefaultNetworkHandler()) + .networkHandler(peerNetworkHandler) .cryptography(new BouncyCryptography()) .listener(mock(BitmessageContext.Listener.class)) .customCommandHandler(new CustomCommandHandler() { @@ -90,7 +120,6 @@ public class NetworkHandlerTest { peer.startup(); nodeInventory = new TestInventory(); - networkHandler = new DefaultNetworkHandler(); node = new BitmessageContext.Builder() .addressRepo(mock(AddressRepository.class)) .inventory(nodeInventory) @@ -98,7 +127,7 @@ public class NetworkHandlerTest { .powRepo(mock(ProofOfWorkRepository.class)) .port(6002) .nodeRegistry(new TestNodeRegistry(peerAddress)) - .networkHandler(networkHandler) + .networkHandler(nodeNetworkHandler) .cryptography(new BouncyCryptography()) .listener(mock(BitmessageContext.Listener.class)) .build(); @@ -108,7 +137,7 @@ public class NetworkHandlerTest { public void cleanUp() { shutdown(peer); shutdown(node); - shutdown(networkHandler); + shutdown(nodeNetworkHandler); } private static void shutdown(BitmessageContext ctx) { @@ -140,7 +169,7 @@ public class NetworkHandlerTest { } while (networkHandler.isRunning()); } - @Test(timeout = 5_000) + @Test public void ensureNodesAreConnecting() throws Exception { node.startup(); Property status; @@ -151,14 +180,14 @@ public class NetworkHandlerTest { assertEquals(1, status.getProperty("outgoing").getValue()); } - @Test(timeout = 5_000) + @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 = networkHandler.send(peerAddress.toInetAddress(), peerAddress.getPort(), request); + CustomMessage response = nodeNetworkHandler.send(peerAddress.toInetAddress(), peerAddress.getPort(), request); assertThat(response, notNullValue()); assertThat(response.getCustomCommand(), is("test response")); @@ -172,14 +201,14 @@ public class NetworkHandlerTest { CustomMessage request = new CustomMessage("test request", data); node.startup(); - CustomMessage response = networkHandler.send(peerAddress.toInetAddress(), peerAddress.getPort(), request); + 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(timeout = 5_000) + @Test public void ensureObjectsAreSynchronizedIfBothHaveObjects() throws Exception { peerInventory.init( "V4Pubkey.payload", @@ -191,7 +220,7 @@ public class NetworkHandlerTest { "V4Pubkey.payload" ); - Future future = networkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(), + Future future = nodeNetworkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(), mock(NetworkHandler.MessageListener.class), 10); future.get(); @@ -199,7 +228,7 @@ public class NetworkHandlerTest { assertInventorySize(3, peerInventory); } - @Test(timeout = 5_000) + @Test public void ensureObjectsAreSynchronizedIfOnlyPeerHasObjects() throws Exception { peerInventory.init( "V4Pubkey.payload", @@ -208,7 +237,7 @@ public class NetworkHandlerTest { nodeInventory.init(); - Future future = networkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(), + Future future = nodeNetworkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(), mock(NetworkHandler.MessageListener.class), 10); future.get(); @@ -216,7 +245,7 @@ public class NetworkHandlerTest { assertInventorySize(2, peerInventory); } - @Test(timeout = 5_000) + @Test public void ensureObjectsAreSynchronizedIfOnlyNodeHasObjects() throws Exception { peerInventory.init(); @@ -224,7 +253,7 @@ public class NetworkHandlerTest { "V1Msg.payload" ); - Future future = networkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(), + Future future = nodeNetworkHandler.synchronize(peerAddress.toInetAddress(), peerAddress.getPort(), mock(NetworkHandler.MessageListener.class), 10); future.get(); diff --git a/wif/build.gradle b/wif/build.gradle index 93a0248..0c1ae14 100644 --- a/wif/build.gradle +++ b/wif/build.gradle @@ -13,7 +13,7 @@ uploadArchives { dependencies { compile project(':core') compile 'org.ini4j:ini4j:0.5.4' - testCompile 'junit:junit:4.11' + testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' testCompile project(':cryptography-bc') }