Skip to content

Commit

Permalink
Add test for GrpclbNameResolver
Browse files Browse the repository at this point in the history
  • Loading branch information
voidzcy committed Feb 19, 2020
1 parent a28394f commit df1d33d
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 7 deletions.
15 changes: 9 additions & 6 deletions core/src/main/java/io/grpc/internal/DnsNameResolver.java
Expand Up @@ -532,11 +532,12 @@ private static long getNetworkAddressCacheTtlNanos(boolean isAndroid) {
/**
* Describes a parsed SRV record.
*/
protected static final class SrvRecord {
@VisibleForTesting
public static final class SrvRecord {
public final String host;
public final int port;

SrvRecord(String host, int port) {
public SrvRecord(String host, int port) {
this.host = host;
this.port = port;
}
Expand Down Expand Up @@ -569,12 +570,12 @@ public String toString() {
}

@VisibleForTesting
void setAddressResolver(AddressResolver addressResolver) {
protected void setAddressResolver(AddressResolver addressResolver) {
this.addressResolver = addressResolver;
}

@VisibleForTesting
void setResourceResolver(ResourceResolver resourceResolver) {
protected void setResourceResolver(ResourceResolver resourceResolver) {
this.resourceResolver.set(resourceResolver);
}

Expand All @@ -600,7 +601,8 @@ interface ResourceResolverFactory {
/**
* AddressResolver resolves a hostname into a list of addresses.
*/
protected interface AddressResolver {
@VisibleForTesting
public interface AddressResolver {
List<InetAddress> resolveAddress(String host) throws Exception;
}

Expand All @@ -616,7 +618,8 @@ public List<InetAddress> resolveAddress(String host) throws UnknownHostException
/**
* {@link ResourceResolver} is a Dns ResourceRecord resolver.
*/
protected interface ResourceResolver {
@VisibleForTesting
public interface ResourceResolver {
List<String> resolveTxt(String host) throws Exception;

List<SrvRecord> resolveSrv(String host) throws Exception;
Expand Down
1 change: 0 additions & 1 deletion core/src/main/java/io/grpc/internal/GrpcAttributes.java
Expand Up @@ -21,7 +21,6 @@
import io.grpc.Grpc;
import io.grpc.NameResolver;
import io.grpc.SecurityLevel;
import java.util.List;
import java.util.Map;

/**
Expand Down
24 changes: 24 additions & 0 deletions grpclb/src/main/java/io/grpc/grpclb/GrpclbNameResolver.java
Expand Up @@ -16,6 +16,7 @@

package io.grpc.grpclb;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Stopwatch;
import io.grpc.Attributes;
import io.grpc.EquivalentAddressGroup;
Expand Down Expand Up @@ -140,4 +141,27 @@ private List<EquivalentAddressGroup> resolveBalancerAddresses() {
}
return Collections.unmodifiableList(balancerAddresses);
}

@VisibleForTesting
@Override
protected void setAddressResolver(AddressResolver addressResolver) {
super.setAddressResolver(addressResolver);
}

@VisibleForTesting
@Override
protected void setResourceResolver(ResourceResolver resourceResolver) {
super.setResourceResolver(resourceResolver);
}

@VisibleForTesting
@Override
protected String getHost() {
return super.getHost();
}

@VisibleForTesting
static void setEnableTxt(boolean enableTxt) {
DnsNameResolver.enableTxt = enableTxt;
}
}
203 changes: 203 additions & 0 deletions grpclb/src/test/java/io/grpc/grpclb/GrpclbNameResolverTest.java
Expand Up @@ -16,7 +16,210 @@

package io.grpc.grpclb;

import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.common.collect.Iterables;
import io.grpc.Attributes;
import io.grpc.ChannelLogger;
import io.grpc.EquivalentAddressGroup;
import io.grpc.NameResolver;
import io.grpc.NameResolver.ResolutionResult;
import io.grpc.NameResolver.ServiceConfigParser;
import io.grpc.Status;
import io.grpc.Status.Code;
import io.grpc.SynchronizationContext;
import io.grpc.internal.DnsNameResolver.AddressResolver;
import io.grpc.internal.DnsNameResolver.ResourceResolver;
import io.grpc.internal.DnsNameResolver.SrvRecord;
import io.grpc.internal.FakeClock;
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.SharedResourceHolder.Resource;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

/** Unit tests for {@link GrpclbNameResolver}. */
@RunWith(JUnit4.class)
public class GrpclbNameResolverTest {

@Rule
public final MockitoRule mocks = MockitoJUnit.rule();

private static final String NAME = "foo.googleapis.com";
private static final int DEFAULT_PORT = 887;

private final SynchronizationContext syncContext = new SynchronizationContext(
new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
throw new AssertionError(e);
}
});

private final FakeClock fakeClock = new FakeClock();
private final FakeExecutorResource fakeExecutorResource = new FakeExecutorResource();

private final class FakeExecutorResource implements Resource<Executor> {

@Override
public Executor create() {
return fakeClock.getScheduledExecutorService();
}

@Override
public void close(Executor instance) {}
}

@Captor private ArgumentCaptor<ResolutionResult> resultCaptor;
@Captor private ArgumentCaptor<Status> errorCaptor;
@Mock private ServiceConfigParser serviceConfigParser;
@Mock private NameResolver.Listener2 mockListener;

private GrpclbNameResolver resolver;
private String hostName;

@Before
public void setUp() {
GrpclbNameResolver.setEnableTxt(true);
NameResolver.Args args =
NameResolver.Args.newBuilder()
.setDefaultPort(DEFAULT_PORT)
.setProxyDetector(GrpcUtil.NOOP_PROXY_DETECTOR)
.setSynchronizationContext(syncContext)
.setServiceConfigParser(serviceConfigParser)
.setChannelLogger(mock(ChannelLogger.class))
.build();
resolver =
new GrpclbNameResolver(
null, NAME, args, fakeExecutorResource, fakeClock.getStopwatchSupplier().get(),
/* isAndroid */false);
hostName = resolver.getHost();
assertThat(hostName).isEqualTo(NAME);
}

@Test
public void resolve_emptyResult() {
resolver.setAddressResolver(new AddressResolver() {
@Override
public List<InetAddress> resolveAddress(String host) throws Exception {
return Collections.emptyList();
}
});
resolver.setResourceResolver(new ResourceResolver() {
@Override
public List<String> resolveTxt(String host) throws Exception {
return Collections.emptyList();
}

@Override
public List<SrvRecord> resolveSrv(String host) throws Exception {
return Collections.emptyList();
}
});

resolver.start(mockListener);
assertThat(fakeClock.runDueTasks()).isEqualTo(1);

verify(mockListener).onResult(resultCaptor.capture());
ResolutionResult result = resultCaptor.getValue();
assertThat(result.getAddresses()).isEmpty();
assertThat(result.getAttributes()).isEqualTo(Attributes.EMPTY);
assertThat(result.getServiceConfig()).isNull();
}

@Test
public void resolve_balancerAddrsAsAttributes() throws Exception {
InetAddress backendAddr = InetAddress.getByAddress(new byte[] {127, 0, 0, 0});
InetAddress lbAddr = InetAddress.getByAddress(new byte[] {10, 1, 0, 0});
String balancerName = "foo.example.com."; // original name in SRV record
EquivalentAddressGroup balancerAddr =
new EquivalentAddressGroup(
new InetSocketAddress(lbAddr, 8080),
Attributes.newBuilder()
.set(GrpclbConstants.ATTR_LB_ADDR_AUTHORITY, "foo.example.com")
.build());
SrvRecord srvRecord = new SrvRecord(balancerName, 8080);
AddressResolver mockAddressResolver = mock(AddressResolver.class);
when(mockAddressResolver.resolveAddress(hostName))
.thenReturn(Collections.singletonList(backendAddr));
when(mockAddressResolver.resolveAddress(balancerName))
.thenReturn(Collections.singletonList(lbAddr));
ResourceResolver mockResourceResolver = mock(ResourceResolver.class);
when(mockResourceResolver.resolveTxt(anyString())).thenReturn(Collections.<String>emptyList());
when(mockResourceResolver.resolveSrv(anyString()))
.thenReturn(Collections.singletonList(srvRecord));

resolver.setAddressResolver(mockAddressResolver);
resolver.setResourceResolver(mockResourceResolver);

resolver.start(mockListener);
assertThat(fakeClock.runDueTasks()).isEqualTo(1);
verify(mockListener).onResult(resultCaptor.capture());
ResolutionResult result = resultCaptor.getValue();
InetSocketAddress resolvedBackendAddr =
(InetSocketAddress) Iterables.getOnlyElement(
Iterables.getOnlyElement(result.getAddresses()).getAddresses());
assertThat(resolvedBackendAddr.getAddress()).isEqualTo(backendAddr);
assertThat(result.getAttributes().get(GrpclbConstants.ATTR_LB_ADDRS))
.containsExactly(balancerAddr);
verify(mockAddressResolver).resolveAddress(hostName);
verify(mockResourceResolver).resolveTxt("_grpc_config." + hostName);
verify(mockResourceResolver).resolveSrv("_grpclb._tcp." + hostName);
}

@Test
public void resolve_nullResourceResolver() throws Exception {
InetAddress backendAddr = InetAddress.getByAddress(new byte[] {127, 0, 0, 0});
AddressResolver mockAddressResolver = mock(AddressResolver.class);
when(mockAddressResolver.resolveAddress(anyString()))
.thenReturn(Collections.singletonList(backendAddr));
ResourceResolver resourceResolver = null;

resolver.setAddressResolver(mockAddressResolver);
resolver.setResourceResolver(resourceResolver);

resolver.start(mockListener);
assertThat(fakeClock.runDueTasks()).isEqualTo(1);
verify(mockListener).onResult(resultCaptor.capture());
ResolutionResult result = resultCaptor.getValue();
assertThat(result.getAddresses())
.containsExactly(
new EquivalentAddressGroup(new InetSocketAddress(backendAddr, DEFAULT_PORT)));
assertThat(result.getAttributes()).isEqualTo(Attributes.EMPTY);
assertThat(result.getServiceConfig()).isNull();
}

@Test
public void resolve_nullResourceResolver_addressFailure() throws Exception {
AddressResolver mockAddressResolver = mock(AddressResolver.class);
when(mockAddressResolver.resolveAddress(anyString())).thenThrow(new IOException("no addr"));
ResourceResolver resourceResolver = null;

resolver.setAddressResolver(mockAddressResolver);
resolver.setResourceResolver(resourceResolver);

resolver.start(mockListener);
assertThat(fakeClock.runDueTasks()).isEqualTo(1);
verify(mockListener).onError(errorCaptor.capture());
Status errorStatus = errorCaptor.getValue();
assertThat(errorStatus.getCode()).isEqualTo(Code.UNAVAILABLE);
assertThat(errorStatus.getCause()).hasMessageThat().contains("no addr");
}
}

0 comments on commit df1d33d

Please sign in to comment.