@@ -382,6 +382,188 @@ public void ObservableCounterAggregationTest(bool exportDelta)
382
382
}
383
383
}
384
384
385
+ [ Theory ]
386
+ [ InlineData ( true ) ]
387
+ [ InlineData ( false ) ]
388
+ public void DimensionsAreOrderInsensitiveWithSortedKeysFirst ( bool exportDelta )
389
+ {
390
+ var exportedItems = new List < Metric > ( ) ;
391
+
392
+ using var meter = new Meter ( $ "{ Utils . GetCurrentMethodName ( ) } .{ exportDelta } ") ;
393
+ var counterLong = meter . CreateCounter < long > ( "Counter" ) ;
394
+ using var meterProvider = Sdk . CreateMeterProviderBuilder ( )
395
+ . AddMeter ( meter . Name )
396
+ . AddReader ( new BaseExportingMetricReader ( new InMemoryExporter < Metric > ( exportedItems ) )
397
+ {
398
+ Temporality = exportDelta ? AggregationTemporality . Delta : AggregationTemporality . Cumulative ,
399
+ } )
400
+ . Build ( ) ;
401
+
402
+ // Emit the first metric with the sorted order of tag keys
403
+ counterLong . Add ( 5 , new ( "Key1" , "Value1" ) , new ( "Key2" , "Value2" ) , new ( "Key3" , "Value3" ) ) ;
404
+ counterLong . Add ( 10 , new ( "Key1" , "Value1" ) , new ( "Key3" , "Value3" ) , new ( "Key2" , "Value2" ) ) ;
405
+ counterLong . Add ( 10 , new ( "Key2" , "Value20" ) , new ( "Key1" , "Value10" ) , new ( "Key3" , "Value30" ) ) ;
406
+
407
+ // Emit a metric with different set of keys but the same set of values as one of the previous metric points
408
+ counterLong . Add ( 25 , new ( "Key4" , "Value1" ) , new ( "Key5" , "Value3" ) , new ( "Key6" , "Value2" ) ) ;
409
+ counterLong . Add ( 25 , new ( "Key4" , "Value1" ) , new ( "Key6" , "Value3" ) , new ( "Key5" , "Value2" ) ) ;
410
+
411
+ meterProvider . ForceFlush ( MaxTimeToAllowForFlush ) ;
412
+
413
+ List < KeyValuePair < string , object > > expectedTagsForFirstMetricPoint = new List < KeyValuePair < string , object > > ( )
414
+ {
415
+ new ( "Key1" , "Value1" ) ,
416
+ new ( "Key2" , "Value2" ) ,
417
+ new ( "Key3" , "Value3" ) ,
418
+ } ;
419
+
420
+ List < KeyValuePair < string , object > > expectedTagsForSecondMetricPoint = new List < KeyValuePair < string , object > > ( )
421
+ {
422
+ new ( "Key1" , "Value10" ) ,
423
+ new ( "Key2" , "Value20" ) ,
424
+ new ( "Key3" , "Value30" ) ,
425
+ } ;
426
+
427
+ List < KeyValuePair < string , object > > expectedTagsForThirdMetricPoint = new List < KeyValuePair < string , object > > ( )
428
+ {
429
+ new ( "Key4" , "Value1" ) ,
430
+ new ( "Key5" , "Value3" ) ,
431
+ new ( "Key6" , "Value2" ) ,
432
+ } ;
433
+
434
+ List < KeyValuePair < string , object > > expectedTagsForFourthMetricPoint = new List < KeyValuePair < string , object > > ( )
435
+ {
436
+ new ( "Key4" , "Value1" ) ,
437
+ new ( "Key5" , "Value2" ) ,
438
+ new ( "Key6" , "Value3" ) ,
439
+ } ;
440
+
441
+ Assert . Equal ( 4 , GetNumberOfMetricPoints ( exportedItems ) ) ;
442
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForFirstMetricPoint , 1 ) ;
443
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForSecondMetricPoint , 2 ) ;
444
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForThirdMetricPoint , 3 ) ;
445
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForFourthMetricPoint , 4 ) ;
446
+ long sumReceived = GetLongSum ( exportedItems ) ;
447
+ Assert . Equal ( 75 , sumReceived ) ;
448
+
449
+ exportedItems . Clear ( ) ;
450
+
451
+ counterLong . Add ( 5 , new ( "Key2" , "Value2" ) , new ( "Key1" , "Value1" ) , new ( "Key3" , "Value3" ) ) ;
452
+ counterLong . Add ( 5 , new ( "Key2" , "Value2" ) , new ( "Key1" , "Value1" ) , new ( "Key3" , "Value3" ) ) ;
453
+ counterLong . Add ( 10 , new ( "Key2" , "Value2" ) , new ( "Key3" , "Value3" ) , new ( "Key1" , "Value1" ) ) ;
454
+ counterLong . Add ( 10 , new ( "Key2" , "Value20" ) , new ( "Key3" , "Value30" ) , new ( "Key1" , "Value10" ) ) ;
455
+ counterLong . Add ( 20 , new ( "Key4" , "Value1" ) , new ( "Key6" , "Value2" ) , new ( "Key5" , "Value3" ) ) ;
456
+ counterLong . Add ( 20 , new ( "Key4" , "Value1" ) , new ( "Key5" , "Value2" ) , new ( "Key6" , "Value3" ) ) ;
457
+
458
+ meterProvider . ForceFlush ( MaxTimeToAllowForFlush ) ;
459
+
460
+ Assert . Equal ( 4 , GetNumberOfMetricPoints ( exportedItems ) ) ;
461
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForFirstMetricPoint , 1 ) ;
462
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForSecondMetricPoint , 2 ) ;
463
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForThirdMetricPoint , 3 ) ;
464
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForFourthMetricPoint , 4 ) ;
465
+ sumReceived = GetLongSum ( exportedItems ) ;
466
+ if ( exportDelta )
467
+ {
468
+ Assert . Equal ( 70 , sumReceived ) ;
469
+ }
470
+ else
471
+ {
472
+ Assert . Equal ( 145 , sumReceived ) ;
473
+ }
474
+ }
475
+
476
+ [ Theory ]
477
+ [ InlineData ( true ) ]
478
+ [ InlineData ( false ) ]
479
+ public void DimensionsAreOrderInsensitiveWithUnsortedKeysFirst ( bool exportDelta )
480
+ {
481
+ var exportedItems = new List < Metric > ( ) ;
482
+
483
+ using var meter = new Meter ( $ "{ Utils . GetCurrentMethodName ( ) } .{ exportDelta } ") ;
484
+ var counterLong = meter . CreateCounter < long > ( "Counter" ) ;
485
+ using var meterProvider = Sdk . CreateMeterProviderBuilder ( )
486
+ . AddMeter ( meter . Name )
487
+ . AddReader ( new BaseExportingMetricReader ( new InMemoryExporter < Metric > ( exportedItems ) )
488
+ {
489
+ Temporality = exportDelta ? AggregationTemporality . Delta : AggregationTemporality . Cumulative ,
490
+ } )
491
+ . Build ( ) ;
492
+
493
+ // Emit the first metric with the unsorted order of tag keys
494
+ counterLong . Add ( 5 , new ( "Key1" , "Value1" ) , new ( "Key3" , "Value3" ) , new ( "Key2" , "Value2" ) ) ;
495
+ counterLong . Add ( 10 , new ( "Key1" , "Value1" ) , new ( "Key2" , "Value2" ) , new ( "Key3" , "Value3" ) ) ;
496
+ counterLong . Add ( 10 , new ( "Key2" , "Value20" ) , new ( "Key1" , "Value10" ) , new ( "Key3" , "Value30" ) ) ;
497
+
498
+ // Emit a metric with different set of keys but the same set of values as one of the previous metric points
499
+ counterLong . Add ( 25 , new ( "Key4" , "Value1" ) , new ( "Key5" , "Value3" ) , new ( "Key6" , "Value2" ) ) ;
500
+ counterLong . Add ( 25 , new ( "Key4" , "Value1" ) , new ( "Key6" , "Value3" ) , new ( "Key5" , "Value2" ) ) ;
501
+
502
+ meterProvider . ForceFlush ( MaxTimeToAllowForFlush ) ;
503
+
504
+ List < KeyValuePair < string , object > > expectedTagsForFirstMetricPoint = new List < KeyValuePair < string , object > > ( )
505
+ {
506
+ new ( "Key1" , "Value1" ) ,
507
+ new ( "Key2" , "Value2" ) ,
508
+ new ( "Key3" , "Value3" ) ,
509
+ } ;
510
+
511
+ List < KeyValuePair < string , object > > expectedTagsForSecondMetricPoint = new List < KeyValuePair < string , object > > ( )
512
+ {
513
+ new ( "Key1" , "Value10" ) ,
514
+ new ( "Key2" , "Value20" ) ,
515
+ new ( "Key3" , "Value30" ) ,
516
+ } ;
517
+
518
+ List < KeyValuePair < string , object > > expectedTagsForThirdMetricPoint = new List < KeyValuePair < string , object > > ( )
519
+ {
520
+ new ( "Key4" , "Value1" ) ,
521
+ new ( "Key5" , "Value3" ) ,
522
+ new ( "Key6" , "Value2" ) ,
523
+ } ;
524
+
525
+ List < KeyValuePair < string , object > > expectedTagsForFourthMetricPoint = new List < KeyValuePair < string , object > > ( )
526
+ {
527
+ new ( "Key4" , "Value1" ) ,
528
+ new ( "Key5" , "Value2" ) ,
529
+ new ( "Key6" , "Value3" ) ,
530
+ } ;
531
+
532
+ Assert . Equal ( 4 , GetNumberOfMetricPoints ( exportedItems ) ) ;
533
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForFirstMetricPoint , 1 ) ;
534
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForSecondMetricPoint , 2 ) ;
535
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForThirdMetricPoint , 3 ) ;
536
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForFourthMetricPoint , 4 ) ;
537
+ long sumReceived = GetLongSum ( exportedItems ) ;
538
+ Assert . Equal ( 75 , sumReceived ) ;
539
+
540
+ exportedItems . Clear ( ) ;
541
+
542
+ counterLong . Add ( 5 , new ( "Key2" , "Value2" ) , new ( "Key1" , "Value1" ) , new ( "Key3" , "Value3" ) ) ;
543
+ counterLong . Add ( 5 , new ( "Key2" , "Value2" ) , new ( "Key1" , "Value1" ) , new ( "Key3" , "Value3" ) ) ;
544
+ counterLong . Add ( 10 , new ( "Key2" , "Value2" ) , new ( "Key3" , "Value3" ) , new ( "Key1" , "Value1" ) ) ;
545
+ counterLong . Add ( 10 , new ( "Key2" , "Value20" ) , new ( "Key3" , "Value30" ) , new ( "Key1" , "Value10" ) ) ;
546
+ counterLong . Add ( 20 , new ( "Key4" , "Value1" ) , new ( "Key6" , "Value2" ) , new ( "Key5" , "Value3" ) ) ;
547
+ counterLong . Add ( 20 , new ( "Key4" , "Value1" ) , new ( "Key5" , "Value2" ) , new ( "Key6" , "Value3" ) ) ;
548
+
549
+ meterProvider . ForceFlush ( MaxTimeToAllowForFlush ) ;
550
+
551
+ Assert . Equal ( 4 , GetNumberOfMetricPoints ( exportedItems ) ) ;
552
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForFirstMetricPoint , 1 ) ;
553
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForSecondMetricPoint , 2 ) ;
554
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForThirdMetricPoint , 3 ) ;
555
+ CheckTagsForNthMetricPoint ( exportedItems , expectedTagsForFourthMetricPoint , 4 ) ;
556
+ sumReceived = GetLongSum ( exportedItems ) ;
557
+ if ( exportDelta )
558
+ {
559
+ Assert . Equal ( 70 , sumReceived ) ;
560
+ }
561
+ else
562
+ {
563
+ Assert . Equal ( 145 , sumReceived ) ;
564
+ }
565
+ }
566
+
385
567
[ Theory ]
386
568
[ InlineData ( AggregationTemporality . Cumulative ) ]
387
569
[ InlineData ( AggregationTemporality . Delta ) ]
@@ -658,6 +840,41 @@ private static double GetDoubleSum(List<Metric> metrics)
658
840
return sum ;
659
841
}
660
842
843
+ private static int GetNumberOfMetricPoints ( List < Metric > metrics )
844
+ {
845
+ int count = 0 ;
846
+ foreach ( var metric in metrics )
847
+ {
848
+ foreach ( ref readonly var metricPoint in metric . GetMetricPoints ( ) )
849
+ {
850
+ count ++ ;
851
+ }
852
+ }
853
+
854
+ return count ;
855
+ }
856
+
857
+ // Provide tags input sorted by Key
858
+ private static void CheckTagsForNthMetricPoint ( List < Metric > metrics , List < KeyValuePair < string , object > > tags , int n )
859
+ {
860
+ var metric = metrics [ 0 ] ;
861
+ var metricPointEnumerator = metric . GetMetricPoints ( ) . GetEnumerator ( ) ;
862
+
863
+ for ( int i = 0 ; i < n ; i ++ )
864
+ {
865
+ Assert . True ( metricPointEnumerator . MoveNext ( ) ) ;
866
+ }
867
+
868
+ int index = 0 ;
869
+ var metricPoint = metricPointEnumerator . Current ;
870
+ foreach ( var tag in metricPoint . Tags )
871
+ {
872
+ Assert . Equal ( tags [ index ] . Key , tag . Key ) ;
873
+ Assert . Equal ( tags [ index ] . Value , tag . Value ) ;
874
+ index ++ ;
875
+ }
876
+ }
877
+
661
878
private static void CounterUpdateThread < T > ( object obj )
662
879
where T : struct , IComparable
663
880
{
0 commit comments