diff --git a/core/src/test/java/io/grpc/internal/PickFirstLeafLoadBalancerTest.java b/core/src/test/java/io/grpc/internal/PickFirstLeafLoadBalancerTest.java index 7aae0c2731..335d199d8b 100644 --- a/core/src/test/java/io/grpc/internal/PickFirstLeafLoadBalancerTest.java +++ b/core/src/test/java/io/grpc/internal/PickFirstLeafLoadBalancerTest.java @@ -123,16 +123,14 @@ public class PickFirstLeafLoadBalancerTest { private ArgumentCaptor createArgsCaptor; @Captor private ArgumentCaptor stateListenerCaptor; - private final Helper mockHelper = mock(Helper.class, delegatesTo(new MockHelperImpl())); - @Mock + private Helper mockHelper; private FakeSubchannel mockSubchannel1; - @Mock + private FakeSubchannel mockSubchannel1n2; private FakeSubchannel mockSubchannel2; - @Mock + private FakeSubchannel mockSubchannel2n2; private FakeSubchannel mockSubchannel3; - @Mock + private FakeSubchannel mockSubchannel3n2; private FakeSubchannel mockSubchannel4; - @Mock private FakeSubchannel mockSubchannel5; @Mock // This LoadBalancer doesn't use any of the arg fields, as verified in tearDown(). private PickSubchannelArgs mockArgs; @@ -150,23 +148,28 @@ public class PickFirstLeafLoadBalancerTest { SocketAddress addr = new FakeSocketAddress("server" + i); servers.add(new EquivalentAddressGroup(addr)); } - mockSubchannel1 = mock(FakeSubchannel.class); - mockSubchannel2 = mock(FakeSubchannel.class); - mockSubchannel3 = mock(FakeSubchannel.class); - mockSubchannel4 = mock(FakeSubchannel.class); - mockSubchannel5 = mock(FakeSubchannel.class); - when(mockSubchannel1.getAttributes()).thenReturn(Attributes.EMPTY); - when(mockSubchannel2.getAttributes()).thenReturn(Attributes.EMPTY); - when(mockSubchannel3.getAttributes()).thenReturn(Attributes.EMPTY); - when(mockSubchannel4.getAttributes()).thenReturn(Attributes.EMPTY); - when(mockSubchannel5.getAttributes()).thenReturn(Attributes.EMPTY); - - when(mockSubchannel1.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(0))); - when(mockSubchannel2.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(1))); - when(mockSubchannel3.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(2))); - when(mockSubchannel4.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(3))); - when(mockSubchannel5.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(4))); + mockSubchannel1 = mock(FakeSubchannel.class, delegatesTo( + new FakeSubchannel(Arrays.asList(servers.get(0)), Attributes.EMPTY))); + mockSubchannel1n2 = mock(FakeSubchannel.class, delegatesTo( + new FakeSubchannel(Arrays.asList(servers.get(0)), Attributes.EMPTY))); + mockSubchannel2 = mock(FakeSubchannel.class, delegatesTo( + new FakeSubchannel(Arrays.asList(servers.get(1)), Attributes.EMPTY))); + mockSubchannel2n2 = mock(FakeSubchannel.class, delegatesTo( + new FakeSubchannel(Arrays.asList(servers.get(1)), Attributes.EMPTY))); + mockSubchannel3 = mock(FakeSubchannel.class, delegatesTo( + new FakeSubchannel(Arrays.asList(servers.get(2)), Attributes.EMPTY))); + mockSubchannel3n2 = mock(FakeSubchannel.class, delegatesTo( + new FakeSubchannel(Arrays.asList(servers.get(2)), Attributes.EMPTY))); + mockSubchannel4 = mock(FakeSubchannel.class, delegatesTo( + new FakeSubchannel(Arrays.asList(servers.get(3)), Attributes.EMPTY))); + mockSubchannel5 = mock(FakeSubchannel.class, delegatesTo( + new FakeSubchannel(Arrays.asList(servers.get(4)), Attributes.EMPTY))); + mockHelper = mock(Helper.class, delegatesTo(new MockHelperImpl(Arrays.asList( + mockSubchannel1, mockSubchannel1n2, + mockSubchannel2, mockSubchannel2n2, + mockSubchannel3, mockSubchannel3n2, + mockSubchannel4, mockSubchannel5)))); loadBalancer = new PickFirstLeafLoadBalancer(mockHelper); } @@ -251,14 +254,14 @@ public class PickFirstLeafLoadBalancerTest { PickResult pick2 = pickerCaptor.getValue().pickSubchannel(mockArgs); assertEquals(pick1, pick2); verifyNoMoreInteractions(mockHelper); - assertThat(pick1.toString()).contains("subchannel=null"); + assertThat(pick1.getSubchannel()).isNull(); stateListener2.onSubchannelState(ConnectivityStateInfo.forNonError(READY)); verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture()); PickResult pick3 = pickerCaptor.getValue().pickSubchannel(mockArgs); PickResult pick4 = pickerCaptor.getValue().pickSubchannel(mockArgs); assertEquals(pick3, pick4); - assertThat(pick3.toString()).contains("subchannel=Mock"); + assertThat(pick3.getSubchannel()).isEqualTo(mockSubchannel2); } @Test @@ -569,7 +572,7 @@ public class PickFirstLeafLoadBalancerTest { InOrder inOrder = inOrder(mockHelper); SocketAddress socketAddress = servers.get(0).getAddresses().get(0); EquivalentAddressGroup badEag = new EquivalentAddressGroup( - Lists.newArrayList(socketAddress, socketAddress), affinity); + Lists.newArrayList(socketAddress, socketAddress)); List newServers = Lists.newArrayList(badEag); loadBalancer.acceptResolvedAddresses( @@ -727,7 +730,7 @@ public class PickFirstLeafLoadBalancerTest { @Test public void nameResolutionTemporaryError() { List newServers = Lists.newArrayList(servers.get(0)); - InOrder inOrder = inOrder(mockHelper, mockSubchannel1); + InOrder inOrder = inOrder(mockHelper, mockSubchannel1, mockSubchannel1n2); loadBalancer.acceptResolvedAddresses( ResolvedAddresses.newBuilder().setAddresses(newServers).setAttributes(affinity).build()); inOrder.verify(mockSubchannel1).start(stateListenerCaptor.capture()); @@ -744,14 +747,15 @@ public class PickFirstLeafLoadBalancerTest { loadBalancer.acceptResolvedAddresses( ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(affinity).build()); inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture()); - inOrder.verify(mockSubchannel1).start(stateListenerCaptor.capture()); + inOrder.verify(mockSubchannel1n2).start(stateListenerCaptor.capture()); SubchannelStateListener stateListener2 = stateListenerCaptor.getValue(); assertNull(pickerCaptor.getValue().pickSubchannel(mockArgs).getSubchannel()); stateListener2.onSubchannelState(ConnectivityStateInfo.forNonError(READY)); inOrder.verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture()); - assertEquals(mockSubchannel1, pickerCaptor.getValue().pickSubchannel(mockArgs).getSubchannel()); + assertEquals(mockSubchannel1n2, + pickerCaptor.getValue().pickSubchannel(mockArgs).getSubchannel()); } @@ -1027,7 +1031,7 @@ public class PickFirstLeafLoadBalancerTest { @Test public void updateAddresses_disjoint_ready_twice() { InOrder inOrder = inOrder(mockHelper, mockSubchannel1, mockSubchannel2, - mockSubchannel3, mockSubchannel4); + mockSubchannel3, mockSubchannel4, mockSubchannel1n2, mockSubchannel2n2); // Creating first set of endpoints/addresses List oldServers = Lists.newArrayList(servers.get(0), servers.get(1)); SubchannelStateListener stateListener2 = null; @@ -1126,7 +1130,7 @@ public class PickFirstLeafLoadBalancerTest { ResolvedAddresses.newBuilder().setAddresses(newestServers).setAttributes(affinity).build()); inOrder.verify(mockSubchannel3).shutdown(); inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture()); - inOrder.verify(mockSubchannel1).start(stateListenerCaptor.capture()); + inOrder.verify(mockSubchannel1n2).start(stateListenerCaptor.capture()); stateListener = stateListenerCaptor.getValue(); assertEquals(CONNECTING, loadBalancer.getConcludedConnectivityState()); picker = pickerCaptor.getValue(); @@ -1135,7 +1139,7 @@ public class PickFirstLeafLoadBalancerTest { assertEquals(picker.pickSubchannel(mockArgs), picker.pickSubchannel(mockArgs)); // But the picker calls requestConnection() only once - inOrder.verify(mockSubchannel1).requestConnection(); + inOrder.verify(mockSubchannel1n2).requestConnection(); assertEquals(PickResult.withNoResult(), pickerCaptor.getValue().pickSubchannel(mockArgs)); assertEquals(CONNECTING, loadBalancer.getConcludedConnectivityState()); @@ -1150,23 +1154,24 @@ public class PickFirstLeafLoadBalancerTest { stateListener.onSubchannelState(ConnectivityStateInfo.forTransientFailure(CONNECTION_ERROR)); // Starting connection attempt to address 2 - if (!enableHappyEyeballs) { - inOrder.verify(mockSubchannel2).start(stateListenerCaptor.capture()); - stateListener2 = stateListenerCaptor.getValue(); - } - inOrder.verify(mockSubchannel2).requestConnection(); + FakeSubchannel mockSubchannel2Attempt = + enableHappyEyeballs ? mockSubchannel2n2 : mockSubchannel2; + inOrder.verify(mockSubchannel2Attempt).start(stateListenerCaptor.capture()); + stateListener2 = stateListenerCaptor.getValue(); + inOrder.verify(mockSubchannel2Attempt).requestConnection(); // Connection attempt to address 2 is successful stateListener2.onSubchannelState(ConnectivityStateInfo.forNonError(READY)); assertEquals(READY, loadBalancer.getConcludedConnectivityState()); - inOrder.verify(mockSubchannel1).shutdown(); + inOrder.verify(mockSubchannel1n2).shutdown(); // Successful connection shuts down other subchannel inOrder.verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture()); picker = pickerCaptor.getValue(); // Verify that picker still returns correct subchannel - assertEquals(PickResult.withSubchannel(mockSubchannel2), picker.pickSubchannel(mockArgs)); + assertEquals( + PickResult.withSubchannel(mockSubchannel2Attempt), picker.pickSubchannel(mockArgs)); } @Test @@ -2048,7 +2053,7 @@ public class PickFirstLeafLoadBalancerTest { // Starting first connection attempt InOrder inOrder = inOrder(mockHelper, mockSubchannel1, mockSubchannel2, - mockSubchannel3, mockSubchannel4); // captor: captures + mockSubchannel3, mockSubchannel4, mockSubchannel1n2); // captor: captures // Creating first set of endpoints/addresses List addrs = @@ -2084,9 +2089,9 @@ public class PickFirstLeafLoadBalancerTest { // Calling pickSubchannel() requests a connection. assertEquals(picker.pickSubchannel(mockArgs), picker.pickSubchannel(mockArgs)); - inOrder.verify(mockSubchannel1).start(stateListenerCaptor.capture()); + inOrder.verify(mockSubchannel1n2).start(stateListenerCaptor.capture()); SubchannelStateListener stateListener3 = stateListenerCaptor.getValue(); - inOrder.verify(mockSubchannel1).requestConnection(); + inOrder.verify(mockSubchannel1n2).requestConnection(); when(mockSubchannel1.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(0))); // gives the same result when called twice @@ -2101,7 +2106,7 @@ public class PickFirstLeafLoadBalancerTest { // second subchannel connection attempt succeeds inOrder.verify(mockSubchannel2).requestConnection(); stateListener2.onSubchannelState(ConnectivityStateInfo.forNonError(READY)); - inOrder.verify(mockSubchannel1).shutdown(); + inOrder.verify(mockSubchannel1n2).shutdown(); inOrder.verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture()); assertEquals(READY, loadBalancer.getConcludedConnectivityState()); @@ -2146,7 +2151,7 @@ public class PickFirstLeafLoadBalancerTest { public void ready_then_transient_failure_again() { // Starting first connection attempt InOrder inOrder = inOrder(mockHelper, mockSubchannel1, mockSubchannel2, - mockSubchannel3, mockSubchannel4); // captor: captures + mockSubchannel3, mockSubchannel4, mockSubchannel1n2); // captor: captures // Creating first set of endpoints/addresses List addrs = @@ -2183,9 +2188,9 @@ public class PickFirstLeafLoadBalancerTest { // Calling pickSubchannel() requests a connection, gives the same result when called twice. assertEquals(picker.pickSubchannel(mockArgs), picker.pickSubchannel(mockArgs)); - inOrder.verify(mockSubchannel1).start(stateListenerCaptor.capture()); + inOrder.verify(mockSubchannel1n2).start(stateListenerCaptor.capture()); SubchannelStateListener stateListener3 = stateListenerCaptor.getValue(); - inOrder.verify(mockSubchannel1).requestConnection(); + inOrder.verify(mockSubchannel1n2).requestConnection(); when(mockSubchannel3.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(0))); stateListener3.onSubchannelState(ConnectivityStateInfo.forNonError(CONNECTING)); inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture()); @@ -2201,7 +2206,7 @@ public class PickFirstLeafLoadBalancerTest { assertEquals(READY, loadBalancer.getConcludedConnectivityState()); // verify that picker returns correct subchannel - inOrder.verify(mockSubchannel1).shutdown(); + inOrder.verify(mockSubchannel1n2).shutdown(); inOrder.verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture()); picker = pickerCaptor.getValue(); assertEquals(PickResult.withSubchannel(mockSubchannel2), picker.pickSubchannel(mockArgs)); @@ -2309,7 +2314,8 @@ public class PickFirstLeafLoadBalancerTest { public void happy_eyeballs_pick_pushes_index_over_end() { Assume.assumeTrue(enableHappyEyeballs); // This test is only for happy eyeballs - InOrder inOrder = inOrder(mockHelper, mockSubchannel1, mockSubchannel2, mockSubchannel3); + InOrder inOrder = inOrder(mockHelper, mockSubchannel1, mockSubchannel2, mockSubchannel3, + mockSubchannel2n2, mockSubchannel3n2); Status error = Status.UNAUTHENTICATED.withDescription("simulated failure"); List addrs = @@ -2359,9 +2365,9 @@ public class PickFirstLeafLoadBalancerTest { // Try pushing after end with just picks listeners[0].onSubchannelState(ConnectivityStateInfo.forNonError(READY)); - for (SubchannelStateListener listener : listeners) { - listener.onSubchannelState(ConnectivityStateInfo.forNonError(IDLE)); - } + verify(mockSubchannel2).shutdown(); + verify(mockSubchannel3).shutdown(); + listeners[0].onSubchannelState(ConnectivityStateInfo.forNonError(IDLE)); loadBalancer.acceptResolvedAddresses( ResolvedAddresses.newBuilder().setAddresses(addrs).setAttributes(affinity).build()); inOrder.verify(mockHelper).updateBalancingState(eq(IDLE), pickerCaptor.capture()); @@ -2372,11 +2378,14 @@ public class PickFirstLeafLoadBalancerTest { } assertEquals(IDLE, loadBalancer.getConcludedConnectivityState()); - for (SubchannelStateListener listener : listeners) { - listener.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error)); - } + listeners[0].onSubchannelState(ConnectivityStateInfo.forTransientFailure(error)); + inOrder.verify(mockSubchannel2n2).start(stateListenerCaptor.capture()); + stateListenerCaptor.getValue().onSubchannelState( + ConnectivityStateInfo.forTransientFailure(error)); + inOrder.verify(mockSubchannel3n2).start(stateListenerCaptor.capture()); + stateListenerCaptor.getValue().onSubchannelState( + ConnectivityStateInfo.forTransientFailure(error)); assertEquals(TRANSIENT_FAILURE, loadBalancer.getConcludedConnectivityState()); - } @Test @@ -2571,9 +2580,22 @@ public class PickFirstLeafLoadBalancerTest { @Override public String toString() { - return "FakeSocketAddress-" + name; + return "FakeSocketAddress(" + name + ")"; } + @Override + public boolean equals(Object o) { + if (!(o instanceof FakeSocketAddress)) { + return false; + } + FakeSocketAddress that = (FakeSocketAddress) o; + return this.name.equals(that.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } } private void forwardTimeByConnectionDelay() { @@ -2631,15 +2653,26 @@ public class PickFirstLeafLoadBalancerTest { @Override public void shutdown() { + listener.onSubchannelState(ConnectivityStateInfo.forNonError(SHUTDOWN)); } @Override public void requestConnection() { - listener.onSubchannelState(ConnectivityStateInfo.forNonError(CONNECTING)); + } + + @Override + public String toString() { + return "FakeSubchannel@" + hashCode() + "(" + eags + ")"; } } private class MockHelperImpl extends LoadBalancer.Helper { + private final List subchannels; + + public MockHelperImpl(List subchannels) { + this.subchannels = new ArrayList(subchannels); + } + @Override public ManagedChannel createOobChannel(EquivalentAddressGroup eag, String authority) { return null; @@ -2672,16 +2705,17 @@ public class PickFirstLeafLoadBalancerTest { @Override public Subchannel createSubchannel(CreateSubchannelArgs args) { - SocketAddress addr = args.getAddresses().get(0).getAddresses().get(0); - List fakeSubchannels = - Arrays.asList(mockSubchannel1, mockSubchannel2, mockSubchannel3, mockSubchannel4, - mockSubchannel5); - for (int i = 1; i <= 5; i++) { - if (addr.toString().equals(new FakeSocketAddress("server" + i).toString())) { - return fakeSubchannels.get(i - 1); + for (int i = 0; i < subchannels.size(); i++) { + Subchannel subchannel = subchannels.get(i); + List addrs = subchannel.getAllAddresses(); + verify(subchannel, atLeast(1)).getAllAddresses(); // ignore the interaction + if (!args.getAddresses().equals(addrs)) { + continue; } + subchannels.remove(i); + return subchannel; } - throw new IllegalArgumentException("Unexpected address: " + addr); + throw new IllegalArgumentException("Unexpected addresses: " + args.getAddresses()); } } -} \ No newline at end of file +}