Skip to content

Commit 3259d67

Browse files
committed
add test case for xDS server and others
1 parent 36c2927 commit 3259d67

File tree

8 files changed

+247
-13
lines changed

8 files changed

+247
-13
lines changed

api/src/main/java/io/grpc/ForwardingChannelBuilder2.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public T intercept(ClientInterceptor... interceptors) {
9595
}
9696

9797
@Override
98-
protected T interceptWithTarget(InterceptorFactory factory) {
98+
public T interceptWithTarget(InterceptorFactory factory) {
9999
delegate().interceptWithTarget(factory);
100100
return thisT();
101101
}
@@ -258,7 +258,7 @@ public T disableServiceConfigLookUp() {
258258
}
259259

260260
@Override
261-
protected T addMetricSink(MetricSink metricSink) {
261+
public T addMetricSink(MetricSink metricSink) {
262262
delegate().addMetricSink(metricSink);
263263
return thisT();
264264
}

api/src/main/java/io/grpc/ManagedChannelBuilder.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,17 +160,14 @@ public T offloadExecutor(Executor executor) {
160160
public abstract T intercept(ClientInterceptor... interceptors);
161161

162162
/**
163-
* Internal-only: Adds a factory that will construct an interceptor based on the channel's target.
163+
* Adds a factory that will construct an interceptor based on the channel's target.
164164
* This can be used to work around nameResolverFactory() changing the target string.
165165
*/
166-
@Internal
167-
protected T interceptWithTarget(InterceptorFactory factory) {
166+
public T interceptWithTarget(InterceptorFactory factory) {
168167
throw new UnsupportedOperationException();
169168
}
170169

171-
/** Internal-only. */
172-
@Internal
173-
protected interface InterceptorFactory {
170+
public interface InterceptorFactory {
174171
ClientInterceptor newInterceptor(String target);
175172
}
176173

@@ -638,8 +635,7 @@ public T disableServiceConfigLookUp() {
638635
* @return this
639636
* @since 1.64.0
640637
*/
641-
@Internal
642-
protected T addMetricSink(MetricSink metricSink) {
638+
public T addMetricSink(MetricSink metricSink) {
643639
throw new UnsupportedOperationException();
644640
}
645641

api/src/test/java/io/grpc/ChildChannelConfigurersTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,4 @@ public void compose_ignoresNulls() {
5555

5656
verify(builder).userAgent("agent1");
5757
}
58-
}
58+
}

core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ public ManagedChannelImplBuilder intercept(ClientInterceptor... interceptors) {
404404
}
405405

406406
@Override
407-
protected ManagedChannelImplBuilder interceptWithTarget(InterceptorFactory factory) {
407+
public ManagedChannelImplBuilder interceptWithTarget(InterceptorFactory factory) {
408408
// Add a placeholder instance to the interceptor list, and replace it with a real instance
409409
// during build().
410410
this.interceptors.add(new InterceptorFactoryWrapper(factory));
@@ -713,7 +713,7 @@ public ManagedChannelImplBuilder enableCheckAuthority() {
713713
}
714714

715715
@Override
716-
protected ManagedChannelImplBuilder addMetricSink(MetricSink metricSink) {
716+
public ManagedChannelImplBuilder addMetricSink(MetricSink metricSink) {
717717
metricSinks.add(checkNotNull(metricSink, "metric sink"));
718718
return this;
719719
}

core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import static org.junit.Assert.assertThrows;
2727
import static org.junit.Assert.assertTrue;
2828
import static org.junit.Assert.fail;
29+
import static org.mockito.ArgumentMatchers.any;
2930
import static org.mockito.Mockito.doReturn;
3031
import static org.mockito.Mockito.mock;
3132
import static org.mockito.Mockito.verify;
@@ -34,6 +35,7 @@
3435
import com.google.common.util.concurrent.MoreExecutors;
3536
import io.grpc.CallOptions;
3637
import io.grpc.Channel;
38+
import io.grpc.ChildChannelConfigurer;
3739
import io.grpc.ClientCall;
3840
import io.grpc.ClientInterceptor;
3941
import io.grpc.CompressorRegistry;
@@ -70,6 +72,7 @@
7072
import org.junit.Test;
7173
import org.junit.runner.RunWith;
7274
import org.junit.runners.JUnit4;
75+
import org.mockito.ArgumentCaptor;
7376
import org.mockito.Mock;
7477
import org.mockito.junit.MockitoJUnit;
7578
import org.mockito.junit.MockitoRule;
@@ -762,6 +765,72 @@ public void setNameResolverExtArgs() {
762765
assertThat(builder.nameResolverCustomArgs.get(testKey)).isEqualTo(42);
763766
}
764767

768+
@Test
769+
public void childChannelConfigurer_propagatesMetricsAndInterceptors_xdsTarget() {
770+
// Setup Mocks
771+
when(mockClientTransportFactory.getScheduledExecutorService())
772+
.thenReturn(clock.getScheduledExecutorService());
773+
when(mockClientTransportFactoryBuilder.buildClientTransportFactory())
774+
.thenReturn(mockClientTransportFactory);
775+
when(mockClientTransportFactory.getSupportedSocketAddressTypes())
776+
.thenReturn(Collections.singleton(InetSocketAddress.class));
777+
778+
MetricSink mockMetricSink = mock(MetricSink.class);
779+
ClientInterceptor mockInterceptor = mock(ClientInterceptor.class);
780+
781+
// Define the Configurer
782+
ChildChannelConfigurer configurer = (builder) -> {
783+
builder.addMetricSink(mockMetricSink);
784+
785+
// Assuming InternalInterceptorFactory is also accessible
786+
builder.interceptWithTarget(target -> mockInterceptor);
787+
};
788+
789+
// Mock NameResolver.Factory to capture Args
790+
NameResolver.Factory mockNameResolverFactory = mock(NameResolver.Factory.class);
791+
when(mockNameResolverFactory.getDefaultScheme()).thenReturn("xds");
792+
NameResolver mockNameResolver = mock(NameResolver.class);
793+
when(mockNameResolver.getServiceAuthority()).thenReturn("foo.authority");
794+
ArgumentCaptor<NameResolver.Args> argsCaptor = ArgumentCaptor.forClass(NameResolver.Args.class);
795+
when(mockNameResolverFactory.newNameResolver(any(),
796+
argsCaptor.capture())).thenReturn(mockNameResolver);
797+
798+
// Use the configurer and the mock factory
799+
NameResolverRegistry registry = new NameResolverRegistry();
800+
registry.register(new NameResolverFactoryToProviderFacade(mockNameResolverFactory));
801+
802+
ManagedChannelBuilder<?> parentBuilder = new ManagedChannelImplBuilder(
803+
"xds:///my-service-target",
804+
mockClientTransportFactoryBuilder,
805+
new FixedPortProvider(DUMMY_PORT))
806+
.childChannelConfigurer(configurer)
807+
.nameResolverRegistry(registry);
808+
809+
ManagedChannel channel = parentBuilder.build();
810+
grpcCleanupRule.register(channel);
811+
812+
// Verify that newNameResolver was called
813+
verify(mockNameResolverFactory).newNameResolver(any(), any());
814+
815+
// Extract the parent channel from Args
816+
NameResolver.Args args = argsCaptor.getValue();
817+
ManagedChannel parentChannelInArgs = args.getParentChannel();
818+
assertNotNull("Parent channel should be present in NameResolver.Args",
819+
parentChannelInArgs);
820+
821+
// Verify the configurer on the parent channel is the one we passed
822+
assertThat(parentChannelInArgs.getChildChannelConfigurer()).isSameInstanceAs(configurer);
823+
824+
// Verify the configurer logically applies (by running it on a mock)
825+
ManagedChannelBuilder<?> mockChildBuilder = mock(ManagedChannelBuilder.class);
826+
// Stub addMetricSink to return the builder to avoid generic return type issues
827+
doReturn(mockChildBuilder).when(mockChildBuilder).addMetricSink(any());
828+
829+
configurer.accept(mockChildBuilder);
830+
verify(mockChildBuilder).addMetricSink(mockMetricSink);
831+
verify(mockChildBuilder).interceptWithTarget(any());
832+
}
833+
765834
@Test
766835
public void metricSinks() {
767836
MetricSink mocksink = mock(MetricSink.class);

xds/src/test/java/io/grpc/xds/GrpcXdsTransportFactoryTest.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,25 @@
1717
package io.grpc.xds;
1818

1919
import static com.google.common.truth.Truth.assertThat;
20+
import static org.mockito.ArgumentMatchers.any;
21+
import static org.mockito.Mockito.mock;
22+
import static org.mockito.Mockito.verify;
23+
import static org.mockito.Mockito.when;
2024

2125
import com.google.common.util.concurrent.SettableFuture;
2226
import io.envoyproxy.envoy.service.discovery.v3.AggregatedDiscoveryServiceGrpc;
2327
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
2428
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
2529
import io.grpc.BindableService;
30+
import io.grpc.CallOptions;
31+
import io.grpc.Channel;
32+
import io.grpc.ChildChannelConfigurer;
33+
import io.grpc.ClientInterceptor;
2634
import io.grpc.Grpc;
2735
import io.grpc.InsecureChannelCredentials;
2836
import io.grpc.InsecureServerCredentials;
37+
import io.grpc.ManagedChannel;
38+
import io.grpc.ManagedChannelBuilder;
2939
import io.grpc.MethodDescriptor;
3040
import io.grpc.Server;
3141
import io.grpc.Status;
@@ -139,5 +149,74 @@ public void onStatusReceived(Status status) {
139149
endFuture.set(status);
140150
}
141151
}
152+
153+
@Test
154+
@SuppressWarnings("unchecked")
155+
public void verifyConfigApplied_interceptor() {
156+
// Create a mock Interceptor
157+
final ClientInterceptor mockInterceptor = mock(ClientInterceptor.class);
158+
when(mockInterceptor.interceptCall(any(MethodDescriptor.class),
159+
any(CallOptions.class), any(Channel.class)))
160+
.thenReturn(new io.grpc.NoopClientCall<>());
161+
162+
// Create Configurer that adds the interceptor
163+
ChildChannelConfigurer configurer = (builder) -> builder.intercept(mockInterceptor);
164+
165+
// Mock Parent Channel
166+
ManagedChannel mockParentChannel = mock(ManagedChannel.class);
167+
when(mockParentChannel.getChildChannelConfigurer()).thenReturn(configurer);
168+
169+
// Create Factory
170+
GrpcXdsTransportFactory factory = new GrpcXdsTransportFactory(
171+
null,
172+
mockParentChannel,
173+
null);
174+
175+
// Create Transport
176+
XdsTransportFactory.XdsTransport transport = factory.create(
177+
Bootstrapper.ServerInfo.create("localhost:8080", InsecureChannelCredentials.create()));
178+
179+
// Create a Call to trigger interceptors
180+
MethodDescriptor<Void, Void> method = MethodDescriptor.<Void, Void>newBuilder()
181+
.setType(MethodDescriptor.MethodType.UNARY)
182+
.setFullMethodName("service/method")
183+
.setFullMethodName("service/method")
184+
.setRequestMarshaller(mock(MethodDescriptor.Marshaller.class))
185+
.setResponseMarshaller(mock(MethodDescriptor.Marshaller.class))
186+
.build();
187+
188+
transport.createStreamingCall(method.getFullMethodName(), method.getRequestMarshaller(),
189+
method.getResponseMarshaller());
190+
191+
// Verify interceptor was invoked
192+
verify(mockInterceptor).interceptCall(any(MethodDescriptor.class),
193+
any(CallOptions.class), any(Channel.class));
194+
195+
transport.shutdown();
196+
}
197+
198+
@Test
199+
public void useParentServerConfig() {
200+
// 1. Mock Server and Configurer
201+
Server mockServer = mock(Server.class);
202+
ChildChannelConfigurer mockConfigurer = mock(ChildChannelConfigurer.class);
203+
when(mockServer.getChildChannelConfigurer()).thenReturn(mockConfigurer);
204+
205+
// 2. Create Factory with Parent Server
206+
GrpcXdsTransportFactory factory = new GrpcXdsTransportFactory(
207+
null, // CallCredentials
208+
null, // Parent Channel
209+
mockServer);
210+
211+
// 3. Create Transport (triggers channel creation)
212+
XdsTransportFactory.XdsTransport transport = factory.create(
213+
Bootstrapper.ServerInfo.create("localhost:8080", InsecureChannelCredentials.create()));
214+
215+
// 4. Verify Configurer was accessed and applied
216+
verify(mockServer).getChildChannelConfigurer();
217+
verify(mockConfigurer).accept(any(ManagedChannelBuilder.class));
218+
219+
transport.shutdown();
220+
}
142221
}
143222

xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
import static org.mockito.ArgumentMatchers.any;
2828
import static org.mockito.ArgumentMatchers.anyInt;
2929
import static org.mockito.ArgumentMatchers.anyLong;
30+
import static org.mockito.ArgumentMatchers.anyString;
3031
import static org.mockito.ArgumentMatchers.eq;
32+
import static org.mockito.ArgumentMatchers.isNull;
3133
import static org.mockito.Mockito.atLeast;
3234
import static org.mockito.Mockito.lenient;
3335
import static org.mockito.Mockito.mock;
@@ -68,6 +70,7 @@
6870
import io.grpc.NameResolver.ServiceConfigParser;
6971
import io.grpc.NoopClientCall;
7072
import io.grpc.NoopClientCall.NoopClientCallListener;
73+
import io.grpc.ProxyDetector;
7174
import io.grpc.Server;
7275
import io.grpc.Status;
7376
import io.grpc.Status.Code;
@@ -2951,4 +2954,64 @@ void deliverErrorStatus() {
29512954
listener.onClose(Status.UNAVAILABLE, new Metadata());
29522955
}
29532956
}
2957+
2958+
@Test
2959+
public void start_passesParentChannelToClientPoolFactory() {
2960+
// Create a mock Parent Channel
2961+
ManagedChannel mockParentChannel = mock(ManagedChannel.class);
2962+
2963+
// Build NameResolver.Args containing the parent channel
2964+
NameResolver.Args args = NameResolver.Args.newBuilder()
2965+
.setDefaultPort(8080)
2966+
.setProxyDetector(mock(ProxyDetector.class))
2967+
.setSynchronizationContext(syncContext)
2968+
.setServiceConfigParser(serviceConfigParser)
2969+
.setChannelLogger(mock(ChannelLogger.class))
2970+
.setParentChannel(mockParentChannel)
2971+
.build();
2972+
2973+
// Mock the XdsClientPoolFactory
2974+
XdsClientPoolFactory mockPoolFactory = mock(XdsClientPoolFactory.class);
2975+
@SuppressWarnings("unchecked")
2976+
ObjectPool<XdsClient> mockObjectPool = mock(ObjectPool.class);
2977+
XdsClient mockXdsClient = mock(XdsClient.class);
2978+
when(mockObjectPool.getObject()).thenReturn(mockXdsClient);
2979+
when(mockXdsClient.getBootstrapInfo()).thenReturn(bootstrapInfo);
2980+
2981+
when(mockPoolFactory.getOrCreate(
2982+
anyString(),
2983+
any(BootstrapInfo.class),
2984+
any(MetricRecorder.class),
2985+
any(ManagedChannel.class),
2986+
any()))
2987+
.thenReturn(mockObjectPool);
2988+
2989+
XdsNameResolver resolver = new XdsNameResolver(
2990+
URI.create(AUTHORITY),
2991+
null, // targetAuthority (nullable)
2992+
AUTHORITY, // name
2993+
null, // overrideAuthority (nullable)
2994+
serviceConfigParser,
2995+
syncContext,
2996+
scheduler,
2997+
mockPoolFactory,
2998+
mockRandom,
2999+
FilterRegistry.getDefaultRegistry(),
3000+
rawBootstrap,
3001+
metricRecorder,
3002+
args);
3003+
3004+
// Start the resolver (this triggers the factory call)
3005+
resolver.start(mockListener);
3006+
3007+
// Ensure the factory's getOrCreate method was called with parent channel
3008+
verify(mockPoolFactory).getOrCreate(
3009+
eq(AUTHORITY),
3010+
any(BootstrapInfo.class),
3011+
eq(metricRecorder),
3012+
eq(mockParentChannel),
3013+
isNull());
3014+
3015+
resolver.shutdown();
3016+
}
29543017
}

0 commit comments

Comments
 (0)