Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Helper class to allow in process servers to use peer uids in test #11014

Merged
merged 8 commits into from
Mar 22, 2024
Merged
1 change: 1 addition & 0 deletions binder/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dependencies {
testImplementation project(':grpc-testing')
testImplementation project(':grpc-inprocess')
testImplementation testFixtures(project(':grpc-core'))
testImplementation project(':grpc-binder')
jdcormie marked this conversation as resolved.
Show resolved Hide resolved

androidTestAnnotationProcessor libraries.auto.value
androidTestImplementation project(':grpc-testing')
Expand Down
120 changes: 120 additions & 0 deletions binder/src/test/java/io/grpc/binder/PeerUidTestHelperTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package io.grpc.binder;

import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.collect.ImmutableList;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientInterceptors;
import io.grpc.ManagedChannel;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.ServerInterceptors;
import io.grpc.ServerServiceDefinition;
import io.grpc.inprocess.InProcessChannelBuilder;
import io.grpc.inprocess.InProcessServerBuilder;
import io.grpc.stub.ClientCalls;
import io.grpc.stub.MetadataUtils;
import io.grpc.stub.ServerCalls;
import io.grpc.testing.GrpcCleanupRule;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public final class PeerUidTestHelperTest {
@Rule public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();

private static final int FAKE_UID = 12345;

private final AtomicReference<PeerUid> clientUidCapture = new AtomicReference<>();

@Test
public void keyPopulatedWithInterceptorAndHeader() throws Exception {
makeServiceCall(/* includeInterceptor= */ true, /* includeUidInHeader= */ true, FAKE_UID);
assertThat(clientUidCapture.get()).isEqualTo(new PeerUid(FAKE_UID));
}

@Test
public void keyNotPopulatedWithInterceptorAndNoHeader() throws Exception {
makeServiceCall(/* includeInterceptor= */ true, /* includeUidInHeader= */ false, /* uid= */ -1);
assertThat(clientUidCapture.get()).isNull();
}

@Test
public void keyNotPopulatedWithoutInterceptorAndWithHeader() throws Exception {
makeServiceCall(
/* includeInterceptor= */ false, /* includeUidInHeader= */ true, /* uid= */ FAKE_UID);
assertThat(clientUidCapture.get()).isNull();
}

private final MethodDescriptor<String, String> method =
MethodDescriptor.newBuilder(StringMarshaller.INSTANCE, StringMarshaller.INSTANCE)
.setFullMethodName("test/method")
.setType(MethodDescriptor.MethodType.UNARY)
.build();

private void makeServiceCall(boolean includeInterceptor, boolean includeUidInHeader, int uid)
throws Exception {
ServerCallHandler<String, String> callHandler =
ServerCalls.asyncUnaryCall(
(req, respObserver) -> {
clientUidCapture.set(PeerUids.REMOTE_PEER.get());
respObserver.onNext(req);
respObserver.onCompleted();
});
ImmutableList<ServerInterceptor> interceptors;
if (includeInterceptor) {
interceptors = ImmutableList.of(PeerUidTestHelper.newTestPeerIdentifyingServerInterceptor());
} else {
interceptors = ImmutableList.of();
}
ServerServiceDefinition serviceDef =
ServerInterceptors.intercept(
ServerServiceDefinition.builder("test").addMethod(method, callHandler).build(),
interceptors);

InProcessServerBuilder server =
InProcessServerBuilder.forName("test").directExecutor().addService(serviceDef);

grpcCleanup.register(server.build().start());

Channel channel = InProcessChannelBuilder.forName("test").directExecutor().build();
grpcCleanup.register((ManagedChannel) channel);

if (includeUidInHeader) {
Metadata header = new Metadata();
header.put(PeerUidTestHelper.UID_KEY, uid);
channel =
ClientInterceptors.intercept(channel, MetadataUtils.newAttachHeadersInterceptor(header));
}

ClientCalls.blockingUnaryCall(channel, method, CallOptions.DEFAULT, "hello");
}

private static class StringMarshaller implements MethodDescriptor.Marshaller<String> {
public static final StringMarshaller INSTANCE = new StringMarshaller();

@Override
public InputStream stream(String value) {
return new ByteArrayInputStream(value.getBytes(UTF_8));
}

@Override
public String parse(InputStream stream) {
try {
return new String(stream.readAllBytes(), UTF_8);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
}
56 changes: 56 additions & 0 deletions binder/src/testFixtures/java/io/grpc/binder/PeerUidTestHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.grpc.binder;

import io.grpc.Context;
import io.grpc.Contexts;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;

/** Class which helps set up {@link PeerUids} to be used in tests. */
public class PeerUidTestHelper {

/** The UID of the calling package is set with the value of this key. */
public static final Metadata.Key<Integer> UID_KEY =
Metadata.Key.of("binder-remote-uid-for-unit-testing", PeerUidTestMarshaller.INSTANCE);

/**
* Creates an interceptor that associates the {@link PeerUids#REMOTE_PEER} key in the request
* {@link Context} with a UID provided by the client in the {@link #UID_KEY} request header, if
* present.
*
* <p>The returned interceptor works with any gRPC transport but is meant for in-process unit
* testing of gRPC/binder services that depend on {@link PeerUids}.
*/
public static ServerInterceptor newTestPeerIdentifyingServerInterceptor() {
return new ServerInterceptor() {
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
Context context = Context.current();
if (headers.containsKey(UID_KEY)) {
context = context.withValue(PeerUids.REMOTE_PEER, new PeerUid(headers.get(UID_KEY)));
}

return Contexts.interceptCall(context, call, headers, next);
}
};
}

private PeerUidTestHelper() {}

private static class PeerUidTestMarshaller implements Metadata.AsciiMarshaller<Integer> {
public static final PeerUidTestMarshaller INSTANCE = new PeerUidTestMarshaller();

@Override
public String toAsciiString(Integer value) {
return value.toString();
}

@Override
public Integer parseAsciiString(String serialized) {
return Integer.parseInt(serialized);
}
}
;
}
Loading