Skip to content

Commit 70f8bef

Browse files
committed
add unit and integration tests
1 parent fadcefc commit 70f8bef

File tree

5 files changed

+455
-5
lines changed

5 files changed

+455
-5
lines changed

opentelemetry-sdk/tests/metrics/integration_test/test_histogram_export.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
from opentelemetry.sdk.metrics import MeterProvider
1818
from opentelemetry.sdk.metrics.export import InMemoryMetricReader
1919
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
20+
from opentelemetry.sdk.metrics._internal.exemplar import (
21+
AlwaysOffExemplarFilter,
22+
AlwaysOnExemplarFilter,
23+
TraceBasedExemplarFilter,
24+
)
2025

2126

2227
class TestHistogramExport(TestCase):
@@ -88,3 +93,63 @@ def test_histogram_counter_collection(self):
8893
),
8994
1,
9095
)
96+
97+
def test_histogram_with_exemplars(self):
98+
99+
in_memory_metric_reader = InMemoryMetricReader()
100+
101+
provider = MeterProvider(
102+
resource=Resource.create({SERVICE_NAME: "otel-test"}),
103+
metric_readers=[in_memory_metric_reader],
104+
exemplar_filter=AlwaysOnExemplarFilter()
105+
)
106+
meter = provider.get_meter("my-meter")
107+
histogram = meter.create_histogram("my_histogram")
108+
109+
histogram.record(2, {"attribute": "value1"}) # Should go in the first bucket
110+
histogram.record(7, {"attribute": "value2"}) # Should go in the second bucket
111+
histogram.record(9, {"attribute": "value2"}) # Should also go in the second bucket
112+
histogram.record(15, {"attribute": "value3"}) # Should go in the third bucket
113+
114+
metric_data = in_memory_metric_reader.get_metrics_data()
115+
116+
self.assertEqual(len(metric_data.resource_metrics[0].scope_metrics[0].metrics), 1)
117+
histogram_metric = metric_data.resource_metrics[0].scope_metrics[0].metrics[0]
118+
119+
self.assertEqual(len(histogram_metric.data.data_points), 3)
120+
121+
self.assertEqual(len(histogram_metric.data.data_points[0].exemplars), 1)
122+
self.assertEqual(len(histogram_metric.data.data_points[1].exemplars), 1)
123+
self.assertEqual(len(histogram_metric.data.data_points[2].exemplars), 1)
124+
125+
self.assertEqual(histogram_metric.data.data_points[0].sum, 2)
126+
self.assertEqual(histogram_metric.data.data_points[1].sum, 16)
127+
self.assertEqual(histogram_metric.data.data_points[2].sum, 15)
128+
129+
self.assertEqual(histogram_metric.data.data_points[0].exemplars[0].value, 2.0)
130+
self.assertEqual(histogram_metric.data.data_points[1].exemplars[0].value, 9.0)
131+
self.assertEqual(histogram_metric.data.data_points[2].exemplars[0].value, 15.0)
132+
133+
def test_filter_with_exemplars(self):
134+
in_memory_metric_reader = InMemoryMetricReader()
135+
136+
provider = MeterProvider(
137+
resource=Resource.create({SERVICE_NAME: "otel-test"}),
138+
metric_readers=[in_memory_metric_reader],
139+
exemplar_filter=AlwaysOffExemplarFilter()
140+
)
141+
meter = provider.get_meter("my-meter")
142+
histogram = meter.create_histogram("my_histogram")
143+
144+
histogram.record(2, {"attribute": "value1"}) # Should go in the first bucket
145+
histogram.record(7, {"attribute": "value2"}) # Should go in the second bucket
146+
147+
metric_data = in_memory_metric_reader.get_metrics_data()
148+
149+
self.assertEqual(len(metric_data.resource_metrics[0].scope_metrics[0].metrics), 1)
150+
histogram_metric = metric_data.resource_metrics[0].scope_metrics[0].metrics[0]
151+
152+
self.assertEqual(len(histogram_metric.data.data_points), 2)
153+
154+
self.assertEqual(len(histogram_metric.data.data_points[0].exemplars), 0)
155+
self.assertEqual(len(histogram_metric.data.data_points[1].exemplars), 0)

opentelemetry-sdk/tests/metrics/integration_test/test_sum_aggregation.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@
2727
InMemoryMetricReader,
2828
)
2929
from opentelemetry.sdk.metrics.view import SumAggregation
30-
30+
from opentelemetry.sdk.metrics._internal.exemplar import (
31+
AlwaysOffExemplarFilter,
32+
AlwaysOnExemplarFilter,
33+
TraceBasedExemplarFilter,
34+
)
35+
from opentelemetry.context import Context
3136

