/* * Copyright 2017 The gRPC Authors * * 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 io.grpc; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import io.grpc.LoadBalancer.CreateSubchannelArgs; import io.grpc.LoadBalancer.PickResult; import io.grpc.LoadBalancer.ResolvedAddresses; import io.grpc.LoadBalancer.Subchannel; import java.net.SocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Unit tests for the inner classes in {@link LoadBalancer}. */ @RunWith(JUnit4.class) public class LoadBalancerTest { private final Subchannel subchannel = mock(Subchannel.class); private final Subchannel subchannel2 = mock(Subchannel.class); private final ClientStreamTracer.Factory tracerFactory = mock(ClientStreamTracer.Factory.class); private final Status status = Status.UNAVAILABLE.withDescription("for test"); private final Status status2 = Status.UNAVAILABLE.withDescription("for test 2"); private final EquivalentAddressGroup eag = new EquivalentAddressGroup(new SocketAddress() {}); private final Attributes attrs = Attributes.newBuilder() .set(Attributes.Key.create("trash"), new Object()) .build(); @Test public void pickResult_withSubchannel() { PickResult result = PickResult.withSubchannel(subchannel); assertThat(result.getSubchannel()).isSameInstanceAs(subchannel); assertThat(result.getStatus()).isSameInstanceAs(Status.OK); assertThat(result.getStreamTracerFactory()).isNull(); assertThat(result.isDrop()).isFalse(); } @Test public void pickResult_withSubchannelAndTracer() { PickResult result = PickResult.withSubchannel(subchannel, tracerFactory); assertThat(result.getSubchannel()).isSameInstanceAs(subchannel); assertThat(result.getStatus()).isSameInstanceAs(Status.OK); assertThat(result.getStreamTracerFactory()).isSameInstanceAs(tracerFactory); assertThat(result.isDrop()).isFalse(); } @Test public void pickResult_withNoResult() { PickResult result = PickResult.withNoResult(); assertThat(result.getSubchannel()).isNull(); assertThat(result.getStatus()).isSameInstanceAs(Status.OK); assertThat(result.getStreamTracerFactory()).isNull(); assertThat(result.isDrop()).isFalse(); } @Test public void pickResult_withError() { PickResult result = PickResult.withError(status); assertThat(result.getSubchannel()).isNull(); assertThat(result.getStatus()).isSameInstanceAs(status); assertThat(result.getStreamTracerFactory()).isNull(); assertThat(result.isDrop()).isFalse(); } @Test public void pickResult_withDrop() { PickResult result = PickResult.withDrop(status); assertThat(result.getSubchannel()).isNull(); assertThat(result.getStatus()).isSameInstanceAs(status); assertThat(result.getStreamTracerFactory()).isNull(); assertThat(result.isDrop()).isTrue(); } @Test public void pickResult_equals() { PickResult sc1 = PickResult.withSubchannel(subchannel); PickResult sc2 = PickResult.withSubchannel(subchannel); PickResult sc3 = PickResult.withSubchannel(subchannel, tracerFactory); PickResult sc4 = PickResult.withSubchannel(subchannel2); PickResult nr = PickResult.withNoResult(); PickResult error1 = PickResult.withError(status); PickResult error2 = PickResult.withError(status2); PickResult error3 = PickResult.withError(status2); PickResult drop1 = PickResult.withDrop(status); PickResult drop2 = PickResult.withDrop(status); PickResult drop3 = PickResult.withDrop(status2); assertThat(sc1).isNotEqualTo(nr); assertThat(sc1).isNotEqualTo(error1); assertThat(sc1).isNotEqualTo(drop1); assertThat(sc1).isEqualTo(sc2); assertThat(sc1).isNotEqualTo(sc3); assertThat(sc1).isNotEqualTo(sc4); assertThat(error1).isNotEqualTo(error2); assertThat(error2).isEqualTo(error3); assertThat(drop1).isEqualTo(drop2); assertThat(drop1).isNotEqualTo(drop3); assertThat(error1.getStatus()).isEqualTo(drop1.getStatus()); assertThat(error1).isNotEqualTo(drop1); } @Test public void helper_createSubchannelList_throws() { try { new NoopHelper().createSubchannel(CreateSubchannelArgs.newBuilder() .setAddresses(eag) .setAttributes(attrs) .build()); fail("Should throw"); } catch (UnsupportedOperationException e) { // expected } } @Test public void subchannel_getAddresses_delegates() { class OverrideGetAllAddresses extends EmptySubchannel { boolean ran; @Override public List getAllAddresses() { ran = true; return Arrays.asList(eag); } } OverrideGetAllAddresses subchannel = new OverrideGetAllAddresses(); assertThat(subchannel.getAddresses()).isEqualTo(eag); assertThat(subchannel.ran).isTrue(); } @Test(expected = IllegalStateException.class) public void subchannel_getAddresses_throwsOnTwoAddrs() { new EmptySubchannel() { boolean ran; @Override public List getAllAddresses() { ran = true; // Doubling up eag is technically a bad idea, but nothing here cares return Arrays.asList(eag, eag); } }.getAddresses(); } @Test public void createSubchannelArgs_option_keyOps() { CreateSubchannelArgs.Key testKey = CreateSubchannelArgs.Key.create("test-key"); String testValue = "test-value"; CreateSubchannelArgs.Key testWithDefaultKey = CreateSubchannelArgs.Key .createWithDefault("test-key", testValue); CreateSubchannelArgs args = CreateSubchannelArgs.newBuilder() .setAddresses(eag) .setAttributes(attrs) .build(); assertThat(args.getOption(testKey)).isNull(); assertThat(args.getOption(testWithDefaultKey)).isSameInstanceAs(testValue); } @Test public void createSubchannelArgs_option_addGet() { String testValue = "test-value"; CreateSubchannelArgs.Key testKey = CreateSubchannelArgs.Key.create("test-key"); CreateSubchannelArgs args = CreateSubchannelArgs.newBuilder() .setAddresses(eag) .setAttributes(attrs) .addOption(testKey, testValue) .build(); assertThat(args.getOption(testKey)).isEqualTo(testValue); } @Test public void createSubchannelArgs_option_lastOneWins() { String testValue1 = "test-value-1"; String testValue2 = "test-value-2"; CreateSubchannelArgs.Key testKey = CreateSubchannelArgs.Key.create("test-key"); CreateSubchannelArgs args = CreateSubchannelArgs.newBuilder() .setAddresses(eag) .setAttributes(attrs) .addOption(testKey, testValue1) .addOption(testKey, testValue2) .build(); assertThat(args.getOption(testKey)).isEqualTo(testValue2); } @Test public void createSubchannelArgs_build() { CreateSubchannelArgs.Key testKey = CreateSubchannelArgs.Key.create("test-key"); Object testValue = new Object(); CreateSubchannelArgs args = CreateSubchannelArgs.newBuilder() .setAddresses(eag) .setAttributes(attrs) .addOption(testKey, testValue) .build(); CreateSubchannelArgs rebuildedArgs = args.toBuilder().build(); assertThat(rebuildedArgs.getAddresses()).containsExactly(eag); assertThat(rebuildedArgs.getAttributes()).isSameInstanceAs(attrs); assertThat(rebuildedArgs.getOption(testKey)).isSameInstanceAs(testValue); } @Test public void createSubchannelArgs_toString() { CreateSubchannelArgs.Key testKey = CreateSubchannelArgs.Key.create("test-key"); CreateSubchannelArgs args = CreateSubchannelArgs.newBuilder() .setAddresses(eag) .setAttributes(attrs) .addOption(testKey, "test-value") .build(); String str = args.toString(); assertThat(str).contains("addrs="); assertThat(str).contains("attrs="); assertThat(str).contains("customOptions="); } @Deprecated @Test public void handleResolvedAddresses_delegatesToAcceptResolvedAddresses() { final AtomicReference resultCapture = new AtomicReference<>(); LoadBalancer balancer = new LoadBalancer() { @Override public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) { resultCapture.set(resolvedAddresses); return true; } @Override public void handleNameResolutionError(Status error) { } @Override public void handleSubchannelState(Subchannel subchannel, ConnectivityStateInfo state) { } @Override public void shutdown() { } }; List servers = Arrays.asList( new EquivalentAddressGroup(new SocketAddress(){}), new EquivalentAddressGroup(new SocketAddress(){})); ResolvedAddresses addresses = ResolvedAddresses.newBuilder().setAddresses(servers) .setAttributes(attrs).build(); balancer.handleResolvedAddresses(addresses); assertThat(resultCapture.get()).isEqualTo( ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(attrs).build()); } @Deprecated @Test public void acceptResolvedAddresses_delegatesToHandleResolvedAddressGroups() { final AtomicReference addressesCapture = new AtomicReference<>(); LoadBalancer balancer = new LoadBalancer() { @Override public void handleResolvedAddresses(ResolvedAddresses addresses) { addressesCapture.set(addresses); } @Override public void handleNameResolutionError(Status error) { } @Override public void handleSubchannelState(Subchannel subchannel, ConnectivityStateInfo state) { } @Override public void shutdown() { } }; List servers = Arrays.asList( new EquivalentAddressGroup(new SocketAddress(){}), new EquivalentAddressGroup(new SocketAddress(){})); ResolvedAddresses addresses = ResolvedAddresses.newBuilder().setAddresses(servers) .setAttributes(attrs).build(); balancer.handleResolvedAddresses(addresses); assertThat(addressesCapture.get().getAddresses()).isEqualTo(servers); assertThat(addressesCapture.get().getAttributes()).isEqualTo(attrs); } @Deprecated @Test public void acceptResolvedAddresses_noInfiniteLoop() { final List addressesCapture = new ArrayList<>(); LoadBalancer balancer = new LoadBalancer() { @Override public void handleResolvedAddresses(ResolvedAddresses addresses) { addressesCapture.add(addresses); super.handleResolvedAddresses(addresses); } @Override public void handleNameResolutionError(Status error) { } @Override public void shutdown() { } }; List servers = Arrays.asList( new EquivalentAddressGroup(new SocketAddress(){}), new EquivalentAddressGroup(new SocketAddress(){})); ResolvedAddresses addresses = ResolvedAddresses.newBuilder().setAddresses(servers) .setAttributes(attrs).build(); balancer.handleResolvedAddresses(addresses); assertThat(addressesCapture).hasSize(1); assertThat(addressesCapture.get(0).getAddresses()).isEqualTo(servers); assertThat(addressesCapture.get(0).getAttributes()).isEqualTo(attrs); } private static class NoopHelper extends LoadBalancer.Helper { @Override public ManagedChannel createOobChannel(EquivalentAddressGroup eag, String authority) { return null; } @Override public void updateBalancingState( ConnectivityState newState, LoadBalancer.SubchannelPicker newPicker) {} @Override public SynchronizationContext getSynchronizationContext() { return null; } @Override public String getAuthority() { return null; } } private static class EmptySubchannel extends LoadBalancer.Subchannel { @Override public void shutdown() {} @Override public void requestConnection() {} @Override public Attributes getAttributes() { return null; } } }