2525import io .opentelemetry .exporter .otlp .http .metrics .OtlpHttpMetricExporterBuilder ;
2626import io .opentelemetry .exporter .otlp .http .trace .OtlpHttpSpanExporter ;
2727import io .opentelemetry .exporter .otlp .http .trace .OtlpHttpSpanExporterBuilder ;
28+ import io .opentelemetry .exporter .otlp .metrics .OtlpGrpcMetricExporter ;
29+ import io .opentelemetry .exporter .otlp .metrics .OtlpGrpcMetricExporterBuilder ;
2830import io .opentelemetry .exporter .otlp .trace .OtlpGrpcSpanExporter ;
2931import io .opentelemetry .exporter .otlp .trace .OtlpGrpcSpanExporterBuilder ;
3032import io .opentelemetry .sdk .OpenTelemetrySdk ;
@@ -119,6 +121,7 @@ public void setup() {
119121 MockitoAnnotations .openMocks (this );
120122 }
121123
124+ // TODO: Use parameterized test for testing traces customizer for http & grpc.
122125 @ Test
123126 public void testTraceCustomizerOtlpHttp () {
124127 // Set resource project system property
@@ -179,6 +182,58 @@ public void testTraceCustomizerOtlpHttp() {
179182 }
180183 }
181184
185+ @ Test
186+ public void testTraceCustomizerOtlpGrpc () {
187+ // Set resource project system property
188+ System .setProperty (
189+ ConfigurableOption .GOOGLE_CLOUD_PROJECT .getSystemProperty (), DUMMY_GCP_RESOURCE_PROJECT_ID );
190+ // Prepare mocks
191+ prepareMockBehaviorForGoogleCredentials ();
192+ OtlpGrpcSpanExporter mockOtlpGrpcSpanExporter = Mockito .mock (OtlpGrpcSpanExporter .class );
193+ OtlpGrpcSpanExporterBuilder spyOtlpGrpcSpanExporterBuilder =
194+ Mockito .spy (OtlpGrpcSpanExporter .builder ());
195+ List <SpanData > exportedSpans = new ArrayList <>();
196+ configureGrpcMockSpanExporter (
197+ mockOtlpGrpcSpanExporter , spyOtlpGrpcSpanExporterBuilder , exportedSpans );
198+
199+ // begin assertions
200+ try (MockedStatic <GoogleCredentials > googleCredentialsMockedStatic =
201+ Mockito .mockStatic (GoogleCredentials .class )) {
202+ googleCredentialsMockedStatic
203+ .when (GoogleCredentials ::getApplicationDefault )
204+ .thenReturn (mockedGoogleCredentials );
205+
206+ OpenTelemetrySdk sdk = buildOpenTelemetrySdkWithExporter (mockOtlpGrpcSpanExporter );
207+ generateTestSpan (sdk );
208+ CompletableResultCode code = sdk .shutdown ();
209+ CompletableResultCode joinResult = code .join (10 , TimeUnit .SECONDS );
210+ assertTrue (joinResult .isSuccess ());
211+
212+ Mockito .verify (mockOtlpGrpcSpanExporter , Mockito .times (1 )).toBuilder ();
213+ Mockito .verify (spyOtlpGrpcSpanExporterBuilder , Mockito .times (1 ))
214+ .setHeaders (traceHeaderSupplierCaptor .capture ());
215+ assertEquals (2 , traceHeaderSupplierCaptor .getValue ().get ().size ());
216+ assertThat (authHeadersQuotaProjectIsPresent (traceHeaderSupplierCaptor .getValue ().get ()))
217+ .isTrue ();
218+
219+ Mockito .verify (mockOtlpGrpcSpanExporter , Mockito .atLeast (1 )).export (Mockito .anyCollection ());
220+
221+ assertThat (exportedSpans )
222+ .hasSizeGreaterThan (0 )
223+ .allSatisfy (
224+ spanData -> {
225+ assertThat (spanData .getResource ().getAttributes ().asMap ())
226+ .containsEntry (
227+ AttributeKey .stringKey (GCP_USER_PROJECT_ID_KEY ),
228+ DUMMY_GCP_RESOURCE_PROJECT_ID )
229+ .containsEntry (AttributeKey .stringKey ("foo" ), "bar" );
230+ assertThat (spanData .getAttributes ().asMap ())
231+ .containsKey (AttributeKey .longKey ("work_loop" ));
232+ });
233+ }
234+ }
235+
236+ // TODO: Use parameterized test for testing metrics customizer for http & grpc.
182237 @ Test
183238 public void testMetricCustomizerOtlpHttp () {
184239 // Set resource project system property
@@ -238,18 +293,19 @@ public void testMetricCustomizerOtlpHttp() {
238293 }
239294
240295 @ Test
241- public void testTraceCustomizerOtlpGrpc () {
296+ public void testMetricCustomizerOtlpGrpc () {
242297 // Set resource project system property
243298 System .setProperty (
244299 ConfigurableOption .GOOGLE_CLOUD_PROJECT .getSystemProperty (), DUMMY_GCP_RESOURCE_PROJECT_ID );
245300 // Prepare mocks
246301 prepareMockBehaviorForGoogleCredentials ();
247- OtlpGrpcSpanExporter mockOtlpGrpcSpanExporter = Mockito .mock (OtlpGrpcSpanExporter .class );
248- OtlpGrpcSpanExporterBuilder spyOtlpGrpcSpanExporterBuilder =
249- Mockito .spy (OtlpGrpcSpanExporter .builder ());
250- List <SpanData > exportedSpans = new ArrayList <>();
251- configureGrpcMockSpanExporter (
252- mockOtlpGrpcSpanExporter , spyOtlpGrpcSpanExporterBuilder , exportedSpans );
302+ OtlpGrpcMetricExporter mockOtlpGrpcMetricExporter = Mockito .mock (OtlpGrpcMetricExporter .class );
303+ OtlpGrpcMetricExporterBuilder otlpMetricExporterBuilder = OtlpGrpcMetricExporter .builder ();
304+ OtlpGrpcMetricExporterBuilder spyOtlpGrpcMetricExporterBuilder =
305+ Mockito .spy (otlpMetricExporterBuilder );
306+ List <MetricData > exportedMetrics = new ArrayList <>();
307+ configureGrpcMockMetricExporter (
308+ mockOtlpGrpcMetricExporter , spyOtlpGrpcMetricExporterBuilder , exportedMetrics );
253309
254310 // begin assertions
255311 try (MockedStatic <GoogleCredentials > googleCredentialsMockedStatic =
@@ -258,32 +314,38 @@ public void testTraceCustomizerOtlpGrpc() {
258314 .when (GoogleCredentials ::getApplicationDefault )
259315 .thenReturn (mockedGoogleCredentials );
260316
261- OpenTelemetrySdk sdk = buildOpenTelemetrySdkWithExporter (mockOtlpGrpcSpanExporter );
262- generateTestSpan (sdk );
317+ OpenTelemetrySdk sdk = buildOpenTelemetrySdkWithExporter (mockOtlpGrpcMetricExporter );
318+ generateTestMetric (sdk );
263319 CompletableResultCode code = sdk .shutdown ();
264320 CompletableResultCode joinResult = code .join (10 , TimeUnit .SECONDS );
265321 assertTrue (joinResult .isSuccess ());
266322
267- Mockito .verify (mockOtlpGrpcSpanExporter , Mockito .times (1 )).toBuilder ();
268- Mockito .verify (spyOtlpGrpcSpanExporterBuilder , Mockito .times (1 ))
269- .setHeaders (traceHeaderSupplierCaptor .capture ());
270- assertEquals (2 , traceHeaderSupplierCaptor .getValue ().get ().size ());
271- assertThat (authHeadersQuotaProjectIsPresent (traceHeaderSupplierCaptor .getValue ().get ()))
323+ Mockito .verify (mockOtlpGrpcMetricExporter , Mockito .times (1 )).toBuilder ();
324+ Mockito .verify (spyOtlpGrpcMetricExporterBuilder , Mockito .times (1 ))
325+ .setHeaders (metricHeaderSupplierCaptor .capture ());
326+ assertEquals (2 , metricHeaderSupplierCaptor .getValue ().get ().size ());
327+ assertThat (authHeadersQuotaProjectIsPresent (metricHeaderSupplierCaptor .getValue ().get ()))
272328 .isTrue ();
273329
274- Mockito .verify (mockOtlpGrpcSpanExporter , Mockito .atLeast (1 )).export (Mockito .anyCollection ());
330+ Mockito .verify (mockOtlpGrpcMetricExporter , Mockito .atLeast (1 ))
331+ .export (Mockito .anyCollection ());
275332
276- assertThat (exportedSpans )
333+ assertThat (exportedMetrics )
277334 .hasSizeGreaterThan (0 )
278335 .allSatisfy (
279- spanData -> {
280- assertThat (spanData .getResource ().getAttributes ().asMap ())
336+ metricData -> {
337+ assertThat (metricData .getResource ().getAttributes ().asMap ())
281338 .containsEntry (
282339 AttributeKey .stringKey (GCP_USER_PROJECT_ID_KEY ),
283340 DUMMY_GCP_RESOURCE_PROJECT_ID )
284341 .containsEntry (AttributeKey .stringKey ("foo" ), "bar" );
285- assertThat (spanData .getAttributes ().asMap ())
286- .containsKey (AttributeKey .longKey ("work_loop" ));
342+ assertThat (metricData .getLongSumData ().getPoints ())
343+ .hasSizeGreaterThan (0 )
344+ .allSatisfy (
345+ longPointData -> {
346+ assertThat (longPointData .getAttributes ().asMap ())
347+ .containsKey (AttributeKey .longKey ("work_loop" ));
348+ });
287349 });
288350 }
289351 }
@@ -448,7 +510,6 @@ private static Stream<Arguments> provideQuotaBehaviorTestCases() {
448510 }
449511
450512 // Configure necessary behavior on the gRPC mock span exporters to work.
451- // TODO: Potential improvement - make this work for Http exporter as well.
452513 private static void configureGrpcMockSpanExporter (
453514 OtlpGrpcSpanExporter mockGrpcExporter ,
454515 OtlpGrpcSpanExporterBuilder spyGrpcExporterBuilder ,
@@ -499,6 +560,40 @@ private static void configureHttpMockMetricExporter(
499560 });
500561 }
501562
563+ private static void configureGrpcMockMetricExporter (
564+ OtlpGrpcMetricExporter mockOtlpGrpcMetricExporter ,
565+ OtlpGrpcMetricExporterBuilder spyOtlpGrpcMetricExporterBuilder ,
566+ List <MetricData > exportedMetricContainer ) {
567+ Mockito .when (spyOtlpGrpcMetricExporterBuilder .build ()).thenReturn (mockOtlpGrpcMetricExporter );
568+ Mockito .when (mockOtlpGrpcMetricExporter .shutdown ())
569+ .thenReturn (CompletableResultCode .ofSuccess ());
570+ Mockito .when (mockOtlpGrpcMetricExporter .toBuilder ())
571+ .thenReturn (spyOtlpGrpcMetricExporterBuilder );
572+ Mockito .when (mockOtlpGrpcMetricExporter .export (Mockito .anyCollection ()))
573+ .thenAnswer (
574+ invocationOnMock -> {
575+ exportedMetricContainer .addAll (invocationOnMock .getArgument (0 ));
576+ return CompletableResultCode .ofSuccess ();
577+ });
578+ // mock the get default aggregation and aggregation temporality - they're required for valid
579+ // metric collection.
580+ Mockito .when (mockOtlpGrpcMetricExporter .getDefaultAggregation (Mockito .any ()))
581+ .thenAnswer (
582+ (Answer <Aggregation >)
583+ invocationOnMock -> {
584+ InstrumentType instrumentType = invocationOnMock .getArgument (0 );
585+ return OtlpGrpcMetricExporter .getDefault ().getDefaultAggregation (instrumentType );
586+ });
587+ Mockito .when (mockOtlpGrpcMetricExporter .getAggregationTemporality (Mockito .any ()))
588+ .thenAnswer (
589+ (Answer <AggregationTemporality >)
590+ invocationOnMock -> {
591+ InstrumentType instrumentType = invocationOnMock .getArgument (0 );
592+ return OtlpGrpcMetricExporter .getDefault ()
593+ .getAggregationTemporality (instrumentType );
594+ });
595+ }
596+
502597 @ AutoValue
503598 abstract static class QuotaProjectIdTestBehavior {
504599 // A null user specified quota represents the use case where user omits specifying quota
0 commit comments