Package com.tryge.xocotl.util

Source Code of com.tryge.xocotl.util.BoundedServerTest$TestThreadHelper

package com.tryge.xocotl.util;

import com.tryge.xocotl.io.*;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.mockito.stubbing.OngoingStubbing;
import org.mockito.stubbing.Stubber;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.*;

/**
* @author michael.zehender@me.com
*/
public class BoundedServerTest {
  private ChannelFactory channelFactory;
  private ChannelListener channelListener;
  private MessageDecoder messageDecoder;
  private MessageListener messageListener;
  private ServerSocket serverSocket;
  private Socket client;
  private DuplexChannel channel;
  private ArgumentCaptor<ChannelListener> channelListenerCaptor;

  private Thread runner;
  private TestThreadHelper testThreadHelper;

  @Before
  public void setUp() throws Exception {
    testThreadHelper = new TestThreadHelper();

    channelFactory = mock(ChannelFactory.class);
    channelListener = mock(ChannelListener.class);
    messageDecoder = mock(MessageDecoder.class);
    messageListener = mock(MessageListener.class);
    serverSocket = mock(ServerSocket.class);

    client = mock(Socket.class);
    channel = mock(DuplexChannel.class);
    channelListenerCaptor = ArgumentCaptor.forClass(ChannelListener.class);

    when(serverSocket.accept())
      .thenReturn(client)
      .thenThrow(new SocketException());
    when(channelFactory.newSocketChannel(same(client), same(messageDecoder)))
      .thenReturn(channel);
  }

  @Test
  public void canBuildMinimalServer() throws Exception {
    TestThreadHelper helper = new TestThreadHelper();

    captureChannelListener();
    helper.signalServerReady().when(channel).open();

    runner = new Thread(new BoundedServer.Builder()
      .setChannelFactory(channelFactory)
      .setMessageDecoder(messageDecoder)
      .setMessageListener(messageListener)
      .setMaximumConnections(2)
      .build(serverSocket));
    runner.start();

    helper.waitForServerThread();
    signalChannelClose();

    verify(serverSocket, times(2)).accept();
  }

  @Test
  public void canCreateChannelOnAccept() throws Exception {
    buildServer(2).run();

    verify(serverSocket, times(2)).accept();
    verify(channelFactory).newSocketChannel(same(client), same(messageDecoder));
    verifyChannelIsInitialized(channel);
  }

  @Test
  public void doesRespectMaximumConnections() throws Exception {
    InOrder inOrder = inOrder(channel, channelListener, serverSocket);

    captureChannelListener();
    testThreadHelper.signalServerReady().when(channel).open();

    startServer(1);
    testThreadHelper.waitForServerThread();
    signalChannelClose();

    inOrder.verify(serverSocket).accept();
    inOrder.verify(channelListener).onClose(same(channel));
    inOrder.verify(serverSocket).accept();
  }

  @Test
  public void closesServerSocketOnStop() throws Exception {
    testThreadHelper.signalServerReadyAndBlock().when(serverSocket).accept();

    startAndStopServer();
    verifyServerHasStopped();
  }

  @Test
  public void closesChannelsOnStop() throws Exception {
    testThreadHelper.signalServerReady().when(channel).open();
    testThreadHelper.doBlock(when(serverSocket.accept()).thenReturn(client));

    startAndStopServer();
    verifyServerHasStopped();
    verify(channel).close();
  }

  private void startAndStopServer() throws InterruptedException, BrokenBarrierException {
    BoundedServer server = startServer(1);
    testThreadHelper.waitForServerThread();

    server.stop();
  }

  private BoundedServer startServer(int maximumConnections) {
    BoundedServer server = buildServer(maximumConnections);

    runner = new Thread(server);
    runner.start();

    return server;
  }

  private BoundedServer buildServer(int maximumConnections) {
    return new BoundedServer.Builder()
      .setChannelFactory(channelFactory)
      .setChannelListener(channelListener)
      .setMessageDecoder(messageDecoder)
      .setMessageListener(messageListener)
      .setMaximumConnections(maximumConnections)
      .build(serverSocket);
  }

  private void captureChannelListener() {
    doNothing().when(channel).setChannelListener(channelListenerCaptor.capture());
  }

  private void signalChannelClose() {
    ChannelListener listener = channelListenerCaptor.getValue();
    listener.onClose(channel);
  }

  private void verifyChannelIsInitialized(DuplexChannel channel) throws IOException {
    verify(channel).setChannelListener(notNull(ChannelListener.class));
    verify(channel).setMessageListener(same(messageListener));
    verify(channel).open();
  }

  private void verifyServerHasStopped() throws IOException {
    verify(serverSocket).close();
    assertFalse(runner.isAlive());
  }

  private class TestThreadHelper {
    private final CyclicBarrier barrier = new CyclicBarrier(2);

    public OngoingStubbing<?> doBlock(OngoingStubbing<?> stubbing) {
      return stubbing.thenAnswer(newBlockAnswer());
    }

    public Stubber signalServerReadyAndBlock() {
      return doAnswer(newAwaitAndBlockAnswer());
    }

    public Stubber signalServerReady() {
      return doAnswer(newAwaitBarrierAnswer());
    }

    public void waitForServerThread() throws InterruptedException, BrokenBarrierException {
      barrier.await();
    }

    private Answer newAwaitAndBlockAnswer() {
      return new Answer() {
        @Override
        public Object answer(InvocationOnMock invocation) throws Throwable {
          newAwaitBarrierAnswer().answer(invocation);
          newBlockAnswer().answer(invocation);
          return null;
        }
      };
    }

    private Answer newAwaitBarrierAnswer() {
      return new Answer() {
        @Override
        public Object answer(InvocationOnMock invocation) throws Throwable {
          barrier.await();
          return null;
        }
      };
    }

    private Answer newBlockAnswer() {
      return new Answer() {
        @Override
        public Object answer(InvocationOnMock invocation) throws Throwable {
          Thread.sleep(Long.MAX_VALUE);
          return null;
        }
      };
    }
  }
}
TOP

Related Classes of com.tryge.xocotl.util.BoundedServerTest$TestThreadHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.