3237
class TestSumAggregation(TestCase):
3338
@mark.skipif(
@@ -474,3 +479,34 @@ def test_synchronous_cumulative_temporality(self):
474479
start_time_unix_nano, metric_data.start_time_unix_nano
475480
)
476481
self.assertEqual(metric_data.value, 80)
482+
483+
def test_sum_aggregation_with_exemplars(self):
484+
485+
in_memory_metric_reader = InMemoryMetricReader()
486+
487+
provider = MeterProvider(
488+
metric_readers=[in_memory_metric_reader],
489+
exemplar_filter=AlwaysOnExemplarFilter(),
490+
)
491+
492+
meter = provider.get_meter("my-meter")
493+
counter = meter.create_counter("my_counter")
494+
495+
counter.add(2, {"attribute": "value1"}, context=Context())
496+
counter.add(5, {"attribute": "value2"}, context=Context())
497+
counter.add(3, {"attribute": "value3"}, context=Context())
498+
499+
metric_data = in_memory_metric_reader.get_metrics_data()
500+
501+
self.assertEqual(len(metric_data.resource_metrics[0].scope_metrics[0].metrics), 1)
502+
503+
sum_metric = metric_data.resource_metrics[0].scope_metrics[0].metrics[0]
504+
505+
data_points = sum_metric.data.data_points
506+
self.assertEqual(len(data_points), 3)
507+
508+
self.assertEqual(data_points[0].exemplars[0].value, 2.0)
509+
self.assertEqual(data_points[1].exemplars[0].value, 5.0)
510+
self.assertEqual(data_points[2].exemplars[0].value, 3.0)
511+
512+
provider.shutdown()

opentelemetry-sdk/tests/metrics/test_aggregation.py

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@
4848
SumAggregation,
4949
)
5050
from opentelemetry.util.types import Attributes
51-
51+
from opentelemetry.sdk.metrics._internal.exemplar import (
52+
AlignedHistogramBucketExemplarReservoir,
53+
ExemplarReservoirFactory,
54+
SimpleFixedSizeExemplarReservoir,
55+
)
5256

5357
def measurement(
5458
value: Union[int, float], attributes: Attributes = None
@@ -640,3 +644,76 @@ def test_observable_gauge(self):
640644
0,
641645
)
642646
self.assertIsInstance(aggregation, _LastValueAggregation)
647+
648+
class TestExemplarsFromAggregations(TestCase):
649+
650+
def test_collection_simple_fixed_size_reservoir(self):
651+
exemplar_reservoir_factory = lambda: SimpleFixedSizeExemplarReservoir(size=5)
652+
synchronous_sum_aggregation = _SumAggregation(
653+
Mock(),
654+
True,
655+
AggregationTemporality.DELTA,
656+
0,
657+
exemplar_reservoir_factory,
658+
)
659+
660+
synchronous_sum_aggregation.aggregate(measurement(1))
661+
synchronous_sum_aggregation.aggregate(measurement(2))
662+
synchronous_sum_aggregation.aggregate(measurement(3))
663+
664+
self.assertEqual(synchronous_sum_aggregation._value, 6)
665+
datapoint = synchronous_sum_aggregation.collect(AggregationTemporality.CUMULATIVE, 0)
666+
self.assertEqual(len(datapoint.exemplars), 3)
667+
668+
def test_collection_simple_fixed_size_reservoir_with_default_reservoir(self):
669+
670+
synchronous_sum_aggregation = _SumAggregation(
671+
Mock(),
672+
True,
673+
AggregationTemporality.DELTA,
674+
0,
675+
default_reservoir_factory(_SumAggregation),
676+
)
677+
678+
synchronous_sum_aggregation.aggregate(measurement(1))
679+
synchronous_sum_aggregation.aggregate(measurement(2))
680+
synchronous_sum_aggregation.aggregate(measurement(3))
681+
682+
self.assertEqual(synchronous_sum_aggregation._value, 6)
683+
datapoint = synchronous_sum_aggregation.collect(AggregationTemporality.CUMULATIVE, 0)
684+
self.assertEqual(len(datapoint.exemplars), 1)
685+
686+
def test_collection_aligned_histogram_bucket_reservoir(self):
687+
boundaries = [5.0, 10.0, 20.0]
688+
exemplar_reservoir_factory = lambda: AlignedHistogramBucketExemplarReservoir(boundaries)
689+
synchronous_sum_aggregation = _SumAggregation(
690+
Mock(),
691+
True,
692+
AggregationTemporality.DELTA,
693+
0,
694+
exemplar_reservoir_factory,
695+
)
696+
697+
synchronous_sum_aggregation.aggregate(measurement(2.0))
698+
synchronous_sum_aggregation.aggregate(measurement(4.0))
699+
synchronous_sum_aggregation.aggregate(measurement(6.0))
700+
synchronous_sum_aggregation.aggregate(measurement(15.0))
701+
synchronous_sum_aggregation.aggregate(measurement(25.0))
702+
703+
datapoint = synchronous_sum_aggregation.collect(AggregationTemporality.CUMULATIVE, 0)
704+
self.assertEqual(len(datapoint.exemplars), 4)
705+
706+
# Verify that exemplars are associated with the correct boundaries
707+
expected_buckets = [
708+
(4.0, boundaries[0]), # First bucket, should hold the last value <= 5.0
709+
(6.0, boundaries[1]), # Second bucket, should hold the last value <= 10.0
710+
(15.0, boundaries[2]), # Third bucket, should hold the last value <= 20.0
711+
(25.0, None), # Last bucket, should hold the value > 20.0
712+
]
713+
714+
for exemplar, (value, boundary) in zip(datapoint.exemplars, expected_buckets):
715+
self.assertEqual(exemplar.value, value)
716+
if boundary is not None:
717+
self.assertLessEqual(exemplar.value, boundary)
718+
else:
719+
self.assertGreater(exemplar.value, boundaries[-1])

