// Copyright © 2011-2013, Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0
package fi.jumi.core.network;
import fi.jumi.actors.queue.MessageSender;
import org.junit.*;
import org.junit.rules.Timeout;
import java.util.Collections;
import java.util.concurrent.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;
public class NettyNetworkCommunicationTest {
private static final long ASSERT_TIMEOUT = 500;
private static final boolean LOGGING = false;
@Rule
public final Timeout timeout = new Timeout(1000);
private final ExecutorService clientExecutor = Executors.newCachedThreadPool();
private final ExecutorService serverExecutor = Executors.newCachedThreadPool();
private final NettyNetworkClient client = new NettyNetworkClient(LOGGING, clientExecutor);
private final NettyNetworkServer server = new NettyNetworkServer(LOGGING, serverExecutor);
private final ClientNetworkEndpoint clientEndpoint = new ClientNetworkEndpoint();
private final ServerNetworkEndpoint serverEndpoint = new ServerNetworkEndpoint();
@After
public void tearDown() {
client.close();
server.close();
}
@Test
public void client_can_send_messages_to_server() throws Exception {
connectClientToServer();
clientEndpoint.toServer.get().send(123);
assertThat(serverEndpoint.messagesReceived.take(), is(123));
}
@Test
public void server_can_send_messages_to_client() throws Exception {
connectClientToServer();
serverEndpoint.toClient.get().send("hello");
assertThat(clientEndpoint.messagesReceived.take(), is("hello"));
}
@Test
public void multiple_clients_can_connect_to_the_server_independently() throws Exception {
ServerNetworkEndpoint serverEndpoint1 = new ServerNetworkEndpoint();
ServerNetworkEndpoint serverEndpoint2 = new ServerNetworkEndpoint();
int port = server.listenOnAnyPort(new StubServerNetworkEndpointFactory(serverEndpoint1, serverEndpoint2));
ClientNetworkEndpoint clientEndpoint1 = new ClientNetworkEndpoint();
client.connect("127.0.0.1", port, clientEndpoint1);
ClientNetworkEndpoint clientEndpoint2 = new ClientNetworkEndpoint();
client.connect("127.0.0.1", port, clientEndpoint2);
serverEndpoint1.toClient.get().send("message1");
clientEndpoint1.toServer.get().send(100);
serverEndpoint2.toClient.get().send("message2");
clientEndpoint2.toServer.get().send(200);
assertThat(clientEndpoint1.messagesReceived.take(), is("message1"));
assertThat(serverEndpoint1.messagesReceived.take(), is(100));
assertThat(clientEndpoint2.messagesReceived.take(), is("message2"));
assertThat(serverEndpoint2.messagesReceived.take(), is(200));
}
@Test
public void client_can_disconnect() throws Exception {
connectClientToServer();
clientEndpoint.connection.get().disconnect();
assertEventHappens("server should get disconnected event", serverEndpoint.disconnected);
assertEventHappens("client should get disconnected event", clientEndpoint.disconnected);
}
@Test
public void server_can_disconnect() throws Exception {
connectClientToServer();
serverEndpoint.connection.get().disconnect();
assertEventHappens("server should get disconnected event", serverEndpoint.disconnected);
assertEventHappens("client should get disconnected event", clientEndpoint.disconnected);
}
@Test
public void on_close_the_client_disconnects_all_connections() throws Exception {
connectClientToServer();
client.close();
assertEventHappens("client should get disconnected event", clientEndpoint.disconnected);
}
@Test
public void on_close_the_server_disconnects_all_connections() throws Exception {
connectClientToServer();
serverEndpoint.connection.get();
server.close();
assertEventHappens("server should get disconnected event", serverEndpoint.disconnected);
}
@Test
public void on_close_the_client_terminates_its_executors() {
connectClientToServer();
client.close();
assertThat("client executor terminated", clientExecutor.isTerminated(), is(true));
}
@Test
public void on_close_the_server_terminates_its_executors() {
connectClientToServer();
server.close();
assertThat("server executor terminated", serverExecutor.isTerminated(), is(true));
}
private void connectClientToServer() {
int port = server.listenOnAnyPort(new StubServerNetworkEndpointFactory(serverEndpoint));
client.connect("127.0.0.1", port, clientEndpoint);
}
private static void assertEventHappens(String message, CountDownLatch event) throws InterruptedException {
assertTrue(message, event.await(ASSERT_TIMEOUT, TimeUnit.MILLISECONDS));
}
private static class ServerNetworkEndpoint implements NetworkEndpoint<Integer, String> {
public final FutureValue<NetworkConnection> connection = new FutureValue<>();
public final FutureValue<MessageSender<String>> toClient = new FutureValue<>();
public final BlockingQueue<Integer> messagesReceived = new LinkedBlockingQueue<>();
public final CountDownLatch disconnected = new CountDownLatch(1);
@Override
public void onConnected(NetworkConnection connection, MessageSender<String> sender) {
this.connection.set(connection);
toClient.set(sender);
}
@Override
public void onMessage(Integer message) {
messagesReceived.add(message);
}
@Override
public void onDisconnected() {
disconnected.countDown();
}
}
private static class ClientNetworkEndpoint implements NetworkEndpoint<String, Integer> {
public final FutureValue<NetworkConnection> connection = new FutureValue<>();
public final FutureValue<MessageSender<Integer>> toServer = new FutureValue<>();
public final BlockingQueue<String> messagesReceived = new LinkedBlockingQueue<>();
public final CountDownLatch disconnected = new CountDownLatch(1);
@Override
public void onConnected(NetworkConnection connection, MessageSender<Integer> sender) {
this.connection.set(connection);
toServer.set(sender);
}
@Override
public void onMessage(String message) {
messagesReceived.add(message);
}
@Override
public void onDisconnected() {
disconnected.countDown();
}
}
private static class StubServerNetworkEndpointFactory implements NetworkEndpointFactory<Integer, String> {
private final BlockingQueue<ServerNetworkEndpoint> serverEndpoints;
public StubServerNetworkEndpointFactory(ServerNetworkEndpoint... serverEndpoints) {
this.serverEndpoints = new ArrayBlockingQueue<>(serverEndpoints.length);
Collections.addAll(this.serverEndpoints, serverEndpoints);
}
@Override
public NetworkEndpoint<Integer, String> createEndpoint() {
ServerNetworkEndpoint endpoint = serverEndpoints.poll();
assertNotNull("more clients connected than were expected", endpoint);
return endpoint;
}
}
}