/* * The MIT License * * Copyright 2014 sorrge. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.nyan.dch.communication.transport.tcpip; import java.io.DataInputStream; import java.io.IOException; import java.net.BindException; import java.net.InetAddress; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.Random; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import static org.junit.Assert.*; import org.junit.Test; import org.nyan.dch.communication.AlwaysAccepting; import org.nyan.dch.communication.Connections; import org.nyan.dch.communication.IAcceptance; import org.nyan.dch.communication.IAddress; import org.nyan.dch.communication.IConnections; import org.nyan.dch.communication.IDiscovery; import org.nyan.dch.communication.IDiscoveryListener; import org.nyan.dch.communication.INetworkTransport; import org.nyan.dch.communication.IRemoteHost; import org.nyan.dch.communication.IRemoteHostListener; import org.nyan.dch.communication.ProtocolException; import org.nyan.dch.communication.RemoteNodeMessages; import org.nyan.dch.communication.transport.simulator.Address; import org.nyan.dch.crypto.SHA256Hash; import org.nyan.dch.node.Node; import org.nyan.dch.posts.Post; import org.nyan.dch.posts.PostData; import org.nyan.dch.posts.Storage; import org.nyan.dch.posts.StorageTest; /** * * @author sorrge */ public class TCPServerTest { static class DiscoveryStub implements IDiscovery { private final AlwaysAccepting acceptance = new AlwaysAccepting(); @Override public void BeginDiscovery() { } @Override public void EndDiscovery() { } @Override public void SetDiscoveryListener(IDiscoveryListener listener) { } @Override public void Close() { } @Override public IAddress GetMyAddress() { return new Address(new Random(12)); } @Override public void Restart() { } @Override public IAcceptance GetAcceptance() { return acceptance; } } static class ConnectionsStub implements IConnections, IRemoteHostListener { final HashSet<IRemoteHost> connected = new HashSet<>(); final ArrayList<SHA256Hash> received = new ArrayList<>(); @Override public boolean AddAddress(IAddress address) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void Connected(IRemoteHost host, boolean iAmTheInitiator) { connected.add(host); host.SetReceiveListener(this); } @Override public void Disconnected(IRemoteHost host) { System.out.printf("Disconnect from %s reported\n", host); connected.remove(host); } @Override public void ConnectionFailed(IAddress host) { System.out.printf("Connection to %s failed\n", host); } @Override public INetworkTransport GetTransport() { return null; } @Override public void Disconnected(boolean iAmInitiator) { } @Override public void ReceiveData(byte[] data) throws ProtocolException { received.add(SHA256Hash.Digest(data)); } @Override public void AddressSupplied(IRemoteHost host, IAddress address, long cookie, boolean wandAcceptanceConfirmation) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } } static class ServerWrapper implements INetworkTransport, Runnable { final TCPServer server; int connects = 0; public ServerWrapper(TCPServer server) { this.server = server; } @Override public void run() { server.run(); } public void Stop() { server.Close(); } @Override public void SetConnectionsListener(IConnections listener) { server.SetConnectionsListener(listener); } @Override public IAddress ReadAddress(DataInputStream stream) throws IOException { return server.ReadAddress(stream); } @Override public int GetCurrentConnectionAttempts() { return server.GetCurrentConnectionAttempts(); } @Override public void Connect(IAddress toWhom, boolean test) { ++connects; server.Connect(toWhom, test); } @Override public IDiscovery GetDiscovery() { return server.GetDiscovery(); } @Override public int GetConnectionsCount() { return server.GetConnectionsCount(); } @Override public IRemoteHost GetConnectedHost(IAddress whom) { return server.GetConnectedHost(whom); } } public TCPServerTest() { Logger root = Logger.getLogger(""); root.setLevel(Level.WARNING); Handler[] handlers = root.getHandlers(); for(Handler h: handlers) h.setLevel(Level.WARNING); } /** * Test of class TCPServer. */ @Test public void testBind() throws IOException, InterruptedException { Random rand = new Random(12); TCPServer server = new TCPServer(13456, new DiscoveryStub(), rand); Thread serverThread = new Thread(server); serverThread.start(); Thread.sleep(1000); assert(serverThread.isAlive()); server.Close(); } /** * Test of class TCPServer. */ @Test public void testConnect() throws IOException, InterruptedException { Random rand = new Random(12); int p1 = 13457, p2 = 13458; ConnectionsStub c1 = new ConnectionsStub(), c2 = new ConnectionsStub(); TCPServer server1 = new TCPServer(p1, new DiscoveryStub(), rand), server2 = new TCPServer(p2, new DiscoveryStub(), rand); server1.SetConnectionsListener(c1); server2.SetConnectionsListener(c2); Thread serverThread1 = new Thread(server1), serverThread2 = new Thread(server2); serverThread1.start(); serverThread2.start(); Thread.sleep(1000); server1.Connect(new IPAddress(InetAddress.getLocalHost(), p2), false); Thread.sleep(1000); assert(serverThread1.isAlive()); assert(serverThread2.isAlive()); assert(c1.connected.size() == 1); assert(c2.connected.size() == 1); IRemoteHost h1 = c1.connected.iterator().next(), h2 = c2.connected.iterator().next(); System.out.println(h1.GetAddress()); System.out.println(h2.GetAddress()); h2.Disconnect(); Thread.sleep(1000); assert(server1.GetConnectionsCount() == 0); assert(server2.GetConnectionsCount() == 0); server1.Close(); server2.Close(); } /** * Test of class TCPServer. */ @Test public void testNet() throws IOException, InterruptedException { int p0 = 13459; int numNodes = 30; Random rand = new Random(12); ArrayList<ConnectionsStub> cs = new ArrayList<>(numNodes); ArrayList<TCPServer> servers = new ArrayList<>(numNodes); for(int i = 0; i < numNodes; ++i) { ConnectionsStub c1 = new ConnectionsStub(); TCPServer server = new TCPServer(p0 + i, new DiscoveryStub(), rand); server.SetConnectionsListener(c1); Thread serverThread = new Thread(server); serverThread.start(); cs.add(c1); servers.add(server); } Thread.sleep(1000); for(TCPServer s : servers) for(int i = 0; i < Connections.WantConnections; ++i) s.Connect(new IPAddress(InetAddress.getLocalHost(), p0 + rand.nextInt(numNodes)), false); Thread.sleep(5000); for(ConnectionsStub c : cs) assert(c.connected.size() >= Connections.WantConnections - 1); for(TCPServer s : servers) s.Close(); } /** * Test of class TCPServer. */ @Test public void testTransfer() throws IOException, InterruptedException, ProtocolException { int p1 = 14457, p2 = 14458; int numMsg = 10000; Random rand = new Random(12); ConnectionsStub c1 = new ConnectionsStub(), c2 = new ConnectionsStub(); TCPServer server1 = new TCPServer(p1, new DiscoveryStub(), rand), server2 = new TCPServer(p2, new DiscoveryStub(), rand); server1.SetConnectionsListener(c1); server2.SetConnectionsListener(c2); Thread serverThread1 = new Thread(server1), serverThread2 = new Thread(server2); serverThread1.start(); serverThread2.start(); Thread.sleep(1000); server1.Connect(new IPAddress(InetAddress.getLocalHost(), p2), false); Thread.sleep(1000); assert(serverThread1.isAlive()); assert(serverThread2.isAlive()); assert(c1.connected.size() == 1); assert(c2.connected.size() == 1); HashSet<SHA256Hash> messages = new HashSet<>(numMsg); for(int i = 0; i < numMsg; ++i) { ConnectionsStub c = rand.nextBoolean() ? c1 : c2; byte[] msg = new byte[rand.nextInt(RemoteNodeMessages.MaxMessageSize) + 1]; rand.nextBytes(msg); messages.add(SHA256Hash.Digest(msg)); c.connected.iterator().next().SendData(msg); } Thread.sleep(5000); HashSet<SHA256Hash> receivedMessages = new HashSet<>(c1.received); receivedMessages.addAll(c2.received); assert(messages.equals(receivedMessages)); server1.Close(); server2.Close(); } /* Tests using the real Node */ static class NodeData { final Node node; SimulatedDiscovery discovery; ServerWrapper server; Thread serverThread, discThread; Connections connections; final SimulatedDiscovery.AddressBook book; final Random rand; int port; public NodeData(SimulatedDiscovery.AddressBook book, int port, Random rand) throws IOException { this.book = book; this.rand = rand; this.port = port; IPAddress addr = new IPAddress(InetAddress.getLocalHost(), port); book.Add(addr); discovery = new SimulatedDiscovery(book, addr); discThread = new Thread(discovery); server = new ServerWrapper(new TCPServer(port, discovery, rand)); node = new Node(new Storage("b", "e", "d")); connections = new Connections(server, node, rand); serverThread = new Thread(server); } public void Start() { discThread.start(); serverThread.start(); } public void Stop() throws InterruptedException { server.Stop(); serverThread.join(10000); discThread.join(10000); assert(!serverThread.isAlive()); assert(!discThread.isAlive()); } public void Restart() throws IOException { boolean restart = true; while(true) try { if(restart) server.server.Restart(); else { System.out.printf("Could not bid to port %d, trying another\n", port); book.Remove(discovery.GetMyAddress()); port += rand.nextInt(1000) + 1000; IPAddress addr = new IPAddress(InetAddress.getLocalHost(), port); book.Add(addr); discovery = new SimulatedDiscovery(book, addr); server = new ServerWrapper(new TCPServer(port, discovery, rand)); connections = new Connections(server, node, rand); } break; } catch(BindException be) { restart = false; } discThread = new Thread(discovery); serverThread = new Thread(server); Start(); connections.EnsureConnected(); } } /** * Test of class TCPServer. */ @Test public void testBind2() throws IOException, InterruptedException { Random rand = new Random(12); SimulatedDiscovery.AddressBook book = new SimulatedDiscovery.AddressBook(rand); NodeData nd = new NodeData(book, 23456, rand); nd.Start(); Thread.sleep(1000); assert(nd.serverThread.isAlive()); nd.server.Stop(); } /** * Test of class TCPServer. */ @Test public void test2() throws IOException, InterruptedException { Random rand = new Random(12); for(int repeat = 0; repeat < 10; ++repeat) { int p0 = 23457; System.out.printf("Repeat %d\n", repeat); SimulatedDiscovery.AddressBook book = new SimulatedDiscovery.AddressBook(rand); NodeData nd1 = new NodeData(book, p0++, rand), nd2 = new NodeData(book, p0++, rand); nd1.Start(); nd2.Start(); Thread.sleep(500); assert(nd1.serverThread.isAlive()); assert(nd2.serverThread.isAlive()); assert(nd1.discThread.isAlive()); assert(nd2.discThread.isAlive()); for(int i = 0; i < 10; ++i) if(!nd1.node.Connections().isEmpty() || !nd2.node.Connections().isEmpty()) break; else { System.out.println("Still not connected..."); Thread.sleep(250); } assert(nd1.node.Connections().size() == 1); assert(nd2.node.Connections().size() == 1); assert(nd1.server.connects <= 1); assert(nd1.server.connects <= 1); Post p = new Post(new PostData("b", null, String.valueOf(rand.nextInt()), String.valueOf(rand.nextInt()), new Date())); nd1.node.storage.Add(p); Thread.sleep(500); assert(nd2.node.storage.Contains(p)); nd1.Stop(); nd2.Stop(); } } /** * Test of class TCPServer. */ @Test public void testNet2() throws IOException, InterruptedException { int numNodes = 30, numPosts = 2000; int p0 = 24457; Random rand = new Random(12); for(int repeat = 0; repeat < 1; ++repeat) { System.out.printf("Repeat %d\n", repeat); System.out.printf("Connecting %d hosts\n", numNodes); SimulatedDiscovery.AddressBook book = new SimulatedDiscovery.AddressBook(rand); ArrayList<NodeData> nodes = new ArrayList<>(numNodes); for(int i = 0; i < numNodes; ++i) { NodeData nd; while(true) try { nd = new NodeData(book, p0++, rand); break; } catch(BindException ex) { System.out.printf("Can't bind to port %d, trying another\n", p0 - 1); Thread.sleep(100); } nodes.add(nd); nd.Start(); } Thread.sleep(2000); for(NodeData nd : nodes) { assert(nd.serverThread.isAlive()); assert(nd.discThread.isAlive()); if(!(nd.server.GetConnectionsCount() >= Connections.WantConnections)) Thread.sleep(10000); assert(nd.server.GetConnectionsCount() >= Connections.WantConnections); assert(nd.node.Connections().size() == nd.server.GetConnectionsCount()); } System.out.printf("Sending %d posts\n", numPosts); ArrayList<Post> posts = StorageTest.GenerateStream(numPosts, rand, 111, "b", "e", "d"); for(Post p : posts) { nodes.get(rand.nextInt(numNodes)).node.storage.Add(p); Thread.sleep(rand.nextInt(50)); } Thread.sleep(2000); for(int i = 1; i < numNodes; ++i) StorageTest.AssertSynced(nodes.get(0).node.storage, nodes.get(i).node.storage); System.out.println("Disconnecting 5 nodes"); for(int i = 0; i < 5; ++i) nodes.get(i).Stop(); Thread.sleep(2000); for(int i = 0; i < numNodes; ++i) { NodeData nd = nodes.get(i); assert(nd.node.Connections().size() == nd.server.GetConnectionsCount()); if(i < 5) assert(nd.server.GetConnectionsCount() == 0); else { if(!(nd.server.GetConnectionsCount() >= Connections.WantConnections)) Thread.sleep(10000); assert(nd.server.GetConnectionsCount() >= Connections.WantConnections); } } System.out.printf("Sending %d more posts\n", numPosts); posts = StorageTest.GenerateStream(numPosts, rand, 10111, "b", "e", "d"); for(Post p : posts) { nodes.get(rand.nextInt(numNodes - 5) + 5).node.storage.Add(p); Thread.sleep(rand.nextInt(50)); } Thread.sleep(2000); for(int i = 1; i < numNodes; ++i) StorageTest.AssertSynced(nodes.get(i < 5 ? 0 : 5).node.storage, nodes.get(i).node.storage); System.out.println("Reconnecting 5 nodes"); for(int i = 0; i < 5; ++i) nodes.get(i).Restart(); Thread.sleep(2000); for(NodeData nd : nodes) { assert(nd.serverThread.isAlive()); assert(nd.discThread.isAlive()); if(!(nd.server.GetConnectionsCount() >= Connections.WantConnections)) Thread.sleep(10000); assert(nd.server.GetConnectionsCount() >= Connections.WantConnections); assert(nd.node.Connections().size() == nd.server.GetConnectionsCount()); } for(int i = 1; i < numNodes; ++i) StorageTest.AssertSynced(nodes.get(0).node.storage, nodes.get(i).node.storage); for(NodeData nd : nodes) nd.Stop(); } } /** * Test of class TCPServer. */ @Test public void testConnectMore2() throws IOException, InterruptedException { int numNodes = 60, toDisconnect = 30; int p0 = 24457; Random rand = new Random(12); for(int repeat = 0; repeat < 1; ++repeat) { System.out.printf("Repeat %d\n", repeat); System.out.printf("Connecting %d hosts\n", numNodes); SimulatedDiscovery.AddressBook book = new SimulatedDiscovery.AddressBook(rand); ArrayList<NodeData> nodes = new ArrayList<>(numNodes); for(int i = 0; i < numNodes; ++i) { NodeData nd; while(true) try { nd = new NodeData(book, p0++, rand); break; } catch(BindException ex) { System.out.printf("Can't bind to port %d, trying another\n", p0 - 1); Thread.sleep(100); } nodes.add(nd); nd.Start(); } Thread.sleep(15000); int totalConnectionRequests = 0; for(NodeData nd : nodes) { assert(nd.serverThread.isAlive()); assert(nd.discThread.isAlive()); assert(nd.server.GetConnectionsCount() >= Connections.WantConnections); assert(nd.node.Connections().size() == nd.server.GetConnectionsCount()); totalConnectionRequests += nd.server.connects; } System.out.println("Checking convergence..."); Thread.sleep(5000); int totalConnectionRequests2 = 0; for(NodeData nd : nodes) { assert(nd.server.GetConnectionsCount() >= Connections.WantConnections); assert(nd.node.Connections().size() == nd.server.GetConnectionsCount()); totalConnectionRequests2 += nd.server.connects; } assert(totalConnectionRequests == totalConnectionRequests2); System.out.printf("Disconnecting %d nodes\n", toDisconnect); for(int i = 0; i < toDisconnect; ++i) nodes.get(i).Stop(); Thread.sleep(15000); totalConnectionRequests = 0; for(int i = 0; i < numNodes; ++i) { NodeData nd = nodes.get(i); assert(nd.node.Connections().size() == nd.server.GetConnectionsCount()); if(i < toDisconnect) assert(nd.server.GetConnectionsCount() == 0); else assert(nd.server.GetConnectionsCount() >= Connections.WantConnections); totalConnectionRequests += nd.server.connects; } System.out.println("Checking convergence..."); Thread.sleep(5000); totalConnectionRequests2 = 0; for(int i = 0; i < numNodes; ++i) { NodeData nd = nodes.get(i); assert(nd.node.Connections().size() == nd.server.GetConnectionsCount()); if(i < toDisconnect) assert(nd.server.GetConnectionsCount() == 0); else assert(nd.server.GetConnectionsCount() >= Connections.WantConnections); totalConnectionRequests2 += nd.server.connects; } assert(totalConnectionRequests == totalConnectionRequests2); System.out.printf("Reconnecting %d nodes\n", toDisconnect); for(int i = 0; i < toDisconnect; ++i) nodes.get(i).Restart(); Thread.sleep(15000); totalConnectionRequests = 0; for(NodeData nd : nodes) { assert(nd.serverThread.isAlive()); assert(nd.discThread.isAlive()); assert(nd.server.GetConnectionsCount() >= Connections.WantConnections); assert(nd.node.Connections().size() == nd.server.GetConnectionsCount()); totalConnectionRequests += nd.server.connects; } System.out.println("Checking convergence..."); Thread.sleep(5000); totalConnectionRequests2 = 0; for(NodeData nd : nodes) { assert(nd.serverThread.isAlive()); assert(nd.discThread.isAlive()); assert(nd.server.GetConnectionsCount() >= Connections.WantConnections); assert(nd.node.Connections().size() == nd.server.GetConnectionsCount()); totalConnectionRequests2 += nd.server.connects; } assert(totalConnectionRequests == totalConnectionRequests2); for(NodeData nd : nodes) nd.Stop(); } } }