opentelemetry-sdk/tests/metrics/test_exemplarreservoir.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from unittest import TestCase
22

3-
#from opentelemetry.context import Context
3+
from opentelemetry.context import Context
44
from opentelemetry.trace import INVALID_SPAN, SpanContext, TraceFlags
55
from opentelemetry import trace
66
from time import time_ns
@@ -58,6 +58,26 @@ def test_filter_attributes(self):
5858
exemplars = reservoir.collect({"key2": "value2", "key3": "value3"})
5959
self.assertEqual(len(exemplars), 1)
6060
self.assertNotEqual("key1", exemplars[0].filtered_attributes)
61+
62+
def test_reset_after_collection(self):
63+
reservoir = SimpleFixedSizeExemplarReservoir(4)
64+
65+
reservoir.offer(1.0, time_ns(), {"attribute": "value1"}, Context())
66+
reservoir.offer(2.0, time_ns(), {"attribute": "value2"}, Context())
67+
reservoir.offer(3.0, time_ns(), {"attribute": "value3"}, Context())
68+
69+
exemplars = reservoir.collect({})
70+
self.assertEqual(len(exemplars), 3)
71+
72+
# Offer new measurements after reset
73+
reservoir.offer(4.0, time_ns(), {"attribute": "value4"}, Context())
74+
reservoir.offer(5.0, time_ns(), {"attribute": "value5"}, Context())
75+
76+
# Collect again and check the number of exemplars
77+
new_exemplars = reservoir.collect({})
78+
self.assertEqual(len(new_exemplars), 2)
79+
self.assertEqual(new_exemplars[0].value, 4.0)
80+
self.assertEqual(new_exemplars[1].value, 5.0)
6181

6282
class TestAlignedHistogramBucketExemplarReservoir(TestCase):
6383

@@ -78,14 +98,40 @@ def test_measurement_in_buckets(self):
7898
reservoir.offer(52, time_ns(), {"bucket": "5"}, ctx)
7999
reservoir.offer(7, time_ns(), {"bucket": "3"}, ctx)
80100
reservoir.offer(6, time_ns(), {"bucket": "3"}, ctx)
101+
81102
exemplars = reservoir.collect({"bucket": "3"})
103+
82104
self.assertEqual(len(exemplars), 2)
83105
self.assertEqual(exemplars[0].value, 6)
84106
self.assertEqual(exemplars[1].value, 52)
85107
self.assertEqual(len(exemplars[0].filtered_attributes), 0)
86108
self.assertNotEqual(exemplars[1].filtered_attributes, {"bucket": "5"})
109+
def test_last_measurement_in_bucket(self):
110+
reservoir = AlignedHistogramBucketExemplarReservoir([0, 5, 10, 25])
111+
span_context = SpanContext(
112+
trace_id=self.TRACE_ID,
113+
span_id=self.SPAN_ID,
114+
is_remote=False,
115+
trace_flags=TraceFlags(TraceFlags.SAMPLED),
116+
trace_state={}
117+
)
118+
span = trace.NonRecordingSpan(span_context)
119+
ctx = trace.set_span_in_context(span)
120+
121+
# Offer values to the reservoir
122+
reservoir.offer(2, time_ns(), {"bucket": "1"}, ctx) # Bucket 1
123+
reservoir.offer(7, time_ns(), {"bucket": "2"}, ctx) # Bucket 2
124+
reservoir.offer(8, time_ns(), {"bucket": "2"}, ctx) # Bucket 2 - should replace the 7
125+
reservoir.offer(15, time_ns(), {"bucket": "3"}, ctx) # Bucket 3
87126

88-
127+
exemplars = reservoir.collect({})
128+
129+
# Check that each bucket has the correct value
130+
self.assertEqual(len(exemplars), 3)
131+
self.assertEqual(exemplars[0].value, 2)
132+
self.assertEqual(exemplars[1].value, 8)
133+
self.assertEqual(exemplars[2].value, 15)
134+
89135
class TestExemplarReservoirFactory(TestCase):
90136
def test_sum_aggregation(self):
91137
exemplar_reservoir = default_reservoir_factory(_SumAggregation)

0 commit comments

Comments
 (0)