@@ -57,3 +57,103 @@ pub use meter_provider::*;
57
57
pub use periodic_reader:: * ;
58
58
pub use pipeline:: Pipeline ;
59
59
pub use view:: * ;
60
+
61
+ #[ cfg( all( test, feature = "testing" ) ) ]
62
+ mod tests {
63
+ use super :: * ;
64
+ use crate :: { runtime, testing:: metrics:: InMemoryMetricsExporter } ;
65
+ use opentelemetry:: {
66
+ metrics:: { MeterProvider as _, Unit } ,
67
+ KeyValue ,
68
+ } ;
69
+
70
+ // "multi_thread" tokio flavor must be used else flush won't
71
+ // be able to make progress!
72
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
73
+ async fn counter_aggregation ( ) {
74
+ // Run this test with stdout enabled to see output.
75
+ // cargo test counter --features=metrics,testing -- --nocapture
76
+
77
+ // Arrange
78
+ let exporter = InMemoryMetricsExporter :: default ( ) ;
79
+ let reader = PeriodicReader :: builder ( exporter. clone ( ) , runtime:: Tokio ) . build ( ) ;
80
+ let meter_provider = SdkMeterProvider :: builder ( ) . with_reader ( reader) . build ( ) ;
81
+
82
+ // Act
83
+ let meter = meter_provider. meter ( "test" ) ;
84
+ let counter = meter
85
+ . u64_counter ( "my_counter" )
86
+ . with_unit ( Unit :: new ( "my_unit" ) )
87
+ . init ( ) ;
88
+ counter. add ( 1 , & [ KeyValue :: new ( "key1" , "value1" ) ] ) ;
89
+ counter. add ( 1 , & [ KeyValue :: new ( "key1" , "value1" ) ] ) ;
90
+ counter. add ( 1 , & [ KeyValue :: new ( "key1" , "value1" ) ] ) ;
91
+ counter. add ( 1 , & [ KeyValue :: new ( "key1" , "value1" ) ] ) ;
92
+ counter. add ( 1 , & [ KeyValue :: new ( "key1" , "value1" ) ] ) ;
93
+
94
+ counter. add ( 1 , & [ KeyValue :: new ( "key1" , "value2" ) ] ) ;
95
+ counter. add ( 1 , & [ KeyValue :: new ( "key1" , "value2" ) ] ) ;
96
+ counter. add ( 1 , & [ KeyValue :: new ( "key1" , "value2" ) ] ) ;
97
+
98
+ meter_provider. force_flush ( ) . unwrap ( ) ;
99
+
100
+ // Assert
101
+ let resource_metrics = exporter
102
+ . get_finished_metrics ( )
103
+ . expect ( "metrics are expected to be exported." ) ;
104
+ assert ! ( !resource_metrics. is_empty( ) ) ;
105
+ let metric = & resource_metrics[ 0 ] . scope_metrics [ 0 ] . metrics [ 0 ] ;
106
+ assert_eq ! ( metric. name, "my_counter" ) ;
107
+ assert_eq ! ( metric. unit. as_str( ) , "my_unit" ) ;
108
+ let sum = metric
109
+ . data
110
+ . as_any ( )
111
+ . downcast_ref :: < data:: Sum < u64 > > ( )
112
+ . expect ( "Sum aggregation expected for Counter instruments by default" ) ;
113
+
114
+ // Expecting 2 time-series.
115
+ assert_eq ! ( sum. data_points. len( ) , 2 ) ;
116
+ assert ! ( sum. is_monotonic, "Counter should produce monotonic." ) ;
117
+ assert_eq ! (
118
+ sum. temporality,
119
+ data:: Temporality :: Cumulative ,
120
+ "Should produce cumulative by default."
121
+ ) ;
122
+
123
+ // find and validate key1=value1 datapoint
124
+ let mut data_point1 = None ;
125
+ for datapoint in & sum. data_points {
126
+ if datapoint
127
+ . attributes
128
+ . iter ( )
129
+ . any ( |( k, v) | k. as_str ( ) == "key1" && v. as_str ( ) == "value1" )
130
+ {
131
+ data_point1 = Some ( datapoint) ;
132
+ }
133
+ }
134
+ assert_eq ! (
135
+ data_point1
136
+ . expect( "datapoint with key1=value1 expected" )
137
+ . value,
138
+ 5
139
+ ) ;
140
+
141
+ // find and validate key1=value2 datapoint
142
+ let mut data_point1 = None ;
143
+ for datapoint in & sum. data_points {
144
+ if datapoint
145
+ . attributes
146
+ . iter ( )
147
+ . any ( |( k, v) | k. as_str ( ) == "key1" && v. as_str ( ) == "value2" )
148
+ {
149
+ data_point1 = Some ( datapoint) ;
150
+ }
151
+ }
152
+ assert_eq ! (
153
+ data_point1
154
+ . expect( "datapoint with key1=value2 expected" )
155
+ . value,
156
+ 3
157
+ ) ;
158
+ }
159
+ }
0 commit comments