package com.twitter.common.zookeeper; import java.net.InetSocketAddress; import java.util.Map; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import org.easymock.Capture; import org.easymock.EasyMock; import org.easymock.IAnswer; import org.junit.Before; import org.junit.Test; import com.twitter.common.base.Command; import com.twitter.common.net.pool.DynamicHostSet.HostChangeMonitor; import com.twitter.common.net.pool.DynamicHostSet.MonitorException; import com.twitter.common.testing.easymock.EasyMockTest; import com.twitter.thrift.ServiceInstance; import static org.easymock.EasyMock.createControl; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.getCurrentArguments; public class CompoundServerSetTest extends EasyMockTest { private static final Map<String, InetSocketAddress> AUX_PORTS = ImmutableMap.of(); private static final InetSocketAddress END_POINT = InetSocketAddress.createUnresolved("foo", 12345); private ServerSet.EndpointStatus mockStatus1; private ServerSet.EndpointStatus mockStatus2; private ServerSet.EndpointStatus mockStatus3; private HostChangeMonitor<ServiceInstance> compoundMonitor; private ServerSet serverSet1; private ServerSet serverSet2; private ServerSet serverSet3; private Command stop1; private Command stop2; private Command stop3; private CompoundServerSet compoundServerSet; private ServiceInstance instance1; private ServiceInstance instance2; private ServiceInstance instance3; private void triggerChange(ServiceInstance... hostChanges) { compoundMonitor.onChange(ImmutableSet.copyOf(hostChanges)); } private void triggerChange( Capture<HostChangeMonitor<ServiceInstance>> capture, ServiceInstance... hostChanges) { capture.getValue().onChange(ImmutableSet.copyOf(hostChanges)); } @Before public void setUpMocks() throws Exception { control = createControl(); compoundMonitor = createMock(new Clazz<HostChangeMonitor<ServiceInstance>>() { }); mockStatus1 = createMock(ServerSet.EndpointStatus.class); mockStatus2 = createMock(ServerSet.EndpointStatus.class); mockStatus3 = createMock(ServerSet.EndpointStatus.class); serverSet1 = createMock(ServerSet.class); serverSet2 = createMock(ServerSet.class); serverSet3 = createMock(ServerSet.class); stop1 = createMock(Command.class); stop2 = createMock(Command.class); stop3 = createMock(Command.class); instance1 = createMock(ServiceInstance.class); instance2 = createMock(ServiceInstance.class); instance3 = createMock(ServiceInstance.class); compoundServerSet = new CompoundServerSet(ImmutableList.of(serverSet1, serverSet2, serverSet3)); } @Test public void testJoin() throws Exception { expect(serverSet1.join(END_POINT, AUX_PORTS, 0)).andReturn(mockStatus1); expect(serverSet2.join(END_POINT, AUX_PORTS, 0)).andReturn(mockStatus2); expect(serverSet3.join(END_POINT, AUX_PORTS, 0)).andReturn(mockStatus3); mockStatus1.leave(); mockStatus2.leave(); mockStatus3.leave(); control.replay(); compoundServerSet.join(END_POINT, AUX_PORTS, 0).leave(); } @Test(expected = Group.JoinException.class) public void testJoinFailure() throws Exception { // Throw exception for the first serverSet join. expect(serverSet1.join(END_POINT, AUX_PORTS)) .andThrow(new Group.JoinException("Group join exception", null)); control.replay(); compoundServerSet.join(END_POINT, AUX_PORTS); } @Test(expected = ServerSet.UpdateException.class) public void testStatusUpdateFailure() throws Exception { expect(serverSet1.join(END_POINT, AUX_PORTS)).andReturn(mockStatus1); expect(serverSet2.join(END_POINT, AUX_PORTS)).andReturn(mockStatus2); expect(serverSet3.join(END_POINT, AUX_PORTS)).andReturn(mockStatus3); mockStatus1.leave(); mockStatus2.leave(); expectLastCall().andThrow(new ServerSet.UpdateException("Update exception")); mockStatus3.leave(); control.replay(); compoundServerSet.join(END_POINT, AUX_PORTS).leave(); } @Test public void testMonitor() throws Exception { Capture<HostChangeMonitor<ServiceInstance>> set1Capture = createCapture(); Capture<HostChangeMonitor<ServiceInstance>> set2Capture = createCapture(); Capture<HostChangeMonitor<ServiceInstance>> set3Capture = createCapture(); expect(serverSet1.watch( EasyMock.<HostChangeMonitor<ServiceInstance>>capture(set1Capture))) .andReturn(stop1); expect(serverSet2.watch( EasyMock.<HostChangeMonitor<ServiceInstance>>capture(set2Capture))) .andReturn(stop2); expect(serverSet3.watch( EasyMock.<HostChangeMonitor<ServiceInstance>>capture(set3Capture))) .andReturn(stop3); triggerChange(instance1); triggerChange(instance1, instance2); triggerChange(instance1, instance2, instance3); triggerChange(instance1, instance3); triggerChange(instance1, instance2, instance3); triggerChange(instance3); triggerChange(); control.replay(); compoundServerSet.watch(compoundMonitor); // No new instances. triggerChange(set1Capture); triggerChange(set2Capture); triggerChange(set3Capture); // Add one instance from each serverset triggerChange(set1Capture, instance1); triggerChange(set2Capture, instance2); triggerChange(set3Capture, instance3); // Remove instance2 triggerChange(set2Capture); // instance1 in both serverset1 and serverset2 triggerChange(set2Capture, instance1, instance2); // Remove instances from serversets. triggerChange(set1Capture); triggerChange(set2Capture); triggerChange(set3Capture); } @Test(expected = MonitorException.class) public void testMonitorFailure() throws Exception { serverSet1.watch(EasyMock.<HostChangeMonitor<ServiceInstance>>anyObject()); expectLastCall().andThrow(new MonitorException("Monitor exception", null)); control.replay(); compoundServerSet.watch(compoundMonitor); } @Test public void testInitialChange() throws Exception { // Ensures that a synchronous change notification during the call to monitor() is properly // reported. serverSet1.watch(EasyMock.<HostChangeMonitor<ServiceInstance>>anyObject()); expectLastCall().andAnswer(new IAnswer<Command>() { @Override public Command answer() { @SuppressWarnings("unchecked") HostChangeMonitor<ServiceInstance> monitor = (HostChangeMonitor<ServiceInstance>) getCurrentArguments()[0]; monitor.onChange(ImmutableSet.of(instance1, instance2)); return stop1; } }); compoundMonitor.onChange(ImmutableSet.of(instance1, instance2)); expect(serverSet2.watch(EasyMock.<HostChangeMonitor<ServiceInstance>>anyObject())) .andReturn(stop2); expect(serverSet3.watch(EasyMock.<HostChangeMonitor<ServiceInstance>>anyObject())) .andReturn(stop3); control.replay(); compoundServerSet.watch(compoundMonitor); } @Test public void testStopMonitoring() throws Exception { expect(serverSet1.watch(EasyMock.<HostChangeMonitor<ServiceInstance>>anyObject())) .andReturn(stop1); expect(serverSet2.watch(EasyMock.<HostChangeMonitor<ServiceInstance>>anyObject())) .andReturn(stop2); expect(serverSet3.watch(EasyMock.<HostChangeMonitor<ServiceInstance>>anyObject())) .andReturn(stop3); stop1.execute(); stop2.execute(); stop3.execute(); control.replay(); compoundServerSet.watch(compoundMonitor).execute(); } }