blob: beaf3335e2cc2804e1e0c16435126a55c09a00b3 [file] [log] [blame]
/*
* 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<EquivalentAddressGroup> 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<EquivalentAddressGroup> 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<String> testKey = CreateSubchannelArgs.Key.create("test-key");
String testValue = "test-value";
CreateSubchannelArgs.Key<String> 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<String> 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<String> 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<Object> 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<String> 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<ResolvedAddresses> 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<EquivalentAddressGroup> 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<ResolvedAddresses> 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<EquivalentAddressGroup> 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<ResolvedAddresses> 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<EquivalentAddressGroup> 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;
}
}
}