Java tutorial
/* * Copyright 2013 Basho Technologies Inc. * * 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 com.basho.riak.client.core; import com.basho.riak.client.core.RiakNode.State; import com.google.protobuf.Message; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelPipeline; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; import java.net.UnknownHostException; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import static com.jayway.awaitility.Awaitility.await; import static com.jayway.awaitility.Awaitility.fieldIn; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.*; import static org.mockito.Mockito.*; /** * @author Brian Roach <roach at basho dot com> */ @RunWith(PowerMockRunner.class) @PrepareForTest({ Bootstrap.class, FutureOperation.class, RiakMessage.class }) public class RiakNodeTest { @Test public void builderProducesDefaultNode() throws UnknownHostException { RiakNode node = new RiakNode.Builder().build(); assertEquals(node.getRemoteAddress(), RiakNode.Builder.DEFAULT_REMOTE_ADDRESS); assertEquals(node.getPort(), RiakNode.Builder.DEFAULT_REMOTE_PORT); assertEquals(node.getNodeState(), State.CREATED); assertEquals(node.getMaxConnections(), Integer.MAX_VALUE); assertEquals(node.getConnectionTimeout(), RiakNode.Builder.DEFAULT_CONNECTION_TIMEOUT); assertEquals(node.getIdleTimeout(), RiakNode.Builder.DEFAULT_IDLE_TIMEOUT); assertEquals(node.getMinConnections(), RiakNode.Builder.DEFAULT_MIN_CONNECTIONS); assertEquals(node.availablePermits(), Integer.MAX_VALUE); } @Test public void builderProducesCorrectNode() throws UnknownHostException { final int IDLE_TIMEOUT = 2000; final int CONNECTION_TIMEOUT = 2001; final int MIN_CONNECTIONS = 2002; final int MAX_CONNECTIONS = 2003; final int PORT = 2004; final int READ_TIMEOUT = 2005; final String REMOTE_ADDRESS = "localhost"; final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(); final Bootstrap BOOTSTRAP = PowerMockito.spy(new Bootstrap()); doReturn(BOOTSTRAP).when(BOOTSTRAP).clone(); RiakNode node = new RiakNode.Builder().withIdleTimeout(IDLE_TIMEOUT) .withConnectionTimeout(CONNECTION_TIMEOUT).withMinConnections(MIN_CONNECTIONS) .withMaxConnections(MAX_CONNECTIONS).withRemotePort(PORT).withRemoteAddress(REMOTE_ADDRESS) .withExecutor(EXECUTOR).withBootstrap(BOOTSTRAP).build(); assertEquals(node.getRemoteAddress(), REMOTE_ADDRESS); assertEquals(node.getNodeState(), RiakNode.State.CREATED); assertEquals(node.getMaxConnections(), MAX_CONNECTIONS); assertEquals(node.getConnectionTimeout(), CONNECTION_TIMEOUT); assertEquals(node.getIdleTimeout(), IDLE_TIMEOUT); assertEquals(node.getMinConnections(), MIN_CONNECTIONS); assertEquals(node.getRemoteAddress(), REMOTE_ADDRESS); assertEquals(node.availablePermits(), MAX_CONNECTIONS); assertEquals(node.getPort(), PORT); } @Test public void nodeRegistersListeners() throws UnknownHostException { RiakNode node = new RiakNode.Builder().build(); NodeStateListener listener = mock(NodeStateListener.class); node.addStateListener(listener); boolean removed = node.removeStateListener(listener); assertTrue(removed); } @Test public void nodeNotifiesListeners() throws UnknownHostException, Exception { RiakNode node = new RiakNode.Builder().build(); NodeStateListener listener = mock(NodeStateListener.class); node.addStateListener(listener); Whitebox.invokeMethod(node, "notifyStateListeners", new Object[0]); verify(listener).nodeStateChanged(node, RiakNode.State.CREATED); } @Test public void nodeStartsMinConnections() throws InterruptedException, UnknownHostException { final int MIN_CONNECTIONS = 5; ChannelFuture future = mock(ChannelFuture.class); Channel c = mock(Channel.class); Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); doReturn(future).when(c).closeFuture(); doReturn(true).when(c).isOpen(); doReturn(future).when(future).await(); doReturn(true).when(future).isSuccess(); doReturn(c).when(future).channel(); doReturn(future).when(bootstrap).connect(); doReturn(bootstrap).when(bootstrap).clone(); RiakNode node = new RiakNode.Builder().withBootstrap(bootstrap).withMinConnections(MIN_CONNECTIONS).build(); node.start(); Deque<?> available = Whitebox.getInternalState(node, "available"); assertEquals(MIN_CONNECTIONS, available.size()); assertEquals(node.getNodeState(), State.RUNNING); } @Test public void NodeRespectsMax() throws InterruptedException, UnknownHostException, Exception { final int MAX_CONNECTIONS = 2; ChannelFuture future = mock(ChannelFuture.class); Channel c = mock(Channel.class); Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); doReturn(future).when(c).closeFuture(); doReturn(true).when(c).isOpen(); doReturn(future).when(future).await(); doReturn(true).when(future).isSuccess(); doReturn(c).when(future).channel(); doReturn(future).when(bootstrap).connect(); doReturn(bootstrap).when(bootstrap).clone(); RiakNode node = new RiakNode.Builder().withBootstrap(bootstrap).withMaxConnections(MAX_CONNECTIONS).build(); node.start(); for (int i = 0; i < MAX_CONNECTIONS; i++) { assertNotNull(Whitebox.invokeMethod(node, "getConnection", new Object[0])); } assertNull(Whitebox.invokeMethod(node, "getConnection", new Object[0])); assertEquals(0, node.availablePermits()); node.setMaxConnections(MAX_CONNECTIONS + 1); assertNotNull(Whitebox.invokeMethod(node, "getConnection", new Object[0])); assertEquals(0, node.availablePermits()); } @Test public void channelsReturnedCorrectly() throws InterruptedException, UnknownHostException, Exception { final int MAX_CONNECTIONS = 1; ChannelFuture future = mock(ChannelFuture.class); Channel c = mock(Channel.class); Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); doReturn(future).when(c).closeFuture(); doReturn(true).when(c).isOpen(); doReturn(future).when(future).await(); doReturn(true).when(future).isSuccess(); doReturn(c).when(future).channel(); doReturn(future).when(bootstrap).connect(); doReturn(bootstrap).when(bootstrap).clone(); RiakNode node = new RiakNode.Builder().withBootstrap(bootstrap).withMaxConnections(MAX_CONNECTIONS).build(); node.start(); assertNotNull(Whitebox.invokeMethod(node, "getConnection", new Object[0])); assertNull(Whitebox.invokeMethod(node, "getConnection", new Object[0])); Whitebox.invokeMethod(node, "returnConnection", c); Deque<?> available = Whitebox.getInternalState(node, "available"); assertEquals(1, available.size()); assertNotNull(Whitebox.invokeMethod(node, "getConnection", new Object[0])); } @Test public void healthCheckChangesState() throws InterruptedException, UnknownHostException, Exception { ChannelFuture future = mock(ChannelFuture.class); Channel c = mock(Channel.class); Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); doReturn(future).when(c).closeFuture(); doReturn(true).when(c).isOpen(); doReturn(future).when(future).await(); doReturn(false).when(future).isSuccess(); doReturn(c).when(future).channel(); doReturn(future).when(bootstrap).connect(); doReturn(bootstrap).when(bootstrap).clone(); RiakNode node = new RiakNode.Builder().withBootstrap(bootstrap).build(); for (int i = 0; i < 5; i++) { ChannelFutureListener listener = Whitebox.getInternalState(node, "inAvailableCloseListener", RiakNode.class); listener.operationComplete(future); } NodeStateListener listener = mock(NodeStateListener.class); node.addStateListener(listener); Whitebox.setInternalState(node, "state", State.RUNNING); Whitebox.invokeMethod(node, "checkHealth", new Object[0]); verify(listener).nodeStateChanged(node, State.HEALTH_CHECKING); } @Test public void idleReaperTest() throws InterruptedException, UnknownHostException, Exception { ChannelFuture future = mock(ChannelFuture.class); Channel c = mock(Channel.class); Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); doReturn(future).when(c).closeFuture(); doReturn(true).when(c).isOpen(); doReturn(future).when(future).await(); doReturn(true).when(future).isSuccess(); doReturn(c).when(future).channel(); doReturn(future).when(bootstrap).connect(); doReturn(bootstrap).when(bootstrap).clone(); RiakNode node = new RiakNode.Builder().withBootstrap(bootstrap).withMinConnections(1).withIdleTimeout(1) .build(); node.start(); Channel[] channelArray = new Channel[6]; for (int i = 0; i < 6; i++) { channelArray[i] = Whitebox.invokeMethod(node, "getConnection", new Object[0]); assertNotNull(channelArray[i]); } for (Channel channel : channelArray) { Whitebox.invokeMethod(node, "returnConnection", channel); } Deque<?> available = Whitebox.getInternalState(node, "available"); assertEquals(6, available.size()); Thread.sleep(10); Whitebox.invokeMethod(node, "reapIdleConnections", new Object[0]); assertEquals(1, available.size()); } @Test public void nodeExecutesOperation() throws InterruptedException, UnknownHostException { Channel channel = mock(Channel.class); ChannelPipeline channelPipeline = mock(ChannelPipeline.class); ChannelFuture future = mock(ChannelFuture.class); FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); RiakMessage response = PowerMockito.mock(RiakMessage.class); Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); doReturn(future).when(channel).closeFuture(); doReturn(true).when(channel).isOpen(); doReturn(channelPipeline).when(channel).pipeline(); doReturn(future).when(channel).writeAndFlush(operation); doReturn(future).when(future).await(); doReturn(true).when(future).isSuccess(); doReturn(channel).when(future).channel(); doReturn(future).when(bootstrap).connect(); doReturn(bootstrap).when(bootstrap).clone(); RiakNode node = new RiakNode.Builder().withBootstrap(bootstrap).build(); node.start(); boolean accepted = node.execute(operation); assertTrue(accepted); verify(channel).writeAndFlush(operation); verify(operation).setLastNode(node); assertEquals(1, node.getNumInProgress()); node.onSuccess(channel, response); assertEquals(0, node.getNumInProgress()); verify(operation).isDone(); } @Test public void nodeFailsOperation() throws InterruptedException, UnknownHostException { Channel channel = mock(Channel.class); ChannelPipeline channelPipeline = mock(ChannelPipeline.class); ChannelFuture future = mock(ChannelFuture.class); FutureOperation operation = PowerMockito.spy(new FutureOperationImpl()); Throwable t = mock(Throwable.class); Bootstrap bootstrap = PowerMockito.spy(new Bootstrap()); doReturn(future).when(channel).closeFuture(); doReturn(true).when(channel).isOpen(); doReturn(channelPipeline).when(channel).pipeline(); doReturn(future).when(channel).writeAndFlush(operation); doReturn(future).when(future).await(); doReturn(true).when(future).isSuccess(); doReturn(channel).when(future).channel(); doReturn(future).when(bootstrap).connect(); doReturn(bootstrap).when(bootstrap).clone(); RiakNode node = new RiakNode.Builder().withBootstrap(bootstrap).build(); node.start(); boolean accepted = node.execute(operation); assertTrue(accepted); verify(channel).writeAndFlush(operation); verify(operation).setLastNode(node); Map<?, ?> inProgressMap = Whitebox.getInternalState(node, "inProgressMap"); assertEquals(1, inProgressMap.size()); node.onException(channel, t); await().atMost(500, TimeUnit.MILLISECONDS) .until(fieldIn(operation).ofType(Throwable.class).andWithName("exception"), equalTo(t)); } private class FutureOperationImpl extends FutureOperation<String, Message, Void> { @Override protected String convert(List<Message> rawResponse) { return "value"; } @Override protected Message decode(RiakMessage rawMessage) { return null; } @Override protected RiakMessage createChannelMessage() { return new RiakMessage((byte) 0, new byte[0]); } @Override public Void getQueryInfo() { return null; } } }