@@ -60,6 +60,7 @@ pub use view::*;
60
60
61
61
#[ cfg( all( test, feature = "testing" ) ) ]
62
62
mod tests {
63
+ use self :: data:: ScopeMetrics ;
63
64
use super :: * ;
64
65
use crate :: metrics:: data:: { ResourceMetrics , Temporality } ;
65
66
use crate :: metrics:: reader:: TemporalitySelector ;
@@ -70,6 +71,7 @@ mod tests {
70
71
metrics:: { MeterProvider as _, Unit } ,
71
72
KeyValue ,
72
73
} ;
74
+ use std:: borrow:: Cow ;
73
75
74
76
// "multi_thread" tokio flavor must be used else flush won't
75
77
// be able to make progress!
@@ -214,6 +216,179 @@ mod tests {
214
216
assert_eq ! ( datapoint. value, 15 ) ;
215
217
}
216
218
219
+ // "multi_thread" tokio flavor must be used else flush won't
220
+ // be able to make progress!
221
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
222
+ async fn counter_duplicate_instrument_different_meter_no_merge ( ) {
223
+ // Arrange
224
+ let exporter = InMemoryMetricsExporter :: default ( ) ;
225
+ let reader = PeriodicReader :: builder ( exporter. clone ( ) , runtime:: Tokio ) . build ( ) ;
226
+ let meter_provider = SdkMeterProvider :: builder ( ) . with_reader ( reader) . build ( ) ;
227
+
228
+ // Act
229
+ let meter1 = meter_provider. meter ( "test.meter1" ) ;
230
+ let meter2 = meter_provider. meter ( "test.meter2" ) ;
231
+ let counter1 = meter1
232
+ . u64_counter ( "my_counter" )
233
+ . with_unit ( Unit :: new ( "my_unit" ) )
234
+ . with_description ( "my_description" )
235
+ . init ( ) ;
236
+
237
+ let counter2 = meter2
238
+ . u64_counter ( "my_counter" )
239
+ . with_unit ( Unit :: new ( "my_unit" ) )
240
+ . with_description ( "my_description" )
241
+ . init ( ) ;
242
+
243
+ let attribute = vec ! [ KeyValue :: new( "key1" , "value1" ) ] ;
244
+ counter1. add ( 10 , & attribute) ;
245
+ counter2. add ( 5 , & attribute) ;
246
+
247
+ meter_provider. force_flush ( ) . unwrap ( ) ;
248
+
249
+ // Assert
250
+ let resource_metrics = exporter
251
+ . get_finished_metrics ( )
252
+ . expect ( "metrics are expected to be exported." ) ;
253
+ assert ! (
254
+ resource_metrics[ 0 ] . scope_metrics. len( ) == 2 ,
255
+ "There should be 2 separate scope"
256
+ ) ;
257
+ assert ! (
258
+ resource_metrics[ 0 ] . scope_metrics[ 0 ] . metrics. len( ) == 1 ,
259
+ "There should be single metric for the scope"
260
+ ) ;
261
+ assert ! (
262
+ resource_metrics[ 0 ] . scope_metrics[ 1 ] . metrics. len( ) == 1 ,
263
+ "There should be single metric for the scope"
264
+ ) ;
265
+
266
+ let scope1 = find_scope_metric ( & resource_metrics[ 0 ] . scope_metrics , "test.meter1" ) ;
267
+ let scope2 = find_scope_metric ( & resource_metrics[ 0 ] . scope_metrics , "test.meter2" ) ;
268
+
269
+ if let Some ( scope1) = scope1 {
270
+ let metric1 = & scope1. metrics [ 0 ] ;
271
+ assert_eq ! ( metric1. name, "my_counter" ) ;
272
+ assert_eq ! ( metric1. unit. as_str( ) , "my_unit" ) ;
273
+ assert_eq ! ( metric1. description, "my_description" ) ;
274
+ let sum1 = metric1
275
+ . data
276
+ . as_any ( )
277
+ . downcast_ref :: < data:: Sum < u64 > > ( )
278
+ . expect ( "Sum aggregation expected for Counter instruments by default" ) ;
279
+
280
+ // Expecting 1 time-series.
281
+ assert_eq ! ( sum1. data_points. len( ) , 1 ) ;
282
+
283
+ let datapoint1 = & sum1. data_points [ 0 ] ;
284
+ assert_eq ! ( datapoint1. value, 10 ) ;
285
+ } else {
286
+ panic ! ( "No MetricScope found for 'test.meter1'" ) ;
287
+ }
288
+
289
+ if let Some ( scope2) = scope2 {
290
+ let metric2 = & scope2. metrics [ 0 ] ;
291
+ assert_eq ! ( metric2. name, "my_counter" ) ;
292
+ assert_eq ! ( metric2. unit. as_str( ) , "my_unit" ) ;
293
+ assert_eq ! ( metric2. description, "my_description" ) ;
294
+ let sum2 = metric2
295
+ . data
296
+ . as_any ( )
297
+ . downcast_ref :: < data:: Sum < u64 > > ( )
298
+ . expect ( "Sum aggregation expected for Counter instruments by default" ) ;
299
+
300
+ // Expecting 1 time-series.
301
+ assert_eq ! ( sum2. data_points. len( ) , 1 ) ;
302
+
303
+ let datapoint2 = & sum2. data_points [ 0 ] ;
304
+ assert_eq ! ( datapoint2. value, 5 ) ;
305
+ } else {
306
+ panic ! ( "No MetricScope found for 'test.meter2'" ) ;
307
+ }
308
+ }
309
+
310
+ // "multi_thread" tokio flavor must be used else flush won't
311
+ // be able to make progress!
312
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
313
+ async fn instrumentation_scope_identity_test ( ) {
314
+ // Arrange
315
+ let exporter = InMemoryMetricsExporter :: default ( ) ;
316
+ let reader = PeriodicReader :: builder ( exporter. clone ( ) , runtime:: Tokio ) . build ( ) ;
317
+ let meter_provider = SdkMeterProvider :: builder ( ) . with_reader ( reader) . build ( ) ;
318
+
319
+ // Act
320
+ // Meters are identical except for scope attributes, but scope attributes are not an identifying property.
321
+ // Hence there should be a single metric stream output for this test.
322
+ let meter1 = meter_provider. versioned_meter (
323
+ "test.meter" ,
324
+ Some ( "v0.1.0" ) ,
325
+ Some ( "schema_url" ) ,
326
+ Some ( vec ! [ KeyValue :: new( "key" , "value1" ) ] ) ,
327
+ ) ;
328
+ let meter2 = meter_provider. versioned_meter (
329
+ "test.meter" ,
330
+ Some ( "v0.1.0" ) ,
331
+ Some ( "schema_url" ) ,
332
+ Some ( vec ! [ KeyValue :: new( "key" , "value2" ) ] ) ,
333
+ ) ;
334
+ let counter1 = meter1
335
+ . u64_counter ( "my_counter" )
336
+ . with_unit ( Unit :: new ( "my_unit" ) )
337
+ . with_description ( "my_description" )
338
+ . init ( ) ;
339
+
340
+ let counter2 = meter2
341
+ . u64_counter ( "my_counter" )
342
+ . with_unit ( Unit :: new ( "my_unit" ) )
343
+ . with_description ( "my_description" )
344
+ . init ( ) ;
345
+
346
+ let attribute = vec ! [ KeyValue :: new( "key1" , "value1" ) ] ;
347
+ counter1. add ( 10 , & attribute) ;
348
+ counter2. add ( 5 , & attribute) ;
349
+
350
+ meter_provider. force_flush ( ) . unwrap ( ) ;
351
+
352
+ // Assert
353
+ let resource_metrics = exporter
354
+ . get_finished_metrics ( )
355
+ . expect ( "metrics are expected to be exported." ) ;
356
+ println ! ( "resource_metrics: {:?}" , resource_metrics) ;
357
+ assert ! (
358
+ resource_metrics[ 0 ] . scope_metrics. len( ) == 1 ,
359
+ "There should be a single scope as the meters are identical"
360
+ ) ;
361
+ assert ! (
362
+ resource_metrics[ 0 ] . scope_metrics[ 0 ] . metrics. len( ) == 1 ,
363
+ "There should be single metric for the scope as instruments are identical"
364
+ ) ;
365
+
366
+ let scope = & resource_metrics[ 0 ] . scope_metrics [ 0 ] . scope ;
367
+ assert_eq ! ( scope. name, "test.meter" ) ;
368
+ assert_eq ! ( scope. version, Some ( Cow :: Borrowed ( "v0.1.0" ) ) ) ;
369
+ assert_eq ! ( scope. schema_url, Some ( Cow :: Borrowed ( "schema_url" ) ) ) ;
370
+
371
+ // This is validating current behavior, but it is not guaranteed to be the case in the future,
372
+ // as this is a user error and SDK reserves right to change this behavior.
373
+ assert_eq ! ( scope. attributes, vec![ KeyValue :: new( "key" , "value1" ) ] ) ;
374
+
375
+ let metric = & resource_metrics[ 0 ] . scope_metrics [ 0 ] . metrics [ 0 ] ;
376
+ assert_eq ! ( metric. name, "my_counter" ) ;
377
+ assert_eq ! ( metric. unit. as_str( ) , "my_unit" ) ;
378
+ assert_eq ! ( metric. description, "my_description" ) ;
379
+ let sum = metric
380
+ . data
381
+ . as_any ( )
382
+ . downcast_ref :: < data:: Sum < u64 > > ( )
383
+ . expect ( "Sum aggregation expected for Counter instruments by default" ) ;
384
+
385
+ // Expecting 1 time-series.
386
+ assert_eq ! ( sum. data_points. len( ) , 1 ) ;
387
+
388
+ let datapoint = & sum. data_points [ 0 ] ;
389
+ assert_eq ! ( datapoint. value, 15 ) ;
390
+ }
391
+
217
392
// "multi_thread" tokio flavor must be used else flush won't
218
393
// be able to make progress!
219
394
#[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
@@ -692,6 +867,15 @@ mod tests {
692
867
assert ! ( resource_metrics. is_empty( ) , "No metrics should be exported as no new measurements were recorded since last collect." ) ;
693
868
}
694
869
870
+ fn find_scope_metric < ' a > (
871
+ metrics : & ' a [ ScopeMetrics ] ,
872
+ name : & ' a str ,
873
+ ) -> Option < & ' a ScopeMetrics > {
874
+ metrics
875
+ . iter ( )
876
+ . find ( |& scope_metric| scope_metric. scope . name == name)
877
+ }
878
+
695
879
struct DeltaTemporalitySelector ( ) ;
696
880
impl TemporalitySelector for DeltaTemporalitySelector {
697
881
fn temporality ( & self , _kind : InstrumentKind ) -> Temporality {
0 commit comments