/* * Copyright 2012 Jason Miller * * 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 jj.http.server; import static jj.AnswerWithSelf.ANSWER_WITH_SELF; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import java.lang.Thread.UncaughtExceptionHandler; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import javax.inject.Provider; import jj.configuration.ConfigurationLoaded; import jj.event.MockPublisher; import jj.execution.MockTaskRunner; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.BDDMockito; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; /** * @author jason * */ @RunWith(MockitoJUnitRunner.class) public class HttpServerTest { @Mock HttpServerSwitch httpServerSwitch; ServerBootstrap serverBootstrap; Provider<ServerBootstrap> serverBootstrapProvider = new Provider<ServerBootstrap>() { @Override public ServerBootstrap get() { return serverBootstrap; } }; MockPublisher publisher; MockTaskRunner taskRunner; MockHttpServerNioEventLoopGroup childGroup; @Mock HttpServerChannelInitializer initializer; @Mock UncaughtExceptionHandler uncaughtExceptionHandler; @Mock ChannelFuture future; @Mock EventLoopGroup bossGroup; @Mock Future<?> groupFuture; int timeout; boolean tcpNoDelay; int sendBufferSize; boolean reuseAddress; int receiveBufferSize; boolean keepAlive; int backlog; List<Binding> bindings; int hashCode; private void givenConfig1() { timeout = 10000; tcpNoDelay = true; sendBufferSize = 65536; reuseAddress = true; receiveBufferSize = 65536; keepAlive = true; backlog = 12; bindings = Arrays.asList(new Binding(8080), new Binding("localhost", 8090)); hashCode = 12; } HttpServerSocketConfiguration configuration = new HttpServerSocketConfiguration() { @Override public int timeout() { return timeout; } @Override public boolean tcpNoDelay() { return tcpNoDelay; } @Override public int sendBufferSize() { return sendBufferSize; } @Override public boolean reuseAddress() { return reuseAddress; } @Override public int receiveBufferSize() { return receiveBufferSize; } @Override public boolean keepAlive() { return keepAlive; } @Override public int backlog() { return backlog; } @Override public List<Binding> bindings() { return bindings; } public int hashCode() { return hashCode; } }; HttpServer httpServer; @Before public void before() { serverBootstrap = mock(ServerBootstrap.class, ANSWER_WITH_SELF); publisher = new MockPublisher(); taskRunner = new MockTaskRunner(); childGroup = new MockHttpServerNioEventLoopGroup(); httpServer = new HttpServer( childGroup, initializer, configuration, httpServerSwitch, publisher, taskRunner, serverBootstrapProvider, uncaughtExceptionHandler ); } @After public void after() { timeout = -1; tcpNoDelay = false; sendBufferSize = -1; reuseAddress = false; receiveBufferSize = -1; keepAlive = false; backlog = -1; bindings = null; hashCode = -1; } @Test public void testServerOff() throws Exception { // when httpServer.on((ConfigurationLoaded)null); // then assertTrue(publisher.events.isEmpty()); } private void givenStartupConditions() { given(httpServerSwitch.on()).willReturn(true); given(serverBootstrap.bind(8080)).willReturn(future); given(serverBootstrap.bind("localhost", 8090)).willReturn(future); } @Captor ArgumentCaptor<EventLoopGroup> bossGroupCaptor; @Test public void testServerOnStartup() throws Exception { // given givenConfig1(); givenStartupConditions(); // when httpServer.on((ConfigurationLoaded)null); taskRunner.runFirstTask(); // check more - the value of the bindings maybe? assertThat(publisher.events.get(0), is(instanceOf(BindingHttpServer.class))); assertThat(publisher.events.get(1), is(instanceOf(BindingHttpServer.class))); assertThat(publisher.events.get(2), is(instanceOf(HttpServerStarted.class))); verify(serverBootstrap).channel(NioServerSocketChannel.class); verify(serverBootstrap).group(bossGroupCaptor.capture(), eq(childGroup)); verify(serverBootstrap).childHandler(initializer); verify(serverBootstrap).option(ChannelOption.SO_KEEPALIVE, configuration.keepAlive()); verify(serverBootstrap).option(ChannelOption.SO_REUSEADDR, configuration.reuseAddress()); verify(serverBootstrap).option(ChannelOption.TCP_NODELAY, configuration.tcpNoDelay()); verify(serverBootstrap).option(ChannelOption.SO_TIMEOUT, configuration.timeout()); verify(serverBootstrap).option(ChannelOption.SO_BACKLOG, configuration.backlog()); verify(serverBootstrap).option(ChannelOption.SO_RCVBUF, configuration.receiveBufferSize()); verify(serverBootstrap).option(ChannelOption.SO_SNDBUF, configuration.sendBufferSize()); verify(future, times(2)).sync(); } @Captor ArgumentCaptor<GenericFutureListener<Future<?>>> groupFutureListenerCaptor; @Test public void testServerRestart() throws Exception { // given givenConfig1(); givenStartupConditions(); // when httpServer.on((ConfigurationLoaded)null); taskRunner.runFirstTask(); assertThat(publisher.events.size(), is(3)); assertThat(publisher.events.get(0), is(instanceOf(BindingHttpServer.class))); assertThat(publisher.events.get(1), is(instanceOf(BindingHttpServer.class))); assertThat(publisher.events.get(2), is(instanceOf(HttpServerStarted.class))); publisher.events.clear(); // given bindings = Collections.singletonList(new Binding(8070)); given(serverBootstrap.bind(8070)).willReturn(future); hashCode = 93987934; // just needs to be different given(serverBootstrap.group()).willReturn(bossGroup); willAnswer(invocation -> { @SuppressWarnings("unchecked") GenericFutureListener<Future<?>> listener = (GenericFutureListener<Future<?>>)invocation.getArguments()[0]; listener.operationComplete(groupFuture); return groupFuture; }).given(groupFuture).addListener(any()); willReturn(groupFuture).given(bossGroup).shutdownGracefully(anyLong(), anyLong(), BDDMockito.any(TimeUnit.class)); given(groupFuture.isSuccess()).willReturn(true); // when httpServer.on((ConfigurationLoaded)null); taskRunner.runFirstTask(); // then assertThat(publisher.events.size(), is(3)); assertThat(publisher.events.get(0), is(instanceOf(HttpServerRestarting.class))); assertThat(publisher.events.get(1), is(instanceOf(BindingHttpServer.class))); assertThat(publisher.events.get(2), is(instanceOf(HttpServerStarted.class))); } }