diff --git a/opentelemetry-api/tests/metrics/test_instruments.py b/opentelemetry-api/tests/metrics/test_instruments.py index 982cb6b6112..4eaf004cb5f 100644 --- a/opentelemetry-api/tests/metrics/test_instruments.py +++ b/opentelemetry-api/tests/metrics/test_instruments.py @@ -31,8 +31,7 @@ UpDownCounter, _Gauge, ) - -# FIXME Test that the instrument methods can be called concurrently safely. +from opentelemetry.test.concurrency_test import ConcurrencyTestBase class ChildInstrument(Instrument): @@ -724,3 +723,33 @@ def test_description_check(self): ], "", ) + + +class TestConcurrency(ConcurrencyTestBase): + def test_counter_add_concurrent(self): + """Test that Counter.add can be called concurrently safely.""" + counter = NoOpCounter("name") + results = self.run_with_many_threads(lambda: counter.add(1)) + self.assertEqual(len(results), 100) + self.assertTrue(all(result is None for result in results)) + + def test_up_down_counter_add_concurrent(self): + """Test that UpDownCounter.add can be called concurrently safely.""" + up_down_counter = NoOpUpDownCounter("name") + results = self.run_with_many_threads(lambda: up_down_counter.add(1)) + self.assertEqual(len(results), 100) + self.assertTrue(all(result is None for result in results)) + + def test_histogram_record_concurrent(self): + """Test that Histogram.record can be called concurrently safely.""" + histogram = NoOpHistogram("name") + results = self.run_with_many_threads(lambda: histogram.record(1)) + self.assertEqual(len(results), 100) + self.assertTrue(all(result is None for result in results)) + + def test_gauge_set_concurrent(self): + """Test that Gauge.set can be called concurrently safely.""" + gauge = NoOpMeter("name").create_gauge("name") + results = self.run_with_many_threads(lambda: gauge.set(1)) + self.assertEqual(len(results), 100) + self.assertTrue(all(result is None for result in results)) diff --git a/opentelemetry-api/tests/metrics/test_meter.py b/opentelemetry-api/tests/metrics/test_meter.py index 5a7ef3bc8b2..e21b221efbe 100644 --- a/opentelemetry-api/tests/metrics/test_meter.py +++ b/opentelemetry-api/tests/metrics/test_meter.py @@ -17,9 +17,18 @@ from unittest import TestCase from unittest.mock import Mock, patch -from opentelemetry.metrics import Meter, NoOpMeter - -# FIXME Test that the meter methods can be called concurrently safely. +from opentelemetry.metrics import ( + Counter, + Histogram, + Meter, + NoOpMeter, + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + UpDownCounter, + _Gauge, +) +from opentelemetry.test.concurrency_test import ConcurrencyTestBase class ChildMeter(Meter): @@ -195,3 +204,76 @@ def test_create_observable_up_down_counter(self): self.assertTrue( Meter.create_observable_up_down_counter.__isabstractmethod__ ) + + +class TestConcurrency(ConcurrencyTestBase): + def test_create_counter_concurrent(self): + """Test that Meter.create_counter can be called concurrently safely.""" + meter = NoOpMeter("name") + results = self.run_with_many_threads( + lambda: meter.create_counter("counter") + ) + self.assertEqual(len(results), 100) + self.assertTrue(all(isinstance(r, Counter) for r in results)) + + def test_create_up_down_counter_concurrent(self): + """Test that Meter.create_up_down_counter can be called concurrently safely.""" + meter = NoOpMeter("name") + results = self.run_with_many_threads( + lambda: meter.create_up_down_counter("up_down_counter") + ) + self.assertEqual(len(results), 100) + self.assertTrue(all(isinstance(r, UpDownCounter) for r in results)) + + def test_create_observable_counter_concurrent(self): + """Test that Meter.create_observable_counter can be called concurrently safely.""" + meter = NoOpMeter("name") + results = self.run_with_many_threads( + lambda: meter.create_observable_counter( + "observable_counter", lambda options: [] + ) + ) + self.assertEqual(len(results), 100) + self.assertTrue(all(isinstance(r, ObservableCounter) for r in results)) + + def test_create_histogram_concurrent(self): + """Test that Meter.create_histogram can be called concurrently safely.""" + meter = NoOpMeter("name") + results = self.run_with_many_threads( + lambda: meter.create_histogram("histogram") + ) + self.assertEqual(len(results), 100) + self.assertTrue(all(isinstance(r, Histogram) for r in results)) + + def test_create_gauge_concurrent(self): + """Test that Meter.create_gauge can be called concurrently safely.""" + meter = NoOpMeter("name") + results = self.run_with_many_threads( + lambda: meter.create_gauge("gauge") + ) + self.assertEqual(len(results), 100) + self.assertTrue(all(isinstance(r, _Gauge) for r in results)) + + def test_create_observable_gauge_concurrent(self): + """Test that Meter.create_observable_gauge can be called concurrently safely.""" + meter = NoOpMeter("name") + results = self.run_with_many_threads( + lambda: meter.create_observable_gauge( + "observable_gauge", lambda options: [] + ) + ) + self.assertEqual(len(results), 100) + self.assertTrue(all(isinstance(r, ObservableGauge) for r in results)) + + def test_create_observable_up_down_counter_concurrent(self): + """Test that Meter.create_observable_up_down_counter can be called concurrently safely.""" + meter = NoOpMeter("name") + results = self.run_with_many_threads( + lambda: meter.create_observable_up_down_counter( + "observable_up_down_counter", lambda options: [] + ) + ) + self.assertEqual(len(results), 100) + self.assertTrue( + all(isinstance(r, ObservableUpDownCounter) for r in results) + ) diff --git a/opentelemetry-api/tests/metrics/test_meter_provider.py b/opentelemetry-api/tests/metrics/test_meter_provider.py index dfaf94bcec2..171911fd7de 100644 --- a/opentelemetry-api/tests/metrics/test_meter_provider.py +++ b/opentelemetry-api/tests/metrics/test_meter_provider.py @@ -43,13 +43,12 @@ _ProxyObservableUpDownCounter, _ProxyUpDownCounter, ) +from opentelemetry.test.concurrency_test import ConcurrencyTestBase from opentelemetry.test.globals_test import ( MetricsGlobalsTest, reset_metrics_globals, ) -# FIXME Test that the instrument methods can be called concurrently safely. - @fixture def reset_meter_provider(): @@ -165,6 +164,26 @@ def test_get_meter_wrapper(self): self.assertEqual(meter.schema_url, "schema_url") +class TestConcurrency(ConcurrencyTestBase): + def test_no_op_meter_provider_get_meter_concurrent(self): + """Test that NoOpMeterProvider.get_meter can be called concurrently safely.""" + meter_provider = NoOpMeterProvider() + results = self.run_with_many_threads( + lambda: meter_provider.get_meter("name") + ) + self.assertEqual(len(results), 100) + self.assertTrue(all(isinstance(r, NoOpMeter) for r in results)) + + def test_proxy_meter_provider_get_meter_concurrent(self): + """Test that _ProxyMeterProvider.get_meter can be called concurrently safely.""" + meter_provider = _ProxyMeterProvider() + results = self.run_with_many_threads( + lambda: meter_provider.get_meter("name") + ) + self.assertEqual(len(results), 100) + self.assertTrue(all(isinstance(r, _ProxyMeter) for r in results)) + + class TestProxy(MetricsGlobalsTest, TestCase): def test_global_proxy_meter_provider(self): # Global get_meter_provider() should initially be a _ProxyMeterProvider