Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright 2017 The gRPC Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package io.grpc.internal;
     18 
     19 import static com.google.common.truth.Truth.assertThat;
     20 import static io.opencensus.tags.unsafe.ContextUtils.TAG_CONTEXT_KEY;
     21 import static java.util.concurrent.TimeUnit.MILLISECONDS;
     22 import static org.junit.Assert.assertEquals;
     23 import static org.junit.Assert.assertFalse;
     24 import static org.junit.Assert.assertNotNull;
     25 import static org.junit.Assert.assertNotSame;
     26 import static org.junit.Assert.assertNull;
     27 import static org.junit.Assert.assertSame;
     28 import static org.junit.Assert.assertTrue;
     29 import static org.junit.Assert.fail;
     30 import static org.mockito.Matchers.any;
     31 import static org.mockito.Matchers.anyString;
     32 import static org.mockito.Matchers.eq;
     33 import static org.mockito.Matchers.isNull;
     34 import static org.mockito.Matchers.same;
     35 import static org.mockito.Mockito.inOrder;
     36 import static org.mockito.Mockito.never;
     37 import static org.mockito.Mockito.reset;
     38 import static org.mockito.Mockito.spy;
     39 import static org.mockito.Mockito.times;
     40 import static org.mockito.Mockito.verify;
     41 import static org.mockito.Mockito.verifyNoMoreInteractions;
     42 import static org.mockito.Mockito.verifyZeroInteractions;
     43 import static org.mockito.Mockito.when;
     44 
     45 import io.grpc.Attributes;
     46 import io.grpc.CallOptions;
     47 import io.grpc.Channel;
     48 import io.grpc.ClientCall;
     49 import io.grpc.ClientInterceptor;
     50 import io.grpc.ClientInterceptors;
     51 import io.grpc.ClientStreamTracer;
     52 import io.grpc.Context;
     53 import io.grpc.Metadata;
     54 import io.grpc.MethodDescriptor;
     55 import io.grpc.ServerCall;
     56 import io.grpc.ServerCallHandler;
     57 import io.grpc.ServerServiceDefinition;
     58 import io.grpc.ServerStreamTracer;
     59 import io.grpc.Status;
     60 import io.grpc.internal.testing.StatsTestUtils;
     61 import io.grpc.internal.testing.StatsTestUtils.FakeStatsRecorder;
     62 import io.grpc.internal.testing.StatsTestUtils.FakeTagContextBinarySerializer;
     63 import io.grpc.internal.testing.StatsTestUtils.FakeTagger;
     64 import io.grpc.internal.testing.StatsTestUtils.MockableSpan;
     65 import io.grpc.testing.GrpcServerRule;
     66 import io.opencensus.contrib.grpc.metrics.RpcMeasureConstants;
     67 import io.opencensus.tags.TagContext;
     68 import io.opencensus.tags.TagValue;
     69 import io.opencensus.trace.BlankSpan;
     70 import io.opencensus.trace.EndSpanOptions;
     71 import io.opencensus.trace.MessageEvent;
     72 import io.opencensus.trace.MessageEvent.Type;
     73 import io.opencensus.trace.Span;
     74 import io.opencensus.trace.SpanBuilder;
     75 import io.opencensus.trace.SpanContext;
     76 import io.opencensus.trace.Tracer;
     77 import io.opencensus.trace.propagation.BinaryFormat;
     78 import io.opencensus.trace.propagation.SpanContextParseException;
     79 import io.opencensus.trace.unsafe.ContextUtils;
     80 import java.io.InputStream;
     81 import java.util.HashSet;
     82 import java.util.List;
     83 import java.util.Random;
     84 import java.util.Set;
     85 import java.util.concurrent.atomic.AtomicReference;
     86 import org.junit.After;
     87 import org.junit.Before;
     88 import org.junit.Rule;
     89 import org.junit.Test;
     90 import org.junit.runner.RunWith;
     91 import org.junit.runners.JUnit4;
     92 import org.mockito.ArgumentCaptor;
     93 import org.mockito.Captor;
     94 import org.mockito.InOrder;
     95 import org.mockito.Mock;
     96 import org.mockito.MockitoAnnotations;
     97 
     98 /**
     99  * Test for {@link CensusStatsModule} and {@link CensusTracingModule}.
    100  */
    101 @RunWith(JUnit4.class)
    102 public class CensusModulesTest {
    103   private static final CallOptions.Key<String> CUSTOM_OPTION =
    104       CallOptions.Key.createWithDefault("option1", "default");
    105   private static final CallOptions CALL_OPTIONS =
    106       CallOptions.DEFAULT.withOption(CUSTOM_OPTION, "customvalue");
    107 
    108   private static class StringInputStream extends InputStream {
    109     final String string;
    110 
    111     StringInputStream(String string) {
    112       this.string = string;
    113     }
    114 
    115     @Override
    116     public int read() {
    117       // InProcessTransport doesn't actually read bytes from the InputStream.  The InputStream is
    118       // passed to the InProcess server and consumed by MARSHALLER.parse().
    119       throw new UnsupportedOperationException("Should not be called");
    120     }
    121   }
    122 
    123   private static final MethodDescriptor.Marshaller<String> MARSHALLER =
    124       new MethodDescriptor.Marshaller<String>() {
    125         @Override
    126         public InputStream stream(String value) {
    127           return new StringInputStream(value);
    128         }
    129 
    130         @Override
    131         public String parse(InputStream stream) {
    132           return ((StringInputStream) stream).string;
    133         }
    134       };
    135 
    136   private final MethodDescriptor<String, String> method =
    137       MethodDescriptor.<String, String>newBuilder()
    138           .setType(MethodDescriptor.MethodType.UNKNOWN)
    139           .setRequestMarshaller(MARSHALLER)
    140           .setResponseMarshaller(MARSHALLER)
    141           .setFullMethodName("package1.service2/method3")
    142           .build();
    143   private final MethodDescriptor<String, String> sampledMethod =
    144       method.toBuilder().setSampledToLocalTracing(true).build();
    145 
    146   private final FakeClock fakeClock = new FakeClock();
    147   private final FakeTagger tagger = new FakeTagger();
    148   private final FakeTagContextBinarySerializer tagCtxSerializer =
    149       new FakeTagContextBinarySerializer();
    150   private final FakeStatsRecorder statsRecorder = new FakeStatsRecorder();
    151   private final Random random = new Random(1234);
    152   private final Span fakeClientParentSpan = MockableSpan.generateRandomSpan(random);
    153   private final Span spyClientSpan = spy(MockableSpan.generateRandomSpan(random));
    154   private final SpanContext fakeClientSpanContext = spyClientSpan.getContext();
    155   private final Span spyServerSpan = spy(MockableSpan.generateRandomSpan(random));
    156   private final byte[] binarySpanContext = new byte[]{3, 1, 5};
    157   private final SpanBuilder spyClientSpanBuilder = spy(new MockableSpan.Builder());
    158   private final SpanBuilder spyServerSpanBuilder = spy(new MockableSpan.Builder());
    159 
    160   @Rule
    161   public final GrpcServerRule grpcServerRule = new GrpcServerRule().directExecutor();
    162 
    163   @Mock
    164   private Tracer tracer;
    165   @Mock
    166   private BinaryFormat mockTracingPropagationHandler;
    167   @Mock
    168   private ClientCall.Listener<String> mockClientCallListener;
    169   @Mock
    170   private ServerCall.Listener<String> mockServerCallListener;
    171   @Captor
    172   private ArgumentCaptor<Status> statusCaptor;
    173   @Captor
    174   private ArgumentCaptor<MessageEvent> messageEventCaptor;
    175 
    176   private CensusStatsModule censusStats;
    177   private CensusTracingModule censusTracing;
    178 
    179   @Before
    180   @SuppressWarnings("unchecked")
    181   public void setUp() throws Exception {
    182     MockitoAnnotations.initMocks(this);
    183     when(spyClientSpanBuilder.startSpan()).thenReturn(spyClientSpan);
    184     when(tracer.spanBuilderWithExplicitParent(anyString(), any(Span.class)))
    185         .thenReturn(spyClientSpanBuilder);
    186     when(spyServerSpanBuilder.startSpan()).thenReturn(spyServerSpan);
    187     when(tracer.spanBuilderWithRemoteParent(anyString(), any(SpanContext.class)))
    188         .thenReturn(spyServerSpanBuilder);
    189     when(mockTracingPropagationHandler.toByteArray(any(SpanContext.class)))
    190         .thenReturn(binarySpanContext);
    191     when(mockTracingPropagationHandler.fromByteArray(any(byte[].class)))
    192         .thenReturn(fakeClientSpanContext);
    193     censusStats =
    194         new CensusStatsModule(
    195             tagger, tagCtxSerializer, statsRecorder, fakeClock.getStopwatchSupplier(), true);
    196     censusTracing = new CensusTracingModule(tracer, mockTracingPropagationHandler);
    197   }
    198 
    199   @After
    200   public void wrapUp() {
    201     assertNull(statsRecorder.pollRecord());
    202   }
    203 
    204   @Test
    205   public void clientInterceptorNoCustomTag() {
    206     testClientInterceptors(false);
    207   }
    208 
    209   @Test
    210   public void clientInterceptorCustomTag() {
    211     testClientInterceptors(true);
    212   }
    213 
    214   // Test that Census ClientInterceptors uses the TagContext and Span out of the current Context
    215   // to create the ClientCallTracer, and that it intercepts ClientCall.Listener.onClose() to call
    216   // ClientCallTracer.callEnded().
    217   private void testClientInterceptors(boolean nonDefaultContext) {
    218     grpcServerRule.getServiceRegistry().addService(
    219         ServerServiceDefinition.builder("package1.service2").addMethod(
    220             method, new ServerCallHandler<String, String>() {
    221                 @Override
    222                 public ServerCall.Listener<String> startCall(
    223                     ServerCall<String, String> call, Metadata headers) {
    224                   call.sendHeaders(new Metadata());
    225                   call.sendMessage("Hello");
    226                   call.close(
    227                       Status.PERMISSION_DENIED.withDescription("No you don't"), new Metadata());
    228                   return mockServerCallListener;
    229                 }
    230               }).build());
    231 
    232     final AtomicReference<CallOptions> capturedCallOptions = new AtomicReference<CallOptions>();
    233     ClientInterceptor callOptionsCaptureInterceptor = new ClientInterceptor() {
    234         @Override
    235         public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
    236             MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
    237           capturedCallOptions.set(callOptions);
    238           return next.newCall(method, callOptions);
    239         }
    240       };
    241     Channel interceptedChannel =
    242         ClientInterceptors.intercept(
    243             grpcServerRule.getChannel(), callOptionsCaptureInterceptor,
    244             censusStats.getClientInterceptor(true, true), censusTracing.getClientInterceptor());
    245     ClientCall<String, String> call;
    246     if (nonDefaultContext) {
    247       Context ctx =
    248           Context.ROOT.withValues(
    249               TAG_CONTEXT_KEY,
    250               tagger.emptyBuilder().put(
    251                   StatsTestUtils.EXTRA_TAG, TagValue.create("extra value")).build(),
    252               ContextUtils.CONTEXT_SPAN_KEY,
    253               fakeClientParentSpan);
    254       Context origCtx = ctx.attach();
    255       try {
    256         call = interceptedChannel.newCall(method, CALL_OPTIONS);
    257       } finally {
    258         ctx.detach(origCtx);
    259       }
    260     } else {
    261       assertEquals(TAG_CONTEXT_KEY.get(Context.ROOT), TAG_CONTEXT_KEY.get());
    262       assertNull(ContextUtils.CONTEXT_SPAN_KEY.get());
    263       call = interceptedChannel.newCall(method, CALL_OPTIONS);
    264     }
    265 
    266     // The interceptor adds tracer factory to CallOptions
    267     assertEquals("customvalue", capturedCallOptions.get().getOption(CUSTOM_OPTION));
    268     assertEquals(2, capturedCallOptions.get().getStreamTracerFactories().size());
    269     assertTrue(
    270         capturedCallOptions.get().getStreamTracerFactories().get(0)
    271         instanceof CensusTracingModule.ClientCallTracer);
    272     assertTrue(
    273         capturedCallOptions.get().getStreamTracerFactories().get(1)
    274         instanceof CensusStatsModule.ClientCallTracer);
    275 
    276     // Make the call
    277     Metadata headers = new Metadata();
    278     call.start(mockClientCallListener, headers);
    279 
    280     StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord();
    281     assertNotNull(record);
    282     TagValue methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD);
    283     assertEquals(method.getFullMethodName(), methodTag.asString());
    284     if (nonDefaultContext) {
    285       TagValue extraTag = record.tags.get(StatsTestUtils.EXTRA_TAG);
    286       assertEquals("extra value", extraTag.asString());
    287       assertEquals(2, record.tags.size());
    288     } else {
    289       assertNull(record.tags.get(StatsTestUtils.EXTRA_TAG));
    290       assertEquals(1, record.tags.size());
    291     }
    292 
    293     if (nonDefaultContext) {
    294       verify(tracer).spanBuilderWithExplicitParent(
    295           eq("Sent.package1.service2.method3"), same(fakeClientParentSpan));
    296       verify(spyClientSpanBuilder).setRecordEvents(eq(true));
    297     } else {
    298       verify(tracer).spanBuilderWithExplicitParent(
    299           eq("Sent.package1.service2.method3"), isNull(Span.class));
    300       verify(spyClientSpanBuilder).setRecordEvents(eq(true));
    301     }
    302     verify(spyClientSpan, never()).end(any(EndSpanOptions.class));
    303 
    304     // End the call
    305     call.halfClose();
    306     call.request(1);
    307 
    308     verify(mockClientCallListener).onClose(statusCaptor.capture(), any(Metadata.class));
    309     Status status = statusCaptor.getValue();
    310     assertEquals(Status.Code.PERMISSION_DENIED, status.getCode());
    311     assertEquals("No you don't", status.getDescription());
    312 
    313     // The intercepting listener calls callEnded() on ClientCallTracer, which records to Census.
    314     record = statsRecorder.pollRecord();
    315     assertNotNull(record);
    316     methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD);
    317     assertEquals(method.getFullMethodName(), methodTag.asString());
    318     TagValue statusTag = record.tags.get(RpcMeasureConstants.RPC_STATUS);
    319     assertEquals(Status.Code.PERMISSION_DENIED.toString(), statusTag.asString());
    320     if (nonDefaultContext) {
    321       TagValue extraTag = record.tags.get(StatsTestUtils.EXTRA_TAG);
    322       assertEquals("extra value", extraTag.asString());
    323     } else {
    324       assertNull(record.tags.get(StatsTestUtils.EXTRA_TAG));
    325     }
    326     verify(spyClientSpan).end(
    327         EndSpanOptions.builder()
    328             .setStatus(
    329                 io.opencensus.trace.Status.PERMISSION_DENIED
    330                     .withDescription("No you don't"))
    331             .setSampleToLocalSpanStore(false)
    332             .build());
    333     verify(spyClientSpan, never()).end();
    334   }
    335 
    336   @Test
    337   public void clientBasicStatsDefaultContext_startsAndFinishes() {
    338     subtestClientBasicStatsDefaultContext(true, true);
    339   }
    340 
    341   @Test
    342   public void clientBasicStatsDefaultContext_startsOnly() {
    343     subtestClientBasicStatsDefaultContext(true, false);
    344   }
    345 
    346   @Test
    347   public void clientBasicStatsDefaultContext_finishesOnly() {
    348     subtestClientBasicStatsDefaultContext(false, true);
    349   }
    350 
    351   @Test
    352   public void clientBasicStatsDefaultContext_neither() {
    353     subtestClientBasicStatsDefaultContext(false, true);
    354   }
    355 
    356   private void subtestClientBasicStatsDefaultContext(boolean recordStarts, boolean recordFinishes) {
    357     CensusStatsModule.ClientCallTracer callTracer =
    358         censusStats.newClientCallTracer(
    359             tagger.empty(), method.getFullMethodName(), recordStarts, recordFinishes);
    360     Metadata headers = new Metadata();
    361     ClientStreamTracer tracer = callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers);
    362 
    363     if (recordStarts) {
    364       StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord();
    365       assertNotNull(record);
    366       assertNoServerContent(record);
    367       assertEquals(1, record.tags.size());
    368       TagValue methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD);
    369       assertEquals(method.getFullMethodName(), methodTag.asString());
    370       assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_STARTED_COUNT));
    371     } else {
    372       assertNull(statsRecorder.pollRecord());
    373     }
    374 
    375     fakeClock.forwardTime(30, MILLISECONDS);
    376     tracer.outboundHeaders();
    377 
    378     fakeClock.forwardTime(100, MILLISECONDS);
    379     tracer.outboundMessage(0);
    380     tracer.outboundWireSize(1028);
    381     tracer.outboundUncompressedSize(1128);
    382 
    383     fakeClock.forwardTime(16, MILLISECONDS);
    384     tracer.inboundMessage(0);
    385     tracer.inboundWireSize(33);
    386     tracer.inboundUncompressedSize(67);
    387     tracer.outboundMessage(1);
    388     tracer.outboundWireSize(99);
    389     tracer.outboundUncompressedSize(865);
    390 
    391     fakeClock.forwardTime(24, MILLISECONDS);
    392     tracer.inboundMessage(1);
    393     tracer.inboundWireSize(154);
    394     tracer.inboundUncompressedSize(552);
    395     tracer.streamClosed(Status.OK);
    396     callTracer.callEnded(Status.OK);
    397 
    398     if (recordFinishes) {
    399       StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord();
    400       assertNotNull(record);
    401       assertNoServerContent(record);
    402       TagValue methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD);
    403       assertEquals(method.getFullMethodName(), methodTag.asString());
    404       TagValue statusTag = record.tags.get(RpcMeasureConstants.RPC_STATUS);
    405       assertEquals(Status.Code.OK.toString(), statusTag.asString());
    406       assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_FINISHED_COUNT));
    407       assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_ERROR_COUNT));
    408       assertEquals(2, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_REQUEST_COUNT));
    409       assertEquals(
    410           1028 + 99, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_REQUEST_BYTES));
    411       assertEquals(
    412           1128 + 865,
    413           record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_UNCOMPRESSED_REQUEST_BYTES));
    414       assertEquals(2, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_RESPONSE_COUNT));
    415       assertEquals(
    416           33 + 154, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_RESPONSE_BYTES));
    417       assertEquals(67 + 552,
    418           record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_UNCOMPRESSED_RESPONSE_BYTES));
    419       assertEquals(30 + 100 + 16 + 24,
    420           record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_ROUNDTRIP_LATENCY));
    421     } else {
    422       assertNull(statsRecorder.pollRecord());
    423     }
    424   }
    425 
    426   @Test
    427   public void clientBasicTracingDefaultSpan() {
    428     CensusTracingModule.ClientCallTracer callTracer =
    429         censusTracing.newClientCallTracer(null, method);
    430     Metadata headers = new Metadata();
    431     ClientStreamTracer clientStreamTracer =
    432         callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers);
    433     verify(tracer).spanBuilderWithExplicitParent(
    434         eq("Sent.package1.service2.method3"), isNull(Span.class));
    435     verify(spyClientSpan, never()).end(any(EndSpanOptions.class));
    436 
    437     clientStreamTracer.outboundMessage(0);
    438     clientStreamTracer.outboundMessageSent(0, 882, -1);
    439     clientStreamTracer.inboundMessage(0);
    440     clientStreamTracer.outboundMessage(1);
    441     clientStreamTracer.outboundMessageSent(1, -1, 27);
    442     clientStreamTracer.inboundMessageRead(0, 255, 90);
    443 
    444     clientStreamTracer.streamClosed(Status.OK);
    445     callTracer.callEnded(Status.OK);
    446 
    447     InOrder inOrder = inOrder(spyClientSpan);
    448     inOrder.verify(spyClientSpan, times(3)).addMessageEvent(messageEventCaptor.capture());
    449     List<MessageEvent> events = messageEventCaptor.getAllValues();
    450     assertEquals(
    451         MessageEvent.builder(Type.SENT, 0).setCompressedMessageSize(882).build(), events.get(0));
    452     assertEquals(
    453         MessageEvent.builder(Type.SENT, 1).setUncompressedMessageSize(27).build(), events.get(1));
    454     assertEquals(
    455         MessageEvent.builder(Type.RECEIVED, 0)
    456             .setCompressedMessageSize(255)
    457             .setUncompressedMessageSize(90)
    458             .build(),
    459         events.get(2));
    460     inOrder.verify(spyClientSpan).end(
    461         EndSpanOptions.builder()
    462             .setStatus(io.opencensus.trace.Status.OK)
    463             .setSampleToLocalSpanStore(false)
    464             .build());
    465     verifyNoMoreInteractions(spyClientSpan);
    466     verifyNoMoreInteractions(tracer);
    467   }
    468 
    469   @Test
    470   public void clientTracingSampledToLocalSpanStore() {
    471     CensusTracingModule.ClientCallTracer callTracer =
    472         censusTracing.newClientCallTracer(null, sampledMethod);
    473     callTracer.callEnded(Status.OK);
    474 
    475     verify(spyClientSpan).end(
    476         EndSpanOptions.builder()
    477             .setStatus(io.opencensus.trace.Status.OK)
    478             .setSampleToLocalSpanStore(true)
    479             .build());
    480   }
    481 
    482   @Test
    483   public void clientStreamNeverCreatedStillRecordStats() {
    484     CensusStatsModule.ClientCallTracer callTracer =
    485         censusStats.newClientCallTracer(
    486             tagger.empty(), method.getFullMethodName(), true, true);
    487 
    488     fakeClock.forwardTime(3000, MILLISECONDS);
    489     callTracer.callEnded(Status.DEADLINE_EXCEEDED.withDescription("3 seconds"));
    490 
    491     // Upstart record
    492     StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord();
    493     assertNotNull(record);
    494     assertNoServerContent(record);
    495     assertEquals(1, record.tags.size());
    496     TagValue methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD);
    497     assertEquals(method.getFullMethodName(), methodTag.asString());
    498     assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_STARTED_COUNT));
    499 
    500     // Completion record
    501     record = statsRecorder.pollRecord();
    502     assertNotNull(record);
    503     assertNoServerContent(record);
    504     methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD);
    505     assertEquals(method.getFullMethodName(), methodTag.asString());
    506     TagValue statusTag = record.tags.get(RpcMeasureConstants.RPC_STATUS);
    507     assertEquals(Status.Code.DEADLINE_EXCEEDED.toString(), statusTag.asString());
    508     assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_FINISHED_COUNT));
    509     assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_ERROR_COUNT));
    510     assertEquals(0, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_REQUEST_COUNT));
    511     assertEquals(0, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_REQUEST_BYTES));
    512     assertEquals(0,
    513         record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_UNCOMPRESSED_REQUEST_BYTES));
    514     assertEquals(0, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_RESPONSE_COUNT));
    515     assertEquals(0, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_RESPONSE_BYTES));
    516     assertEquals(0,
    517         record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_UNCOMPRESSED_RESPONSE_BYTES));
    518     assertEquals(
    519         3000, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_ROUNDTRIP_LATENCY));
    520     assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_SERVER_ELAPSED_TIME));
    521   }
    522 
    523   @Test
    524   public void clientStreamNeverCreatedStillRecordTracing() {
    525     CensusTracingModule.ClientCallTracer callTracer =
    526         censusTracing.newClientCallTracer(fakeClientParentSpan, method);
    527     verify(tracer).spanBuilderWithExplicitParent(
    528         eq("Sent.package1.service2.method3"), same(fakeClientParentSpan));
    529     verify(spyClientSpanBuilder).setRecordEvents(eq(true));
    530 
    531     callTracer.callEnded(Status.DEADLINE_EXCEEDED.withDescription("3 seconds"));
    532     verify(spyClientSpan).end(
    533         EndSpanOptions.builder()
    534             .setStatus(
    535                 io.opencensus.trace.Status.DEADLINE_EXCEEDED
    536                     .withDescription("3 seconds"))
    537             .setSampleToLocalSpanStore(false)
    538             .build());
    539     verifyNoMoreInteractions(spyClientSpan);
    540   }
    541 
    542   @Test
    543   public void statsHeadersPropagateTags_record() {
    544     subtestStatsHeadersPropagateTags(true, true);
    545   }
    546 
    547   @Test
    548   public void statsHeadersPropagateTags_notRecord() {
    549     subtestStatsHeadersPropagateTags(true, false);
    550   }
    551 
    552   @Test
    553   public void statsHeadersNotPropagateTags_record() {
    554     subtestStatsHeadersPropagateTags(false, true);
    555   }
    556 
    557   @Test
    558   public void statsHeadersNotPropagateTags_notRecord() {
    559     subtestStatsHeadersPropagateTags(false, false);
    560   }
    561 
    562   private void subtestStatsHeadersPropagateTags(boolean propagate, boolean recordStats) {
    563     // EXTRA_TAG is propagated by the FakeStatsContextFactory. Note that not all tags are
    564     // propagated.  The StatsContextFactory decides which tags are to propagated.  gRPC facilitates
    565     // the propagation by putting them in the headers.
    566     TagContext clientCtx = tagger.emptyBuilder().put(
    567         StatsTestUtils.EXTRA_TAG, TagValue.create("extra-tag-value-897")).build();
    568     CensusStatsModule census =
    569         new CensusStatsModule(
    570             tagger,
    571             tagCtxSerializer,
    572             statsRecorder,
    573             fakeClock.getStopwatchSupplier(),
    574             propagate);
    575     Metadata headers = new Metadata();
    576     CensusStatsModule.ClientCallTracer callTracer =
    577         census.newClientCallTracer(clientCtx, method.getFullMethodName(), recordStats, recordStats);
    578     // This propagates clientCtx to headers if propagates==true
    579     callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers);
    580     if (recordStats) {
    581       // Client upstart record
    582       StatsTestUtils.MetricsRecord clientRecord = statsRecorder.pollRecord();
    583       assertNotNull(clientRecord);
    584       assertNoServerContent(clientRecord);
    585       assertEquals(2, clientRecord.tags.size());
    586       TagValue clientMethodTag = clientRecord.tags.get(RpcMeasureConstants.RPC_METHOD);
    587       assertEquals(method.getFullMethodName(), clientMethodTag.asString());
    588       TagValue clientPropagatedTag = clientRecord.tags.get(StatsTestUtils.EXTRA_TAG);
    589       assertEquals("extra-tag-value-897", clientPropagatedTag.asString());
    590     }
    591 
    592     if (propagate) {
    593       assertTrue(headers.containsKey(census.statsHeader));
    594     } else {
    595       assertFalse(headers.containsKey(census.statsHeader));
    596       return;
    597     }
    598 
    599     ServerStreamTracer serverTracer =
    600         census.getServerTracerFactory(recordStats, recordStats).newServerStreamTracer(
    601             method.getFullMethodName(), headers);
    602     // Server tracer deserializes clientCtx from the headers, so that it records stats with the
    603     // propagated tags.
    604     Context serverContext = serverTracer.filterContext(Context.ROOT);
    605     // It also put clientCtx in the Context seen by the call handler
    606     assertEquals(
    607         tagger.toBuilder(clientCtx).put(
    608             RpcMeasureConstants.RPC_METHOD,
    609             TagValue.create(method.getFullMethodName())).build(),
    610         TAG_CONTEXT_KEY.get(serverContext));
    611 
    612     // Verifies that the server tracer records the status with the propagated tag
    613     serverTracer.streamClosed(Status.OK);
    614 
    615     if (recordStats) {
    616       // Server upstart record
    617       StatsTestUtils.MetricsRecord serverRecord = statsRecorder.pollRecord();
    618       assertNotNull(serverRecord);
    619       assertNoClientContent(serverRecord);
    620       assertEquals(2, serverRecord.tags.size());
    621       TagValue serverMethodTag = serverRecord.tags.get(RpcMeasureConstants.RPC_METHOD);
    622       assertEquals(method.getFullMethodName(), serverMethodTag.asString());
    623       TagValue serverPropagatedTag = serverRecord.tags.get(StatsTestUtils.EXTRA_TAG);
    624       assertEquals("extra-tag-value-897", serverPropagatedTag.asString());
    625 
    626       // Server completion record
    627       serverRecord = statsRecorder.pollRecord();
    628       assertNotNull(serverRecord);
    629       assertNoClientContent(serverRecord);
    630       serverMethodTag = serverRecord.tags.get(RpcMeasureConstants.RPC_METHOD);
    631       assertEquals(method.getFullMethodName(), serverMethodTag.asString());
    632       TagValue serverStatusTag = serverRecord.tags.get(RpcMeasureConstants.RPC_STATUS);
    633       assertEquals(Status.Code.OK.toString(), serverStatusTag.asString());
    634       assertNull(serverRecord.getMetric(RpcMeasureConstants.RPC_SERVER_ERROR_COUNT));
    635       serverPropagatedTag = serverRecord.tags.get(StatsTestUtils.EXTRA_TAG);
    636       assertEquals("extra-tag-value-897", serverPropagatedTag.asString());
    637     }
    638 
    639     // Verifies that the client tracer factory uses clientCtx, which includes the custom tags, to
    640     // record stats.
    641     callTracer.callEnded(Status.OK);
    642 
    643     if (recordStats) {
    644       // Client completion record
    645       StatsTestUtils.MetricsRecord clientRecord = statsRecorder.pollRecord();
    646       assertNotNull(clientRecord);
    647       assertNoServerContent(clientRecord);
    648       TagValue clientMethodTag = clientRecord.tags.get(RpcMeasureConstants.RPC_METHOD);
    649       assertEquals(method.getFullMethodName(), clientMethodTag.asString());
    650       TagValue clientStatusTag = clientRecord.tags.get(RpcMeasureConstants.RPC_STATUS);
    651       assertEquals(Status.Code.OK.toString(), clientStatusTag.asString());
    652       assertNull(clientRecord.getMetric(RpcMeasureConstants.RPC_CLIENT_ERROR_COUNT));
    653       TagValue clientPropagatedTag = clientRecord.tags.get(StatsTestUtils.EXTRA_TAG);
    654       assertEquals("extra-tag-value-897", clientPropagatedTag.asString());
    655     }
    656 
    657     if (!recordStats) {
    658       assertNull(statsRecorder.pollRecord());
    659     }
    660   }
    661 
    662   @Test
    663   public void statsHeadersNotPropagateDefaultContext() {
    664     CensusStatsModule.ClientCallTracer callTracer =
    665         censusStats.newClientCallTracer(tagger.empty(), method.getFullMethodName(), false, false);
    666     Metadata headers = new Metadata();
    667     callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers);
    668     assertFalse(headers.containsKey(censusStats.statsHeader));
    669   }
    670 
    671   @Test
    672   public void statsHeaderMalformed() {
    673     // Construct a malformed header and make sure parsing it will throw
    674     byte[] statsHeaderValue = new byte[]{1};
    675     Metadata.Key<byte[]> arbitraryStatsHeader =
    676         Metadata.Key.of("grpc-tags-bin", Metadata.BINARY_BYTE_MARSHALLER);
    677     try {
    678       tagCtxSerializer.fromByteArray(statsHeaderValue);
    679       fail("Should have thrown");
    680     } catch (Exception e) {
    681       // Expected
    682     }
    683 
    684     // But the header key will return a default context for it
    685     Metadata headers = new Metadata();
    686     assertNull(headers.get(censusStats.statsHeader));
    687     headers.put(arbitraryStatsHeader, statsHeaderValue);
    688     assertSame(tagger.empty(), headers.get(censusStats.statsHeader));
    689   }
    690 
    691   @Test
    692   public void traceHeadersPropagateSpanContext() throws Exception {
    693     CensusTracingModule.ClientCallTracer callTracer =
    694         censusTracing.newClientCallTracer(fakeClientParentSpan, method);
    695     Metadata headers = new Metadata();
    696     callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers);
    697 
    698     verify(mockTracingPropagationHandler).toByteArray(same(fakeClientSpanContext));
    699     verifyNoMoreInteractions(mockTracingPropagationHandler);
    700     verify(tracer).spanBuilderWithExplicitParent(
    701         eq("Sent.package1.service2.method3"), same(fakeClientParentSpan));
    702     verify(spyClientSpanBuilder).setRecordEvents(eq(true));
    703     verifyNoMoreInteractions(tracer);
    704     assertTrue(headers.containsKey(censusTracing.tracingHeader));
    705 
    706     ServerStreamTracer serverTracer =
    707         censusTracing.getServerTracerFactory().newServerStreamTracer(
    708             method.getFullMethodName(), headers);
    709     verify(mockTracingPropagationHandler).fromByteArray(same(binarySpanContext));
    710     verify(tracer).spanBuilderWithRemoteParent(
    711         eq("Recv.package1.service2.method3"), same(spyClientSpan.getContext()));
    712     verify(spyServerSpanBuilder).setRecordEvents(eq(true));
    713 
    714     Context filteredContext = serverTracer.filterContext(Context.ROOT);
    715     assertSame(spyServerSpan, ContextUtils.CONTEXT_SPAN_KEY.get(filteredContext));
    716   }
    717 
    718   @Test
    719   public void traceHeaders_propagateSpanContext() throws Exception {
    720     CensusTracingModule.ClientCallTracer callTracer =
    721         censusTracing.newClientCallTracer(fakeClientParentSpan, method);
    722     Metadata headers = new Metadata();
    723 
    724     callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers);
    725 
    726     assertThat(headers.keys()).isNotEmpty();
    727   }
    728 
    729   @Test
    730   public void traceHeaders_missingCensusImpl_notPropagateSpanContext()
    731       throws Exception {
    732     reset(spyClientSpanBuilder);
    733     when(spyClientSpanBuilder.startSpan()).thenReturn(BlankSpan.INSTANCE);
    734     Metadata headers = new Metadata();
    735 
    736     CensusTracingModule.ClientCallTracer callTracer =
    737         censusTracing.newClientCallTracer(BlankSpan.INSTANCE, method);
    738     callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers);
    739 
    740     assertThat(headers.keys()).isEmpty();
    741   }
    742 
    743   @Test
    744   public void traceHeaders_clientMissingCensusImpl_preservingHeaders() throws Exception {
    745     reset(spyClientSpanBuilder);
    746     when(spyClientSpanBuilder.startSpan()).thenReturn(BlankSpan.INSTANCE);
    747     Metadata headers = new Metadata();
    748     headers.put(
    749         Metadata.Key.of("never-used-key-bin", Metadata.BINARY_BYTE_MARSHALLER),
    750         new byte[] {});
    751     Set<String> originalHeaderKeys = new HashSet<String>(headers.keys());
    752 
    753     CensusTracingModule.ClientCallTracer callTracer =
    754         censusTracing.newClientCallTracer(BlankSpan.INSTANCE, method);
    755     callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers);
    756 
    757     assertThat(headers.keys()).containsExactlyElementsIn(originalHeaderKeys);
    758   }
    759 
    760   @Test
    761   public void traceHeaderMalformed() throws Exception {
    762     // As comparison, normal header parsing
    763     Metadata headers = new Metadata();
    764     headers.put(censusTracing.tracingHeader, fakeClientSpanContext);
    765     // mockTracingPropagationHandler was stubbed to always return fakeServerParentSpanContext
    766     assertSame(spyClientSpan.getContext(), headers.get(censusTracing.tracingHeader));
    767 
    768     // Make BinaryPropagationHandler always throw when parsing the header
    769     when(mockTracingPropagationHandler.fromByteArray(any(byte[].class)))
    770         .thenThrow(new SpanContextParseException("Malformed header"));
    771 
    772     headers = new Metadata();
    773     assertNull(headers.get(censusTracing.tracingHeader));
    774     headers.put(censusTracing.tracingHeader, fakeClientSpanContext);
    775     assertSame(SpanContext.INVALID, headers.get(censusTracing.tracingHeader));
    776     assertNotSame(spyClientSpan.getContext(), SpanContext.INVALID);
    777 
    778     // A null Span is used as the parent in this case
    779     censusTracing.getServerTracerFactory().newServerStreamTracer(
    780         method.getFullMethodName(), headers);
    781     verify(tracer).spanBuilderWithRemoteParent(
    782         eq("Recv.package1.service2.method3"), isNull(SpanContext.class));
    783     verify(spyServerSpanBuilder).setRecordEvents(eq(true));
    784   }
    785 
    786   @Test
    787   public void serverBasicStatsNoHeaders_startsAndFinishes() {
    788     subtestServerBasicStatsNoHeaders(true, true);
    789   }
    790 
    791   @Test
    792   public void serverBasicStatsNoHeaders_startsOnly() {
    793     subtestServerBasicStatsNoHeaders(true, false);
    794   }
    795 
    796   @Test
    797   public void serverBasicStatsNoHeaders_finishesOnly() {
    798     subtestServerBasicStatsNoHeaders(false, true);
    799   }
    800 
    801   @Test
    802   public void serverBasicStatsNoHeaders_neither() {
    803     subtestServerBasicStatsNoHeaders(false, false);
    804   }
    805 
    806   private void subtestServerBasicStatsNoHeaders(boolean recordStarts, boolean recordFinishes) {
    807     ServerStreamTracer.Factory tracerFactory =
    808         censusStats.getServerTracerFactory(recordStarts, recordFinishes);
    809     ServerStreamTracer tracer =
    810         tracerFactory.newServerStreamTracer(method.getFullMethodName(), new Metadata());
    811 
    812     if (recordStarts) {
    813       StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord();
    814       assertNotNull(record);
    815       assertNoClientContent(record);
    816       assertEquals(1, record.tags.size());
    817       TagValue methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD);
    818       assertEquals(method.getFullMethodName(), methodTag.asString());
    819       assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_STARTED_COUNT));
    820     } else {
    821       assertNull(statsRecorder.pollRecord());
    822     }
    823 
    824     Context filteredContext = tracer.filterContext(Context.ROOT);
    825     TagContext statsCtx = TAG_CONTEXT_KEY.get(filteredContext);
    826     assertEquals(
    827         tagger
    828             .emptyBuilder()
    829             .put(
    830                 RpcMeasureConstants.RPC_METHOD,
    831                 TagValue.create(method.getFullMethodName()))
    832             .build(),
    833         statsCtx);
    834 
    835     tracer.inboundMessage(0);
    836     tracer.inboundWireSize(34);
    837     tracer.inboundUncompressedSize(67);
    838 
    839     fakeClock.forwardTime(100, MILLISECONDS);
    840     tracer.outboundMessage(0);
    841     tracer.outboundWireSize(1028);
    842     tracer.outboundUncompressedSize(1128);
    843 
    844     fakeClock.forwardTime(16, MILLISECONDS);
    845     tracer.inboundMessage(1);
    846     tracer.inboundWireSize(154);
    847     tracer.inboundUncompressedSize(552);
    848     tracer.outboundMessage(1);
    849     tracer.outboundWireSize(99);
    850     tracer.outboundUncompressedSize(865);
    851 
    852     fakeClock.forwardTime(24, MILLISECONDS);
    853 
    854     tracer.streamClosed(Status.CANCELLED);
    855 
    856     if (recordFinishes) {
    857       StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord();
    858       assertNotNull(record);
    859       assertNoClientContent(record);
    860       TagValue methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD);
    861       assertEquals(method.getFullMethodName(), methodTag.asString());
    862       TagValue statusTag = record.tags.get(RpcMeasureConstants.RPC_STATUS);
    863       assertEquals(Status.Code.CANCELLED.toString(), statusTag.asString());
    864       assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_FINISHED_COUNT));
    865       assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_ERROR_COUNT));
    866       assertEquals(2, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_RESPONSE_COUNT));
    867       assertEquals(
    868           1028 + 99, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_RESPONSE_BYTES));
    869       assertEquals(
    870           1128 + 865,
    871           record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_UNCOMPRESSED_RESPONSE_BYTES));
    872       assertEquals(2, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_REQUEST_COUNT));
    873       assertEquals(
    874           34 + 154, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_REQUEST_BYTES));
    875       assertEquals(67 + 552,
    876           record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_UNCOMPRESSED_REQUEST_BYTES));
    877       assertEquals(100 + 16 + 24,
    878           record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_SERVER_LATENCY));
    879     } else {
    880       assertNull(statsRecorder.pollRecord());
    881     }
    882   }
    883 
    884   @Test
    885   public void serverBasicTracingNoHeaders() {
    886     ServerStreamTracer.Factory tracerFactory = censusTracing.getServerTracerFactory();
    887     ServerStreamTracer serverStreamTracer =
    888         tracerFactory.newServerStreamTracer(method.getFullMethodName(), new Metadata());
    889     verifyZeroInteractions(mockTracingPropagationHandler);
    890     verify(tracer).spanBuilderWithRemoteParent(
    891         eq("Recv.package1.service2.method3"), isNull(SpanContext.class));
    892     verify(spyServerSpanBuilder).setRecordEvents(eq(true));
    893 
    894     Context filteredContext = serverStreamTracer.filterContext(Context.ROOT);
    895     assertSame(spyServerSpan, ContextUtils.CONTEXT_SPAN_KEY.get(filteredContext));
    896 
    897     serverStreamTracer.serverCallStarted(
    898         new ServerCallInfoImpl<String, String>(method, Attributes.EMPTY, null));
    899 
    900     verify(spyServerSpan, never()).end(any(EndSpanOptions.class));
    901 
    902     serverStreamTracer.outboundMessage(0);
    903     serverStreamTracer.outboundMessageSent(0, 882, -1);
    904     serverStreamTracer.inboundMessage(0);
    905     serverStreamTracer.outboundMessage(1);
    906     serverStreamTracer.outboundMessageSent(1, -1, 27);
    907     serverStreamTracer.inboundMessageRead(0, 255, 90);
    908 
    909     serverStreamTracer.streamClosed(Status.CANCELLED);
    910 
    911     InOrder inOrder = inOrder(spyServerSpan);
    912     inOrder.verify(spyServerSpan, times(3)).addMessageEvent(messageEventCaptor.capture());
    913     List<MessageEvent> events = messageEventCaptor.getAllValues();
    914     assertEquals(
    915         MessageEvent.builder(Type.SENT, 0).setCompressedMessageSize(882).build(), events.get(0));
    916     assertEquals(
    917         MessageEvent.builder(Type.SENT, 1).setUncompressedMessageSize(27).build(), events.get(1));
    918     assertEquals(
    919         MessageEvent.builder(Type.RECEIVED, 0)
    920             .setCompressedMessageSize(255)
    921             .setUncompressedMessageSize(90)
    922             .build(),
    923         events.get(2));
    924     inOrder.verify(spyServerSpan).end(
    925         EndSpanOptions.builder()
    926             .setStatus(io.opencensus.trace.Status.CANCELLED)
    927             .setSampleToLocalSpanStore(false)
    928             .build());
    929     verifyNoMoreInteractions(spyServerSpan);
    930   }
    931 
    932   @Test
    933   public void serverTracingSampledToLocalSpanStore() {
    934     ServerStreamTracer.Factory tracerFactory = censusTracing.getServerTracerFactory();
    935     ServerStreamTracer serverStreamTracer =
    936         tracerFactory.newServerStreamTracer(sampledMethod.getFullMethodName(), new Metadata());
    937 
    938     serverStreamTracer.filterContext(Context.ROOT);
    939 
    940     serverStreamTracer.serverCallStarted(
    941         new ServerCallInfoImpl<String, String>(sampledMethod, Attributes.EMPTY, null));
    942 
    943     serverStreamTracer.streamClosed(Status.CANCELLED);
    944 
    945     verify(spyServerSpan).end(
    946         EndSpanOptions.builder()
    947             .setStatus(io.opencensus.trace.Status.CANCELLED)
    948             .setSampleToLocalSpanStore(true)
    949             .build());
    950   }
    951 
    952   @Test
    953   public void serverTracingNotSampledToLocalSpanStore_whenServerCallNotCreated() {
    954     ServerStreamTracer.Factory tracerFactory = censusTracing.getServerTracerFactory();
    955     ServerStreamTracer serverStreamTracer =
    956         tracerFactory.newServerStreamTracer(sampledMethod.getFullMethodName(), new Metadata());
    957 
    958     serverStreamTracer.streamClosed(Status.CANCELLED);
    959 
    960     verify(spyServerSpan).end(
    961         EndSpanOptions.builder()
    962             .setStatus(io.opencensus.trace.Status.CANCELLED)
    963             .setSampleToLocalSpanStore(false)
    964             .build());
    965   }
    966 
    967   @Test
    968   public void convertToTracingStatus() {
    969     // Without description
    970     for (Status.Code grpcCode : Status.Code.values()) {
    971       Status grpcStatus = Status.fromCode(grpcCode);
    972       io.opencensus.trace.Status tracingStatus =
    973           CensusTracingModule.convertStatus(grpcStatus);
    974       assertEquals(grpcCode.toString(), tracingStatus.getCanonicalCode().toString());
    975       assertNull(tracingStatus.getDescription());
    976     }
    977 
    978     // With description
    979     for (Status.Code grpcCode : Status.Code.values()) {
    980       Status grpcStatus = Status.fromCode(grpcCode).withDescription("This is my description");
    981       io.opencensus.trace.Status tracingStatus =
    982           CensusTracingModule.convertStatus(grpcStatus);
    983       assertEquals(grpcCode.toString(), tracingStatus.getCanonicalCode().toString());
    984       assertEquals(grpcStatus.getDescription(), tracingStatus.getDescription());
    985     }
    986   }
    987 
    988 
    989   @Test
    990   public void generateTraceSpanName() {
    991     assertEquals(
    992         "Sent.io.grpc.Foo", CensusTracingModule.generateTraceSpanName(false, "io.grpc/Foo"));
    993     assertEquals(
    994         "Recv.io.grpc.Bar", CensusTracingModule.generateTraceSpanName(true, "io.grpc/Bar"));
    995   }
    996 
    997   private static void assertNoServerContent(StatsTestUtils.MetricsRecord record) {
    998     assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_ERROR_COUNT));
    999     assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_REQUEST_COUNT));
   1000     assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_RESPONSE_COUNT));
   1001     assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_REQUEST_BYTES));
   1002     assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_RESPONSE_BYTES));
   1003     assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_SERVER_ELAPSED_TIME));
   1004     assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_SERVER_LATENCY));
   1005     assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_UNCOMPRESSED_REQUEST_BYTES));
   1006     assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_UNCOMPRESSED_RESPONSE_BYTES));
   1007   }
   1008 
   1009   private static void assertNoClientContent(StatsTestUtils.MetricsRecord record) {
   1010     assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_ERROR_COUNT));
   1011     assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_REQUEST_COUNT));
   1012     assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_RESPONSE_COUNT));
   1013     assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_REQUEST_BYTES));
   1014     assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_RESPONSE_BYTES));
   1015     assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_ROUNDTRIP_LATENCY));
   1016     assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_SERVER_ELAPSED_TIME));
   1017     assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_UNCOMPRESSED_REQUEST_BYTES));
   1018     assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_UNCOMPRESSED_RESPONSE_BYTES));
   1019   }
   1020 }
   1021