summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/opentelemetry-cpp/sdk/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/jaegertracing/opentelemetry-cpp/sdk/test')
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/CMakeLists.txt12
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/BUILD165
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/CMakeLists.txt21
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/controller_test.cc56
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/counter_aggregator_test.cc120
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/exact_aggregator_test.cc229
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/gauge_aggregator_test.cc133
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/histogram_aggregator_test.cc173
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_provider_sdk_test.cc26
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_test.cc297
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/metric_instrument_test.cc487
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/min_max_sum_count_aggregator_test.cc209
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/sketch_aggregator_test.cc254
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/ungrouped_processor_test.cc601
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/BUILD153
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/CMakeLists.txt37
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/atomic_unique_ptr_test.cc42
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/attribute_utils_test.cc53
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_benchmark.cc22
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_test.cc32
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/baseline_circular_buffer.h88
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_benchmark.cc133
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_range_test.cc59
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_test.cc161
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/empty_attributes_test.cc19
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/fast_random_number_generator_test.cc22
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/global_log_handle_test.cc67
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_benchmark.cc35
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_fork_test.cc53
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_test.cc35
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/BUILD14
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/CMakeLists.txt11
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc26
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/BUILD75
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/CMakeLists.txt10
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/batch_log_processor_test.cc269
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/log_record_test.cc66
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_provider_sdk_test.cc133
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_sdk_test.cc97
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/logs/simple_log_processor_test.cc152
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/BUILD203
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/CMakeLists.txt34
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/aggregation_test.cc182
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_instruments_test.cc60
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_metric_storage_test.cc132
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_benchmark.cc53
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_test.cc71
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_benchmark.cc27
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_test.cc49
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/BUILD47
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/CMakeLists.txt10
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/always_sample_filter_test.cc19
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/never_sample_filter_test.cc20
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/no_exemplar_reservoir_test.cc22
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/meter_provider_sdk_test.cc91
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/metric_reader_test.cc39
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/multi_metric_storage_test.cc62
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/observer_result_test.cc38
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/periodic_exporting_metric_reader_test.cc81
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_instruments_test.cc138
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_metric_storage_test.cc246
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/view_registry_test.cc82
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/resource/BUILD12
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/resource/CMakeLists.txt9
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/resource/resource_test.cc213
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/trace/BUILD155
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/trace/CMakeLists.txt30
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_off_sampler_test.cc42
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_on_sampler_test.cc60
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/trace/batch_span_processor_test.cc291
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/trace/parent_sampler_test.cc73
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/trace/sampler_benchmark.cc156
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/trace/simple_processor_test.cc75
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/trace/span_data_test.cc135
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/trace/trace_id_ratio_sampler_test.cc258
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_provider_test.cc99
-rw-r--r--src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_test.cc731
77 files changed, 8692 insertions, 0 deletions
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/CMakeLists.txt
new file mode 100644
index 000000000..dc8cc2b22
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_subdirectory(common)
+add_subdirectory(trace)
+if(WITH_METRICS_PREVIEW)
+ add_subdirectory(_metrics)
+else()
+ add_subdirectory(metrics)
+endif()
+if(WITH_LOGS_PREVIEW)
+ add_subdirectory(logs)
+endif()
+add_subdirectory(resource)
+add_subdirectory(instrumentationlibrary)
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/BUILD
new file mode 100644
index 000000000..44835d102
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/BUILD
@@ -0,0 +1,165 @@
+cc_test(
+ name = "controller_test",
+ srcs = [
+ "controller_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//exporters/ostream:ostream_metrics_exporter_deprecated",
+ "//sdk/src/_metrics:metrics_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "gauge_aggregator_test",
+ srcs = [
+ "gauge_aggregator_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/_metrics:metrics_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "min_max_sum_count_aggregator_test",
+ srcs = [
+ "min_max_sum_count_aggregator_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/_metrics:metrics_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "meter_provider_sdk_test",
+ srcs = [
+ "meter_provider_sdk_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/_metrics:metrics_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "meter_test",
+ srcs = [
+ "meter_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/_metrics:metrics_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "counter_aggregator_test",
+ srcs = [
+ "counter_aggregator_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/_metrics:metrics_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "exact_aggregator_test",
+ srcs = [
+ "exact_aggregator_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/_metrics:metrics_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "histogram_aggregator_test",
+ srcs = [
+ "histogram_aggregator_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/_metrics:metrics_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "metric_instrument_test",
+ srcs = [
+ "metric_instrument_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/_metrics:metrics_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "sketch_aggregator_test",
+ srcs = [
+ "sketch_aggregator_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/_metrics:metrics_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "ungrouped_processor_test",
+ srcs = [
+ "ungrouped_processor_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/_metrics:metrics_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/CMakeLists.txt
new file mode 100644
index 000000000..5d950ffb3
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/CMakeLists.txt
@@ -0,0 +1,21 @@
+foreach(
+ testname
+ meter_provider_sdk_test
+ gauge_aggregator_test
+ min_max_sum_count_aggregator_test
+ exact_aggregator_test
+ counter_aggregator_test
+ histogram_aggregator_test
+ ungrouped_processor_test
+ meter_test
+ metric_instrument_test
+ controller_test)
+ add_executable(${testname} "${testname}.cc")
+ target_link_libraries(
+ ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ opentelemetry_metrics_deprecated)
+ gtest_add_tests(
+ TARGET ${testname}
+ TEST_PREFIX metrics.
+ TEST_LIST ${testname})
+endforeach()
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/controller_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/controller_test.cc
new file mode 100644
index 000000000..aa92c8d04
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/controller_test.cc
@@ -0,0 +1,56 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/_metrics/controller.h"
+# include "opentelemetry/sdk/_metrics/meter.h"
+# include "opentelemetry/sdk/_metrics/ungrouped_processor.h"
+
+# include <gtest/gtest.h>
+# include <numeric>
+# include <thread>
+// #include <chrono>
+
+namespace metrics_api = opentelemetry::metrics;
+using namespace opentelemetry::sdk::common;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace sdk
+{
+namespace metrics
+{
+class DummyExporter : public MetricsExporter
+{
+ ExportResult Export(const std::vector<Record> &records) noexcept
+ {
+ return ExportResult::kSuccess;
+ }
+};
+
+TEST(Controller, Constructor)
+{
+
+ std::shared_ptr<metrics_api::Meter> meter =
+ std::shared_ptr<metrics_api::Meter>(new Meter("Test"));
+ PushController alpha(meter, std::unique_ptr<MetricsExporter>(new DummyExporter),
+ std::shared_ptr<MetricsProcessor>(
+ new opentelemetry::sdk::metrics::UngroupedMetricsProcessor(false)),
+ .05);
+
+ auto instr = meter->NewIntCounter("test", "none", "none", true);
+ std::map<std::string, std::string> labels = {{"key", "value"}};
+ auto labelkv = opentelemetry::common::KeyValueIterableView<decltype(labels)>{labels};
+
+ alpha.start();
+
+ for (int i = 0; i < 20; i++)
+ {
+ instr->add(i, labelkv);
+ }
+ alpha.stop();
+}
+
+} // namespace metrics
+} // namespace sdk
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/counter_aggregator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/counter_aggregator_test.cc
new file mode 100644
index 000000000..7ef452e77
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/counter_aggregator_test.cc
@@ -0,0 +1,120 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h"
+
+# include <gtest/gtest.h>
+# include <numeric>
+# include <thread>
+
+namespace metrics_api = opentelemetry::metrics;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace sdk
+{
+namespace metrics
+{
+
+TEST(CounterAggregator, NoUpdates)
+{
+ CounterAggregator<int> alpha(metrics_api::InstrumentKind::Counter);
+
+ EXPECT_EQ(alpha.get_checkpoint().size(), 1);
+ EXPECT_EQ(alpha.get_checkpoint()[0], 0);
+
+ alpha.checkpoint();
+ EXPECT_EQ(alpha.get_checkpoint().size(), 1);
+ EXPECT_EQ(alpha.get_checkpoint()[0], 0);
+}
+
+TEST(CounterAggregator, Update)
+{
+ CounterAggregator<int> alpha(metrics_api::InstrumentKind::Counter);
+ CounterAggregator<int> beta(metrics_api::InstrumentKind::Counter);
+
+ for (int i = 0; i < 123456; i++)
+ {
+ alpha.update(1);
+ }
+
+ int sum = 0;
+ for (int i = 0; i < 100; i++)
+ {
+ int tmp = std::rand() % 128;
+ beta.update(tmp);
+ sum += tmp;
+ }
+
+ EXPECT_EQ(alpha.get_checkpoint()[0], 0); // checkpoint shouldn't change even with updates
+ EXPECT_EQ(beta.get_checkpoint()[0], 0);
+
+ alpha.checkpoint();
+ beta.checkpoint();
+
+ EXPECT_EQ(alpha.get_checkpoint()[0], 123456);
+ EXPECT_EQ(beta.get_checkpoint()[0], sum);
+
+ alpha.update(15);
+ alpha.checkpoint();
+ EXPECT_EQ(alpha.get_checkpoint()[0], 15); // reset to 0 after first checkpoint call
+}
+
+// callback update function used to test concurrent calls
+void incrementingCallback(Aggregator<int> &agg)
+{
+ for (int i = 0; i < 2000000; i++)
+ {
+ agg.update(1);
+ }
+}
+
+TEST(CounterAggregator, Concurrency)
+{
+ CounterAggregator<int> alpha(metrics_api::InstrumentKind::Counter);
+
+ // spawn new threads that initiate the callback
+ std::thread first(incrementingCallback, std::ref(alpha));
+ std::thread second(incrementingCallback, std::ref(alpha));
+
+ first.join();
+ second.join();
+
+ alpha.checkpoint();
+
+ // race conditions result in values below 2*2000000
+ EXPECT_EQ(alpha.get_checkpoint()[0], 2 * 2000000);
+}
+
+TEST(CounterAggregator, Merge)
+{
+ CounterAggregator<int> alpha(metrics_api::InstrumentKind::Counter);
+ CounterAggregator<int> beta(metrics_api::InstrumentKind::Counter);
+
+ alpha.merge(beta);
+
+ alpha.checkpoint();
+ EXPECT_EQ(alpha.get_checkpoint()[0], 0); // merge with no updates
+
+ for (int i = 0; i < 500; i++)
+ {
+ alpha.update(1);
+ }
+
+ for (int i = 0; i < 700; i++)
+ {
+ beta.update(1);
+ }
+
+ alpha.merge(beta);
+ alpha.checkpoint();
+ EXPECT_EQ(alpha.get_checkpoint()[0], 1200);
+
+ // HistogramAggregator gamma(metrics_api::BoundInstrumentKind::BoundValueRecorder);
+ // ASSERT_THROW(alpha.merge(gamma), AggregatorMismatch);
+}
+
+} // namespace metrics
+} // namespace sdk
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/exact_aggregator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/exact_aggregator_test.cc
new file mode 100644
index 000000000..443cb65ea
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/exact_aggregator_test.cc
@@ -0,0 +1,229 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <gtest/gtest.h>
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include <thread>
+
+# include "opentelemetry/sdk/_metrics/aggregator/exact_aggregator.h"
+
+using namespace opentelemetry::sdk::metrics;
+namespace metrics_api = opentelemetry::metrics;
+
+TEST(ExactAggregatorOrdered, Update)
+{
+ ExactAggregator<int> agg(metrics_api::InstrumentKind::Counter);
+
+ std::vector<int> correct;
+
+ ASSERT_EQ(agg.get_values(), correct);
+
+ agg.update(1);
+ correct.push_back(1);
+
+ ASSERT_EQ(agg.get_values(), std::vector<int>{1});
+
+ for (int i = 2; i <= 5; ++i)
+ {
+ correct.push_back(i);
+ agg.update(i);
+ }
+ ASSERT_EQ(agg.get_values(), correct);
+}
+
+TEST(ExactAggregatorOrdered, Checkpoint)
+{
+ ExactAggregator<int> agg(metrics_api::InstrumentKind::Counter);
+
+ std::vector<int> correct;
+
+ ASSERT_EQ(agg.get_checkpoint(), correct);
+
+ agg.update(1);
+ correct.push_back(1);
+ agg.checkpoint();
+
+ ASSERT_EQ(agg.get_checkpoint(), correct);
+}
+
+TEST(ExactAggregatorOrdered, Merge)
+{
+ ExactAggregator<int> agg1(metrics_api::InstrumentKind::Counter);
+ ExactAggregator<int> agg2(metrics_api::InstrumentKind::Counter);
+
+ agg1.update(1);
+ agg2.update(2);
+ agg1.merge(agg2);
+
+ std::vector<int> correct{1, 2};
+
+ ASSERT_EQ(agg1.get_values(), correct);
+}
+
+TEST(ExactAggregatorOrdered, BadMerge)
+{
+ // This verifies that we encounter and error when we try to merge
+ // two aggregators of different numeric types together.
+ ExactAggregator<int> agg1(metrics_api::InstrumentKind::Counter);
+ ExactAggregator<int> agg2(metrics_api::InstrumentKind::ValueRecorder);
+
+ agg1.update(1);
+ agg2.update(2);
+
+ agg1.merge(agg2);
+
+ // Verify that the aggregators did NOT merge
+ std::vector<int> correct{1};
+ ASSERT_EQ(agg1.get_values(), correct);
+}
+
+TEST(ExactAggregatorOrdered, Types)
+{
+ // This test verifies that we do not encounter any errors when
+ // using various numeric types.
+ ExactAggregator<int> agg_int(metrics_api::InstrumentKind::Counter);
+ ExactAggregator<long> agg_long(metrics_api::InstrumentKind::Counter);
+ ExactAggregator<float> agg_float(metrics_api::InstrumentKind::Counter);
+ ExactAggregator<double> agg_double(metrics_api::InstrumentKind::Counter);
+
+ for (int i = 1; i <= 5; ++i)
+ {
+ agg_int.update(i);
+ agg_long.update(i);
+ }
+
+ for (float i = 1.0; i <= 5.0; i += 1)
+ {
+ agg_float.update(i);
+ agg_double.update(i);
+ }
+
+ std::vector<int> correct_int{1, 2, 3, 4, 5};
+ std::vector<long> correct_long{1, 2, 3, 4, 5};
+ std::vector<float> correct_float{1.0, 2.0, 3.0, 4.0, 5.0};
+ std::vector<double> correct_double{1.0, 2.0, 3.0, 4.0, 5.0};
+
+ ASSERT_EQ(agg_int.get_values(), correct_int);
+ ASSERT_EQ(agg_long.get_values(), correct_long);
+ ASSERT_EQ(agg_float.get_values(), correct_float);
+ ASSERT_EQ(agg_double.get_values(), correct_double);
+}
+
+TEST(ExactAggregatorQuant, Update)
+{
+ ExactAggregator<int> agg(metrics_api::InstrumentKind::Counter, true);
+
+ std::vector<int> correct;
+
+ ASSERT_EQ(agg.get_values(), correct);
+
+ agg.update(1);
+ correct.push_back(1);
+
+ ASSERT_EQ(agg.get_values(), std::vector<int>{1});
+
+ for (int i = 2; i <= 5; ++i)
+ {
+ correct.push_back(i);
+ agg.update(i);
+ }
+ ASSERT_EQ(agg.get_values(), correct);
+}
+
+TEST(ExactAggregatorQuant, Checkpoint)
+{
+ // This test verifies that the aggregator updates correctly when
+ // quantile estimation is turned on.
+
+ ExactAggregator<int> agg(metrics_api::InstrumentKind::Counter, true);
+
+ std::vector<int> correct;
+
+ ASSERT_EQ(agg.get_checkpoint(), correct);
+
+ agg.update(1);
+ agg.update(0);
+ agg.update(-1);
+
+ // The vector MUST be sorted when checkpointed
+ correct.push_back(-1);
+ correct.push_back(0);
+ correct.push_back(1);
+ agg.checkpoint();
+
+ ASSERT_EQ(agg.get_checkpoint(), correct);
+}
+
+TEST(ExactAggregatorQuant, Quantile)
+{
+ // This test verifies that the quantile estimation function returns
+ // the correct values.
+
+ ExactAggregator<int> agg(metrics_api::InstrumentKind::Counter, true);
+
+ std::vector<int> tmp{3, 9, 42, 57, 163, 210, 272, 300};
+ for (int i : tmp)
+ {
+ agg.update(i);
+ }
+ agg.checkpoint();
+ ASSERT_EQ(agg.get_quantiles(.25), 42);
+ ASSERT_EQ(agg.get_quantiles(0.5), 163);
+ ASSERT_EQ(agg.get_quantiles(0.75), 272);
+}
+
+TEST(ExactAggregatorInOrder, Quantile)
+{
+ // This test verifies that if the user has an exact aggregator in "in-order" mode
+ // an exception will be thrown if they call the quantile() function.
+ ExactAggregator<int> agg(metrics_api::InstrumentKind::Counter);
+
+ std::vector<int> tmp{3, 9, 42, 57, 163, 210, 272, 300};
+ for (int i : tmp)
+ {
+ agg.update(i);
+ }
+ agg.checkpoint();
+# if __EXCEPTIONS
+ ASSERT_THROW(agg.get_quantiles(0.5), std::domain_error);
+# else
+# endif
+}
+
+void callback(ExactAggregator<int> &agg)
+{
+ for (int i = 1; i <= 10000; ++i)
+ {
+ agg.update(i);
+ }
+}
+
+TEST(ExactAggregatorQuant, Concurrency)
+{
+ // This test checks that the aggregator updates appropriately
+ // when called in a multi-threaded context.
+ ExactAggregator<int> agg(metrics_api::InstrumentKind::Counter, true);
+
+ std::thread first(&callback, std::ref(agg));
+ std::thread second(&callback, std::ref(agg));
+
+ first.join();
+ second.join();
+
+ std::vector<int> correct;
+ for (int i = 1; i <= 10000; ++i)
+ {
+ correct.push_back(i);
+ correct.push_back(i);
+ }
+ agg.checkpoint();
+
+ ASSERT_EQ(agg.get_checkpoint(), correct);
+}
+#else
+TEST(ExactAggregatorQuant, DummyTest)
+{
+ // empty
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/gauge_aggregator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/gauge_aggregator_test.cc
new file mode 100644
index 000000000..9565e0629
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/gauge_aggregator_test.cc
@@ -0,0 +1,133 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include <gtest/gtest.h>
+# include <thread>
+
+# include "opentelemetry/sdk/_metrics/aggregator/gauge_aggregator.h"
+
+using namespace opentelemetry::sdk::metrics;
+namespace metrics_api = opentelemetry::metrics;
+
+TEST(GaugeAggregator, Update)
+{
+ // This tests that the aggregator updates the maintained value correctly
+ // after a call to the update() function.
+ auto agg = new GaugeAggregator<int>(metrics_api::InstrumentKind::Counter);
+
+ // Verify default value
+ ASSERT_EQ(agg->get_values()[0], 0);
+
+ // Verify that the value updates correctly
+ agg->update(1);
+ ASSERT_EQ(agg->get_values()[0], 1);
+
+ // Verify that the value continually updates correctly
+ for (int i = 0; i < 10; ++i)
+ {
+ agg->update(i);
+ }
+ ASSERT_EQ(agg->get_values()[0], 9);
+ delete agg;
+}
+
+TEST(GaugeAggregator, Checkpoint)
+{
+ // This tests that the aggregator correctly updates the
+ // checkpoint_ value after a call to update() followed
+ // by a call to checkpoint().
+ GaugeAggregator<int> agg(metrics_api::InstrumentKind::Counter);
+
+ // Verify default checkpoint, before updates
+ ASSERT_EQ(agg.get_checkpoint()[0], 0);
+
+ agg.update(10);
+ agg.checkpoint();
+
+ // Verify that the new checkpoint contains the update value
+ ASSERT_EQ(agg.get_checkpoint()[0], 10);
+}
+
+TEST(GaugeAggregator, Merge)
+{
+ // This tests that the values_ vector is updated correctly after
+ // two aggregators are merged together.
+ GaugeAggregator<int> agg1(metrics_api::InstrumentKind::Counter);
+ GaugeAggregator<int> agg2(metrics_api::InstrumentKind::Counter);
+
+ agg1.update(1);
+ agg2.update(2);
+
+ agg1.merge(agg2);
+
+ // Verify that the aggregators merged and the value was updated correctly
+ ASSERT_EQ(agg1.get_values()[0], 2);
+}
+
+TEST(GaugeAggregator, BadMerge)
+{
+ // This verifies that we encounter and error when we try to merge
+ // two aggregators of different numeric types together.
+ GaugeAggregator<int> agg1(metrics_api::InstrumentKind::Counter);
+ GaugeAggregator<int> agg2(metrics_api::InstrumentKind::ValueRecorder);
+
+ agg1.update(1);
+ agg2.update(2);
+ agg1.merge(agg2);
+
+ // Verify that the aggregators did NOT merge
+ std::vector<int> correct{1};
+ ASSERT_EQ(agg1.get_values(), correct);
+}
+
+TEST(GaugeAggregator, Types)
+{
+ // This test verifies that we do not encounter any errors when
+ // using various numeric types.
+ GaugeAggregator<int> agg_int(metrics_api::InstrumentKind::Counter);
+ GaugeAggregator<long> agg_long(metrics_api::InstrumentKind::Counter);
+ GaugeAggregator<float> agg_float(metrics_api::InstrumentKind::Counter);
+ GaugeAggregator<double> agg_double(metrics_api::InstrumentKind::Counter);
+
+ for (int i = 1; i <= 10; ++i)
+ {
+ agg_int.update(i);
+ agg_long.update(i);
+ }
+
+ for (float i = 1.0; i <= 10.0; i += 1)
+ {
+ agg_float.update(i);
+ agg_double.update(i);
+ }
+
+ ASSERT_EQ(agg_int.get_values()[0], 10);
+ ASSERT_EQ(agg_long.get_values()[0], 10);
+ ASSERT_EQ(agg_float.get_values()[0], 10.0);
+ ASSERT_EQ(agg_double.get_values()[0], 10.0);
+}
+
+static void callback(GaugeAggregator<int> &agg)
+{
+ for (int i = 1; i <= 10000; ++i)
+ {
+ agg.update(i);
+ }
+}
+
+TEST(GaugeAggregator, Concurrency)
+{
+ // This test checks that the aggregator updates appropriately
+ // when called in a multi-threaded context.
+ GaugeAggregator<int> agg(metrics_api::InstrumentKind::Counter);
+
+ std::thread first(&callback, std::ref(agg));
+ std::thread second(&callback, std::ref(agg));
+
+ first.join();
+ second.join();
+
+ ASSERT_EQ(agg.get_values()[0], 10000);
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/histogram_aggregator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/histogram_aggregator_test.cc
new file mode 100644
index 000000000..daf920aa7
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/histogram_aggregator_test.cc
@@ -0,0 +1,173 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/_metrics/aggregator/histogram_aggregator.h"
+
+# include <gtest/gtest.h>
+# include <iostream>
+# include <numeric>
+# include <thread>
+
+// #include <chrono>
+
+namespace metrics_api = opentelemetry::metrics;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace sdk
+{
+namespace metrics
+{
+
+// Test updating with a uniform set of updates
+TEST(Histogram, Uniform)
+{
+ std::vector<double> boundaries{10, 20, 30, 40, 50};
+ HistogramAggregator<int> alpha(metrics_api::InstrumentKind::Counter, boundaries);
+
+ EXPECT_EQ(alpha.get_aggregator_kind(), AggregatorKind::Histogram);
+
+ alpha.checkpoint();
+ EXPECT_EQ(alpha.get_checkpoint().size(), 2);
+ EXPECT_EQ(alpha.get_counts().size(), 6);
+
+ for (int i = 0; i < 60; i++)
+ {
+ alpha.update(i);
+ }
+
+ alpha.checkpoint();
+
+ EXPECT_EQ(alpha.get_checkpoint()[0], 1770);
+ EXPECT_EQ(alpha.get_checkpoint()[1], 60);
+
+ std::vector<int> correct = {10, 10, 10, 10, 10, 10};
+ EXPECT_EQ(alpha.get_counts(), correct);
+}
+
+// Test updating with a normal distribution
+TEST(Histogram, Normal)
+{
+ std::vector<double> boundaries{2, 4, 6, 8, 10, 12};
+ HistogramAggregator<int> alpha(metrics_api::InstrumentKind::Counter, boundaries);
+
+ std::vector<int> vals{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13};
+ for (int i : vals)
+ {
+ alpha.update(i);
+ }
+
+ alpha.checkpoint();
+
+ EXPECT_EQ(alpha.get_checkpoint()[0], std::accumulate(vals.begin(), vals.end(), 0));
+ EXPECT_EQ(alpha.get_checkpoint()[1], vals.size());
+
+ std::vector<int> correct = {1, 2, 3, 4, 3, 2, 1};
+ EXPECT_EQ(alpha.get_counts(), correct);
+}
+
+TEST(Histogram, Merge)
+{
+ std::vector<double> boundaries{2, 4, 6, 8, 10, 12};
+ HistogramAggregator<int> alpha(metrics_api::InstrumentKind::Counter, boundaries);
+ HistogramAggregator<int> beta(metrics_api::InstrumentKind::Counter, boundaries);
+
+ std::vector<int> vals{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13};
+ for (int i : vals)
+ {
+ alpha.update(i);
+ }
+
+ std::vector<int> otherVals{1, 1, 1, 1, 11, 11, 13, 13, 13, 15};
+ for (int i : otherVals)
+ {
+ beta.update(i);
+ }
+
+ alpha.merge(beta);
+ alpha.checkpoint();
+
+ EXPECT_EQ(alpha.get_checkpoint()[0], std::accumulate(vals.begin(), vals.end(), 0) +
+ std::accumulate(otherVals.begin(), otherVals.end(), 0));
+ EXPECT_EQ(alpha.get_checkpoint()[1], vals.size() + otherVals.size());
+
+ std::vector<int> correct = {5, 2, 3, 4, 3, 4, 5};
+ EXPECT_EQ(alpha.get_counts(), correct);
+}
+
+// Update callback used to validate multi-threaded performance
+void histogramUpdateCallback(Aggregator<int> &agg, std::vector<int> vals)
+{
+ for (int i : vals)
+ {
+ agg.update(i);
+ }
+}
+
+int randVal()
+{
+ return rand() % 15;
+}
+
+TEST(Histogram, Concurrency)
+{
+ std::vector<double> boundaries{2, 4, 6, 8, 10, 12};
+ HistogramAggregator<int> alpha(metrics_api::InstrumentKind::Counter, boundaries);
+
+ std::vector<int> vals1(1000);
+ std::generate(vals1.begin(), vals1.end(), randVal);
+
+ std::vector<int> vals2(1000);
+ std::generate(vals2.begin(), vals2.end(), randVal);
+
+ std::thread first(histogramUpdateCallback, std::ref(alpha), vals1);
+ std::thread second(histogramUpdateCallback, std::ref(alpha), vals2);
+
+ first.join();
+ second.join();
+
+ HistogramAggregator<int> beta(metrics_api::InstrumentKind::Counter, boundaries);
+
+ // Timing harness to compare linear and binary insertion
+ // auto start = std::chrono::system_clock::now();
+ for (int i : vals1)
+ {
+ beta.update(i);
+ }
+ for (int i : vals2)
+ {
+ beta.update(i);
+ }
+ // auto end = std::chrono::system_clock::now();
+ // auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
+ // std::cout <<"Update time: " <<elapsed.count() <<std::endl;
+
+ alpha.checkpoint();
+ beta.checkpoint();
+
+ EXPECT_EQ(alpha.get_checkpoint(), beta.get_checkpoint());
+ EXPECT_EQ(alpha.get_counts(), beta.get_counts());
+}
+
+# if __EXCEPTIONS
+
+TEST(Histogram, Errors)
+{
+ std::vector<double> boundaries{2, 4, 6, 8, 10, 12};
+ std::vector<double> boundaries2{1, 4, 6, 8, 10, 12};
+ std::vector<double> unsortedBoundaries{10, 12, 4, 6, 8};
+ EXPECT_ANY_THROW(
+ HistogramAggregator<int> alpha(metrics_api::InstrumentKind::Counter, unsortedBoundaries));
+
+ HistogramAggregator<int> beta(metrics_api::InstrumentKind::Counter, boundaries);
+ HistogramAggregator<int> gamma(metrics_api::InstrumentKind::Counter, boundaries2);
+
+ EXPECT_ANY_THROW(beta.merge(gamma));
+}
+
+# endif
+
+} // namespace metrics
+} // namespace sdk
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_provider_sdk_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_provider_sdk_test.cc
new file mode 100644
index 000000000..2181bb62a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_provider_sdk_test.cc
@@ -0,0 +1,26 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include <gtest/gtest.h>
+
+# include "opentelemetry/sdk/_metrics/meter.h"
+# include "opentelemetry/sdk/_metrics/meter_provider.h"
+
+using namespace opentelemetry::sdk::metrics;
+
+TEST(MeterProvider, GetMeter)
+{
+ MeterProvider tf;
+ auto t1 = tf.GetMeter("test");
+ auto t2 = tf.GetMeter("test");
+ auto t3 = tf.GetMeter("different", "1.0.0");
+ ASSERT_NE(nullptr, t1);
+ ASSERT_NE(nullptr, t2);
+ ASSERT_NE(nullptr, t3);
+
+ // Should return the same instance each time.
+ ASSERT_EQ(t1, t2);
+ ASSERT_EQ(t1, t3);
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_test.cc
new file mode 100644
index 000000000..73ff1f9bd
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_test.cc
@@ -0,0 +1,297 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include <gtest/gtest.h>
+# include <future>
+
+# include "opentelemetry/sdk/_metrics/meter.h"
+
+using namespace opentelemetry::sdk::metrics;
+namespace metrics_api = opentelemetry::metrics;
+namespace common = opentelemetry::common;
+namespace nostd = opentelemetry::nostd;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+
+TEST(Meter, CreateSyncInstruments)
+{
+ // Test that there are no errors creating synchronous instruments.
+ Meter m("Test");
+
+ m.NewShortCounter("Test-short-counter", "For testing", "Unitless", true);
+ m.NewIntCounter("Test-int-counter", "For testing", "Unitless", true);
+ m.NewFloatCounter("Test-float-counter", "For testing", "Unitless", true);
+ m.NewDoubleCounter("Test-double-counter", "For testing", "Unitless", true);
+
+ m.NewShortUpDownCounter("Test-short-ud-counter", "For testing", "Unitless", true);
+ m.NewIntUpDownCounter("Test-int-ud-counter", "For testing", "Unitless", true);
+ m.NewFloatUpDownCounter("Test-float-ud-counter", "For testing", "Unitless", true);
+ m.NewDoubleUpDownCounter("Test-double-ud-counter", "For testing", "Unitless", true);
+
+ m.NewShortValueRecorder("Test-short-recorder", "For testing", "Unitless", true);
+ m.NewIntValueRecorder("Test-int-recorder", "For testing", "Unitless", true);
+ m.NewFloatValueRecorder("Test-float-recorder", "For testing", "Unitless", true);
+ m.NewDoubleValueRecorder("Test-double-recorder", "For testing", "Unitless", true);
+}
+
+// Dummy functions for asynchronous instrument constructors
+void ShortCallback(metrics_api::ObserverResult<short>) {}
+void IntCallback(metrics_api::ObserverResult<int>) {}
+void FloatCallback(metrics_api::ObserverResult<float>) {}
+void DoubleCallback(metrics_api::ObserverResult<double>) {}
+
+TEST(Meter, CreateAsyncInstruments)
+{
+ // Test that there are no errors when creating asynchronous instruments.
+ Meter m("Test");
+
+ m.NewShortSumObserver("Test-short-sum-obs", "For testing", "Unitless", true, &ShortCallback);
+ m.NewIntSumObserver("Test-int-sum-obs", "For testing", "Unitless", true, &IntCallback);
+ m.NewFloatSumObserver("Test-float-sum-obs", "For testing", "Unitless", true, &FloatCallback);
+ m.NewDoubleSumObserver("Test-double-sum-obs", "For testing", "Unitless", true, &DoubleCallback);
+
+ m.NewShortUpDownSumObserver("Test-short-ud-sum-obs", "For testing", "Unitless", true,
+ &ShortCallback);
+ m.NewIntUpDownSumObserver("Test-int-ud-sum-obs", "For testing", "Unitless", true, &IntCallback);
+ m.NewFloatUpDownSumObserver("Test-float-ud-sum-obs", "For testing", "Unitless", true,
+ &FloatCallback);
+ m.NewDoubleUpDownSumObserver("Test-double-ud-sum-obs", "For testing", "Unitless", true,
+ &DoubleCallback);
+
+ m.NewShortValueObserver("Test-short-val-obs", "For testing", "Unitless", true, &ShortCallback);
+ m.NewIntValueObserver("Test-int-val-obs", "For testing", "Unitless", true, &IntCallback);
+ m.NewFloatValueObserver("Test-float-val-obs", "For testing", "Unitless", true, &FloatCallback);
+ m.NewDoubleValueObserver("Test-double-val-obs", "For testing", "Unitless", true, &DoubleCallback);
+}
+
+TEST(Meter, CollectSyncInstruments)
+{
+ // Verify that the records returned on a call to Collect() are correct for synchronous
+ // instruments.
+ Meter m("Test");
+
+ ASSERT_EQ(m.Collect().size(), 0);
+
+ auto counter = m.NewShortCounter("Test-counter", "For testing", "Unitless", true);
+
+ std::map<std::string, std::string> labels = {{"Key", "Value"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+
+ counter->add(1, labelkv);
+
+ std::vector<Record> res = m.Collect();
+ auto agg_var = res[0].GetAggregator();
+ auto agg = nostd::get<0>(agg_var);
+
+ ASSERT_EQ(agg->get_checkpoint()[0], 1);
+
+ // Now call add() and Collect() again to ensure that the value in the underlying
+ // aggregator was reset to the default.
+
+ counter->add(10, labelkv);
+
+ res = m.Collect();
+ agg_var = res[0].GetAggregator();
+ agg = nostd::get<0>(agg_var);
+
+ ASSERT_EQ(agg->get_checkpoint()[0], 10);
+}
+
+TEST(Meter, CollectDeletedSync)
+{
+ // Verify that calling Collect() after creating a synchronous instrument and destroying
+ // the return pointer does not result in a segfault.
+
+ Meter m("Test");
+
+ ASSERT_EQ(m.Collect().size(), 0);
+
+ std::map<std::string, std::string> labels = {{"Key", "Value"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ {
+ auto counter = m.NewShortCounter("Test-counter", "For testing", "Unitless", true);
+ counter->add(1, labelkv);
+ } // counter shared_ptr deleted here
+
+ std::vector<Record> res = m.Collect();
+ auto agg_var = res[0].GetAggregator();
+ auto agg = nostd::get<0>(agg_var);
+
+ ASSERT_EQ(agg->get_checkpoint()[0], 1);
+}
+
+// Dummy function for asynchronous instrument constructors.
+void Callback(metrics_api::ObserverResult<short> result)
+{
+ std::map<std::string, std::string> labels = {{"key", "value"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ result.observe(1, labelkv);
+}
+
+TEST(Meter, CollectAsyncInstruments)
+{
+ // Verify that the records returned on a call to Collect() are correct for asynchronous
+ // instruments.
+ Meter m("Test");
+
+ ASSERT_EQ(m.Collect().size(), 0);
+
+ auto sumobs =
+ m.NewShortSumObserver("Test-counter", "For testing", "Unitless", true, &ShortCallback);
+
+ std::map<std::string, std::string> labels = {{"Key", "Value"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+
+ sumobs->observe(1, labelkv);
+
+ std::vector<Record> res = m.Collect();
+ auto agg_var = res[0].GetAggregator();
+ auto agg = nostd::get<0>(agg_var);
+
+ ASSERT_EQ(agg->get_checkpoint()[0], 1);
+
+ // Now call observe() and Collect() again to ensure that the value in the underlying
+ // aggregator was reset to the default.
+
+ sumobs->observe(10, labelkv);
+
+ res = m.Collect();
+ agg_var = res[0].GetAggregator();
+ agg = nostd::get<0>(agg_var);
+
+ ASSERT_EQ(agg->get_checkpoint()[0], 10);
+}
+
+TEST(Meter, CollectDeletedAsync)
+{
+ // Verify that calling Collect() after creating an asynchronous instrument and destroying
+ // the return pointer does not result in a segfault.
+
+ Meter m("Test");
+
+ ASSERT_EQ(m.Collect().size(), 0);
+
+ std::map<std::string, std::string> labels = {{"Key", "Value"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ {
+ auto sumobs = m.NewShortSumObserver("Test-counter", "For testing", "Unitless", true, &Callback);
+ sumobs->observe(1, labelkv);
+ } // sumobs shared_ptr deleted here
+
+ std::vector<Record> res = m.Collect();
+ auto agg_var = res[0].GetAggregator();
+ auto agg = nostd::get<0>(agg_var);
+
+ ASSERT_EQ(agg->get_checkpoint()[0], 1);
+}
+
+TEST(Meter, RecordBatch)
+{
+ // This tests that RecordBatch appropriately updates the aggregators of the instruments
+ // passed to the function. Short, int, float, and double data types are tested.
+ Meter m("Test");
+
+ auto scounter = m.NewShortCounter("Test-scounter", "For testing", "Unitless", true);
+ auto icounter = m.NewIntCounter("Test-icounter", "For testing", "Unitless", true);
+ auto fcounter = m.NewFloatCounter("Test-fcounter", "For testing", "Unitless", true);
+ auto dcounter = m.NewDoubleCounter("Test-dcounter", "For testing", "Unitless", true);
+
+ std::map<std::string, std::string> labels = {{"Key", "Value"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+
+ metrics_api::SynchronousInstrument<short> *sinstr_arr[] = {scounter.get()};
+ short svalues_arr[] = {1};
+
+ nostd::span<metrics_api::SynchronousInstrument<short> *> sinstrs{sinstr_arr};
+ nostd::span<const short, 1> svalues{svalues_arr};
+
+ m.RecordShortBatch(labelkv, sinstrs, svalues);
+ std::vector<Record> res = m.Collect();
+ auto short_agg_var = res[0].GetAggregator();
+ auto short_agg = nostd::get<0>(short_agg_var);
+ ASSERT_EQ(short_agg->get_checkpoint()[0], 1);
+
+ metrics_api::SynchronousInstrument<int> *iinstr_arr[] = {icounter.get()};
+ int ivalues_arr[] = {1};
+
+ nostd::span<metrics_api::SynchronousInstrument<int> *> iinstrs{iinstr_arr};
+ nostd::span<const int, 1> ivalues{ivalues_arr};
+
+ m.RecordIntBatch(labelkv, iinstrs, ivalues);
+ res = m.Collect();
+ auto int_agg_var = res[0].GetAggregator();
+ auto int_agg = nostd::get<1>(int_agg_var);
+ ASSERT_EQ(int_agg->get_checkpoint()[0], 1);
+
+ metrics_api::SynchronousInstrument<float> *finstr_arr[] = {fcounter.get()};
+ float fvalues_arr[] = {1.0};
+
+ nostd::span<metrics_api::SynchronousInstrument<float> *> finstrs{finstr_arr};
+ nostd::span<const float, 1> fvalues{fvalues_arr};
+
+ m.RecordFloatBatch(labelkv, finstrs, fvalues);
+ res = m.Collect();
+ auto float_agg_var = res[0].GetAggregator();
+ auto float_agg = nostd::get<2>(float_agg_var);
+ ASSERT_EQ(float_agg->get_checkpoint()[0], 1.0);
+
+ metrics_api::SynchronousInstrument<double> *dinstr_arr[] = {dcounter.get()};
+ double dvalues_arr[] = {1.0};
+
+ nostd::span<metrics_api::SynchronousInstrument<double> *> dinstrs{dinstr_arr};
+ nostd::span<const double, 1> dvalues{dvalues_arr};
+
+ m.RecordDoubleBatch(labelkv, dinstrs, dvalues);
+ res = m.Collect();
+ auto double_agg_var = res[0].GetAggregator();
+ auto double_agg = nostd::get<3>(double_agg_var);
+ ASSERT_EQ(double_agg->get_checkpoint()[0], 1.0);
+}
+
+TEST(Meter, DisableCollectSync)
+{
+ Meter m("Test");
+ std::map<std::string, std::string> labels = {{"Key", "Value"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ auto c = m.NewShortCounter("c", "", "", false);
+ c->add(1, labelkv);
+ ASSERT_EQ(m.Collect().size(), 0);
+}
+
+TEST(Meter, DisableCollectAsync)
+{
+ Meter m("Test");
+ std::map<std::string, std::string> labels = {{"Key", "Value"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ auto c = m.NewShortValueObserver("c", "", "", false, &ShortCallback);
+ c->observe(1, labelkv);
+ ASSERT_EQ(m.Collect().size(), 0);
+}
+
+TEST(MeterStringUtil, IsValid)
+{
+# if __EXCEPTIONS
+ Meter m("Test");
+ ASSERT_ANY_THROW(m.NewShortCounter("", "Empty name is invalid", " ", true));
+ ASSERT_ANY_THROW(m.NewShortCounter("1a", "Can't begin with a number", " ", true));
+ ASSERT_ANY_THROW(m.NewShortCounter(".a", "Can't begin with punctuation", " ", true));
+ ASSERT_ANY_THROW(m.NewShortCounter(" a", "Can't begin with space", " ", true));
+ ASSERT_ANY_THROW(m.NewShortCounter(
+ "te^ s=%t", "Only alphanumeric ., -, and _ characters are allowed", " ", true));
+# endif
+}
+
+TEST(MeterStringUtil, AlreadyExists)
+{
+# if __EXCEPTIONS
+ Meter m("Test");
+
+ m.NewShortCounter("a", "First instance of instrument named 'a'", "", true);
+ ASSERT_ANY_THROW(m.NewShortCounter("a", "Second (illegal) instrument named 'a'", "", true));
+ ASSERT_ANY_THROW(m.NewShortSumObserver("a", "Still illegal even though it is not a short counter",
+ "", true, &ShortCallback));
+# endif
+}
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/metric_instrument_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/metric_instrument_test.cc
new file mode 100644
index 000000000..28df53fc3
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/metric_instrument_test.cc
@@ -0,0 +1,487 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+
+# include <gtest/gtest.h>
+# include <cstring>
+# include <iostream>
+# include <map>
+# include <memory>
+# include <string>
+# include <thread>
+
+# include "opentelemetry/common/macros.h"
+# include "opentelemetry/sdk/_metrics/async_instruments.h"
+# include "opentelemetry/sdk/_metrics/sync_instruments.h"
+
+namespace metrics_api = opentelemetry::metrics;
+
+# ifdef OPENTELEMETRY_RTTI_ENABLED
+# define METRICS_TEST_TYPE_CAST(TO, FROM) dynamic_cast<TO>(FROM)
+# else
+# define METRICS_TEST_TYPE_CAST(TO, FROM) static_cast<TO>(FROM)
+# endif
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace sdk
+{
+namespace metrics
+{
+
+void ObserverConstructorCallback(metrics_api::ObserverResult<int> result)
+{
+ std::map<std::string, std::string> labels = {{"key", "value"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ result.observe(1, labelkv);
+}
+
+TEST(ApiSdkConversion, async)
+{
+ nostd::shared_ptr<metrics_api::AsynchronousInstrument<int>> alpha =
+ nostd::shared_ptr<metrics_api::AsynchronousInstrument<int>>(
+ new ValueObserver<int>("ankit", "none", "unitles", true, &ObserverConstructorCallback));
+
+ std::map<std::string, std::string> labels = {{"key587", "value264"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+
+ alpha->observe(123456, labelkv);
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(AsynchronousInstrument<int> *, alpha.get())
+ ->GetRecords()[0]
+ .GetLabels(),
+ "{key587:value264}");
+
+ alpha->observe(123456, labelkv);
+ AggregatorVariant canCollect = METRICS_TEST_TYPE_CAST(AsynchronousInstrument<int> *, alpha.get())
+ ->GetRecords()[0]
+ .GetAggregator();
+ EXPECT_EQ(nostd::holds_alternative<std::shared_ptr<Aggregator<short>>>(canCollect), false);
+ EXPECT_EQ(nostd::holds_alternative<std::shared_ptr<Aggregator<int>>>(canCollect), true);
+ EXPECT_EQ(nostd::get<std::shared_ptr<Aggregator<int>>>(canCollect)->get_checkpoint()[0], 123456);
+}
+
+TEST(IntValueObserver, InstrumentFunctions)
+{
+ ValueObserver<int> alpha("enabled", "no description", "unitless", true,
+ &ObserverConstructorCallback);
+ std::map<std::string, std::string> labels = {{"key", "value"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+
+ EXPECT_EQ(alpha.GetName(), "enabled");
+ EXPECT_EQ(alpha.GetDescription(), "no description");
+ EXPECT_EQ(alpha.GetUnits(), "unitless");
+ EXPECT_EQ(alpha.IsEnabled(), true);
+ EXPECT_EQ(alpha.GetKind(), metrics_api::InstrumentKind::ValueObserver);
+
+ alpha.run();
+ EXPECT_EQ(alpha.boundAggregators_[KvToString(labelkv)]->get_values()[0], 1); // min
+}
+
+void ObserverCallback(std::shared_ptr<ValueObserver<int>> in,
+ int freq,
+ const common::KeyValueIterable &labels)
+{
+ for (int i = 0; i < freq; i++)
+ {
+ in->observe(i, labels);
+ }
+}
+
+void NegObserverCallback(std::shared_ptr<ValueObserver<int>> in,
+ int freq,
+ const common::KeyValueIterable &labels)
+{
+ for (int i = 0; i < freq; i++)
+ {
+ in->observe(-i, labels);
+ }
+}
+
+TEST(IntValueObserver, StressObserve)
+{
+ std::shared_ptr<ValueObserver<int>> alpha(new ValueObserver<int>(
+ "enabled", "no description", "unitless", true, &ObserverConstructorCallback));
+
+ std::map<std::string, std::string> labels = {{"key", "value"}};
+ std::map<std::string, std::string> labels1 = {{"key1", "value1"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ auto labelkv1 = common::KeyValueIterableView<decltype(labels1)>{labels1};
+
+ std::thread first(ObserverCallback, alpha, 25,
+ labelkv); // spawn new threads that call the callback
+ std::thread second(ObserverCallback, alpha, 25, labelkv);
+ std::thread third(ObserverCallback, alpha, 25, labelkv1);
+ std::thread fourth(NegObserverCallback, alpha, 25, labelkv1); // negative values
+
+ first.join();
+ second.join();
+ third.join();
+ fourth.join();
+
+ EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv)]->get_values()[0], 0); // min
+ EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv)]->get_values()[1], 24); // max
+ EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv)]->get_values()[2], 600); // sum
+ EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv)]->get_values()[3], 50); // count
+
+ EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv1)]->get_values()[0], -24); // min
+ EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv1)]->get_values()[1], 24); // max
+ EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv1)]->get_values()[2], 0); // sum
+ EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv1)]->get_values()[3], 50); // count
+}
+
+void SumObserverCallback(std::shared_ptr<SumObserver<int>> in,
+ int freq,
+ const common::KeyValueIterable &labels)
+{
+ for (int i = 0; i < freq; i++)
+ {
+ in->observe(1, labels);
+ }
+}
+
+TEST(IntSumObserver, StressObserve)
+{
+ std::shared_ptr<SumObserver<int>> alpha(
+ new SumObserver<int>("test", "none", "unitless", true, &ObserverConstructorCallback));
+
+ std::map<std::string, std::string> labels = {{"key", "value"}};
+ std::map<std::string, std::string> labels1 = {{"key1", "value1"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ auto labelkv1 = common::KeyValueIterableView<decltype(labels1)>{labels1};
+
+ std::thread first(SumObserverCallback, alpha, 100000, labelkv);
+ std::thread second(SumObserverCallback, alpha, 100000, labelkv);
+ std::thread third(SumObserverCallback, alpha, 300000, labelkv1);
+
+ first.join();
+ second.join();
+ third.join();
+
+ EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv)]->get_values()[0], 200000);
+ EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv1)]->get_values()[0], 300000);
+}
+
+void UpDownSumObserverCallback(std::shared_ptr<UpDownSumObserver<int>> in,
+ int freq,
+ const common::KeyValueIterable &labels)
+{
+ for (int i = 0; i < freq; i++)
+ {
+ in->observe(1, labels);
+ }
+}
+
+void NegUpDownSumObserverCallback(std::shared_ptr<UpDownSumObserver<int>> in,
+ int freq,
+ const common::KeyValueIterable &labels)
+{
+ for (int i = 0; i < freq; i++)
+ {
+ in->observe(-1, labels);
+ }
+}
+
+TEST(IntUpDownObserver, StressAdd)
+{
+ std::shared_ptr<UpDownSumObserver<int>> alpha(
+ new UpDownSumObserver<int>("test", "none", "unitless", true, &ObserverConstructorCallback));
+
+ std::map<std::string, std::string> labels = {{"key", "value"}};
+ std::map<std::string, std::string> labels1 = {{"key1", "value1"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ auto labelkv1 = common::KeyValueIterableView<decltype(labels1)>{labels1};
+
+ std::thread first(UpDownSumObserverCallback, alpha, 12340,
+ labelkv); // spawn new threads that call the callback
+ std::thread second(UpDownSumObserverCallback, alpha, 12340, labelkv);
+ std::thread third(UpDownSumObserverCallback, alpha, 56780, labelkv1);
+ std::thread fourth(NegUpDownSumObserverCallback, alpha, 12340, labelkv1); // negative values
+
+ first.join();
+ second.join();
+ third.join();
+ fourth.join();
+
+ EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv)]->get_values()[0], 12340 * 2);
+ EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv1)]->get_values()[0], 56780 - 12340);
+}
+
+TEST(Counter, InstrumentFunctions)
+{
+ Counter<int> alpha("enabled", "no description", "unitless", true);
+ Counter<double> beta("not enabled", "some description", "units", false);
+
+ EXPECT_EQ(static_cast<std::string>(alpha.GetName()), "enabled");
+ EXPECT_EQ(static_cast<std::string>(alpha.GetDescription()), "no description");
+ EXPECT_EQ(static_cast<std::string>(alpha.GetUnits()), "unitless");
+ EXPECT_EQ(alpha.IsEnabled(), true);
+
+ EXPECT_EQ(static_cast<std::string>(beta.GetName()), "not enabled");
+ EXPECT_EQ(static_cast<std::string>(beta.GetDescription()), "some description");
+ EXPECT_EQ(static_cast<std::string>(beta.GetUnits()), "units");
+ EXPECT_EQ(beta.IsEnabled(), false);
+}
+
+TEST(Counter, Binding)
+{
+ Counter<int> alpha("test", "none", "unitless", true);
+
+ std::map<std::string, std::string> labels = {{"key", "value"}};
+ std::map<std::string, std::string> labels1 = {{"key1", "value1"}};
+ std::map<std::string, std::string> labels2 = {{"key2", "value2"}, {"key3", "value3"}};
+ std::map<std::string, std::string> labels3 = {{"key3", "value3"}, {"key2", "value2"}};
+
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ auto labelkv1 = common::KeyValueIterableView<decltype(labels1)>{labels1};
+ auto labelkv2 = common::KeyValueIterableView<decltype(labels2)>{labels2};
+ auto labelkv3 = common::KeyValueIterableView<decltype(labels3)>{labels3};
+
+ auto beta = alpha.bindCounter(labelkv);
+ auto gamma = alpha.bindCounter(labelkv1);
+ auto delta = alpha.bindCounter(labelkv1);
+ auto epsilon = alpha.bindCounter(labelkv1);
+ auto zeta = alpha.bindCounter(labelkv2);
+ auto eta = alpha.bindCounter(labelkv3);
+
+ EXPECT_EQ(beta->get_ref(), 1);
+ EXPECT_EQ(gamma->get_ref(), 3);
+ EXPECT_EQ(eta->get_ref(), 2);
+
+ delta->unbind();
+ gamma->unbind();
+ epsilon->unbind();
+
+ EXPECT_EQ(alpha.boundInstruments_[KvToString(labelkv1)]->get_ref(), 0);
+ EXPECT_EQ(alpha.boundInstruments_.size(), 3);
+}
+
+TEST(Counter, getAggsandnewupdate)
+{
+ Counter<int> alpha("test", "none", "unitless", true);
+
+ std::map<std::string, std::string> labels = {{"key3", "value3"}, {"key2", "value2"}};
+
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ auto beta = alpha.bindCounter(labelkv);
+ beta->add(1);
+ beta->unbind();
+
+ EXPECT_EQ(alpha.boundInstruments_[KvToString(labelkv)]->get_ref(), 0);
+ EXPECT_EQ(alpha.boundInstruments_.size(), 1);
+
+ auto theta = alpha.GetRecords();
+ EXPECT_EQ(theta.size(), 1);
+ EXPECT_EQ(theta[0].GetName(), "test");
+ EXPECT_EQ(theta[0].GetDescription(), "none");
+ EXPECT_EQ(theta[0].GetLabels(), "{key2:value2,key3:value3}");
+}
+
+void CounterCallback(std::shared_ptr<Counter<int>> in,
+ int freq,
+ const common::KeyValueIterable &labels)
+{
+ for (int i = 0; i < freq; i++)
+ {
+ in->add(1, labels);
+ }
+}
+
+TEST(Counter, StressAdd)
+{
+ std::shared_ptr<Counter<int>> alpha(new Counter<int>("test", "none", "unitless", true));
+
+ std::map<std::string, std::string> labels = {{"key", "value"}};
+ std::map<std::string, std::string> labels1 = {{"key1", "value1"}};
+
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ auto labelkv1 = common::KeyValueIterableView<decltype(labels1)>{labels1};
+
+ std::thread first(CounterCallback, alpha, 1000, labelkv);
+ std::thread second(CounterCallback, alpha, 1000, labelkv);
+ std::thread third(CounterCallback, alpha, 3000, labelkv1);
+
+ first.join();
+ second.join();
+ third.join();
+
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundCounter<int> *,
+ alpha->boundInstruments_[KvToString(labelkv)].get())
+ ->GetAggregator()
+ ->get_values()[0],
+ 2000);
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundCounter<int> *,
+ alpha->boundInstruments_[KvToString(labelkv1)].get())
+ ->GetAggregator()
+ ->get_values()[0],
+ 3000);
+}
+
+void UpDownCounterCallback(std::shared_ptr<UpDownCounter<int>> in,
+ int freq,
+ const common::KeyValueIterable &labels)
+{
+ for (int i = 0; i < freq; i++)
+ {
+ in->add(1, labels);
+ }
+}
+
+void NegUpDownCounterCallback(std::shared_ptr<UpDownCounter<int>> in,
+ int freq,
+ const common::KeyValueIterable &labels)
+{
+ for (int i = 0; i < freq; i++)
+ {
+ in->add(-1, labels);
+ }
+}
+
+TEST(IntUpDownCounter, StressAdd)
+{
+ std::shared_ptr<UpDownCounter<int>> alpha(
+ new UpDownCounter<int>("test", "none", "unitless", true));
+
+ std::map<std::string, std::string> labels = {{"key", "value"}};
+ std::map<std::string, std::string> labels1 = {{"key1", "value1"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ auto labelkv1 = common::KeyValueIterableView<decltype(labels1)>{labels1};
+
+ std::thread first(UpDownCounterCallback, alpha, 12340,
+ labelkv); // spawn new threads that call the callback
+ std::thread second(UpDownCounterCallback, alpha, 12340, labelkv);
+ std::thread third(UpDownCounterCallback, alpha, 56780, labelkv1);
+ std::thread fourth(NegUpDownCounterCallback, alpha, 12340, labelkv1); // negative values
+
+ first.join();
+ second.join();
+ third.join();
+ fourth.join();
+
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundUpDownCounter<int> *,
+ alpha->boundInstruments_[KvToString(labelkv)].get())
+ ->GetAggregator()
+ ->get_values()[0],
+ 12340 * 2);
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundUpDownCounter<int> *,
+ alpha->boundInstruments_[KvToString(labelkv1)].get())
+ ->GetAggregator()
+ ->get_values()[0],
+ 56780 - 12340);
+}
+
+void RecorderCallback(std::shared_ptr<ValueRecorder<int>> in,
+ int freq,
+ const common::KeyValueIterable &labels)
+{
+ for (int i = 0; i < freq; i++)
+ {
+ in->record(i, labels);
+ }
+}
+
+void NegRecorderCallback(std::shared_ptr<ValueRecorder<int>> in,
+ int freq,
+ const common::KeyValueIterable &labels)
+{
+ for (int i = 0; i < freq; i++)
+ {
+ in->record(-i, labels);
+ }
+}
+
+TEST(IntValueRecorder, StressRecord)
+{
+ std::shared_ptr<ValueRecorder<int>> alpha(
+ new ValueRecorder<int>("test", "none", "unitless", true));
+
+ std::map<std::string, std::string> labels = {{"key", "value"}};
+ std::map<std::string, std::string> labels1 = {{"key1", "value1"}};
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+ auto labelkv1 = common::KeyValueIterableView<decltype(labels1)>{labels1};
+
+ std::thread first(RecorderCallback, alpha, 25,
+ labelkv); // spawn new threads that call the callback
+ std::thread second(RecorderCallback, alpha, 50, labelkv);
+ std::thread third(RecorderCallback, alpha, 25, labelkv1);
+ std::thread fourth(NegRecorderCallback, alpha, 100, labelkv1); // negative values
+
+ first.join();
+ second.join();
+ third.join();
+ fourth.join();
+
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder<int> *,
+ alpha->boundInstruments_[KvToString(labelkv)].get())
+ ->GetAggregator()
+ ->get_values()[0],
+ 0); // min
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder<int> *,
+ alpha->boundInstruments_[KvToString(labelkv)].get())
+ ->GetAggregator()
+ ->get_values()[1],
+ 49); // max
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder<int> *,
+ alpha->boundInstruments_[KvToString(labelkv)].get())
+ ->GetAggregator()
+ ->get_values()[2],
+ 1525); // sum
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder<int> *,
+ alpha->boundInstruments_[KvToString(labelkv)].get())
+ ->GetAggregator()
+ ->get_values()[3],
+ 75); // count
+
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder<int> *,
+ alpha->boundInstruments_[KvToString(labelkv1)].get())
+ ->GetAggregator()
+ ->get_values()[0],
+ -99); // min
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder<int> *,
+ alpha->boundInstruments_[KvToString(labelkv1)].get())
+ ->GetAggregator()
+ ->get_values()[1],
+ 24); // max
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder<int> *,
+ alpha->boundInstruments_[KvToString(labelkv1)].get())
+ ->GetAggregator()
+ ->get_values()[2],
+ -4650); // sum
+ EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder<int> *,
+ alpha->boundInstruments_[KvToString(labelkv1)].get())
+ ->GetAggregator()
+ ->get_values()[3],
+ 125); // count
+}
+
+TEST(Instruments, NoUpdateNoRecord)
+{
+ // This test verifies that instruments that have received no updates
+ // in the last collection period are not made into records for export.
+
+ Counter<int> alpha("alpha", "no description", "unitless", true);
+
+ std::map<std::string, std::string> labels = {{"key", "value"}};
+
+ auto labelkv = common::KeyValueIterableView<decltype(labels)>{labels};
+
+ EXPECT_EQ(alpha.GetRecords().size(), 0);
+ alpha.add(1, labelkv);
+ EXPECT_EQ(alpha.GetRecords().size(), 1);
+
+ UpDownCounter<int> beta("beta", "no description", "unitless", true);
+
+ EXPECT_EQ(beta.GetRecords().size(), 0);
+ beta.add(1, labelkv);
+ EXPECT_EQ(beta.GetRecords().size(), 1);
+
+ ValueRecorder<int> gamma("gamma", "no description", "unitless", true);
+
+ EXPECT_EQ(gamma.GetRecords().size(), 0);
+ gamma.record(1, labelkv);
+ EXPECT_EQ(gamma.GetRecords().size(), 1);
+}
+
+} // namespace metrics
+} // namespace sdk
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/min_max_sum_count_aggregator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/min_max_sum_count_aggregator_test.cc
new file mode 100644
index 000000000..3b657d955
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/min_max_sum_count_aggregator_test.cc
@@ -0,0 +1,209 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include <gtest/gtest.h>
+# include <thread>
+
+# include "opentelemetry/sdk/_metrics/aggregator/min_max_sum_count_aggregator.h"
+
+using namespace opentelemetry::sdk::metrics;
+namespace metrics_api = opentelemetry::metrics;
+
+TEST(MinMaxSumCountAggregator, Update)
+{
+ // This tests that the aggregator updates the maintained value correctly
+ // after a call to the update() function.
+ MinMaxSumCountAggregator<int> agg(metrics_api::InstrumentKind::Counter);
+ auto value_set = agg.get_values();
+ ASSERT_EQ(value_set[0], 0);
+ ASSERT_EQ(value_set[1], 0);
+ ASSERT_EQ(value_set[2], 0);
+ ASSERT_EQ(value_set[3], 0);
+
+ // 1 + 2 + 3 + ... + 10 = 55
+ for (int i = 1; i <= 10; ++i)
+ {
+ agg.update(i);
+ }
+
+ value_set = agg.get_values();
+ ASSERT_EQ(value_set[0], 1); // min
+ ASSERT_EQ(value_set[1], 10); // max
+ ASSERT_EQ(value_set[2], 55); // sum
+ ASSERT_EQ(value_set[3], 10); // count
+}
+
+TEST(MinMaxSumCountAggregator, FirstUpdate)
+{
+ // This tests that the aggregator appropriately maintains the min and
+ // max values after a single update call.
+ MinMaxSumCountAggregator<int> agg(metrics_api::InstrumentKind::Counter);
+ agg.update(1);
+ auto value_set = agg.get_values();
+ ASSERT_EQ(value_set[0], 1); // min
+ ASSERT_EQ(value_set[1], 1); // max
+ ASSERT_EQ(value_set[2], 1); // sum
+ ASSERT_EQ(value_set[3], 1); // count
+}
+
+TEST(MinMaxSumCountAggregator, Checkpoint)
+{
+ // This test verifies that the default checkpoint is set correctly
+ // and that the checkpoint values update correctly after a call
+ // to the checkpoint() function.
+ MinMaxSumCountAggregator<int> agg(metrics_api::InstrumentKind::Counter);
+
+ // Verify that the default checkpoint is set correctly.
+ auto checkpoint_set = agg.get_checkpoint();
+ ASSERT_EQ(checkpoint_set[0], 0); // min
+ ASSERT_EQ(checkpoint_set[1], 0); // max
+ ASSERT_EQ(checkpoint_set[2], 0); // sum
+ ASSERT_EQ(checkpoint_set[3], 0); // count
+
+ // 1 + 2 + 3 + ... + 10 = 55
+ for (int i = 1; i <= 10; ++i)
+ {
+ agg.update(i);
+ }
+
+ agg.checkpoint();
+
+ // Verify that the checkpoint values were updated.
+ checkpoint_set = agg.get_checkpoint();
+ ASSERT_EQ(checkpoint_set[0], 1); // min
+ ASSERT_EQ(checkpoint_set[1], 10); // max
+ ASSERT_EQ(checkpoint_set[2], 55); // sum
+ ASSERT_EQ(checkpoint_set[3], 10); // count
+
+ // Verify that the current values were reset to the default state.
+ auto value_set = agg.get_values();
+ ASSERT_EQ(value_set[0], 0); // min
+ ASSERT_EQ(value_set[1], 0); // max
+ ASSERT_EQ(value_set[2], 0); // sum
+ ASSERT_EQ(value_set[3], 0); // count
+}
+
+TEST(MinMaxSumCountAggregator, Merge)
+{
+ // This tests that the values_ vector is updated correctly after
+ // two aggregators are merged together.
+ MinMaxSumCountAggregator<int> agg1(metrics_api::InstrumentKind::Counter);
+ MinMaxSumCountAggregator<int> agg2(metrics_api::InstrumentKind::Counter);
+
+ // 1 + 2 + 3 + ... + 10 = 55
+ for (int i = 1; i <= 10; ++i)
+ {
+ agg1.update(i);
+ }
+
+ // 1 + 2 + 3 + ... + 20 = 210
+ for (int i = 1; i <= 20; ++i)
+ {
+ agg2.update(i);
+ }
+
+ agg1.merge(agg2);
+
+ // Verify that the current values were changed by the merge.
+ auto value_set = agg1.get_values();
+ ASSERT_EQ(value_set[0], 1); // min
+ ASSERT_EQ(value_set[1], 20); // max
+ ASSERT_EQ(value_set[2], 265); // sum
+ ASSERT_EQ(value_set[3], 30); // count
+}
+
+TEST(MinMaxSumCountAggregator, BadMerge)
+{
+ // This verifies that we encounter and error when we try to merge
+ // two aggregators of different numeric types together.
+ MinMaxSumCountAggregator<int> agg1(metrics_api::InstrumentKind::Counter);
+ MinMaxSumCountAggregator<int> agg2(metrics_api::InstrumentKind::ValueRecorder);
+
+ agg1.update(1);
+ agg2.update(2);
+
+ agg1.merge(agg2);
+
+ // Verify that the values did NOT merge
+ auto value_set = agg1.get_values();
+ ASSERT_EQ(value_set[0], 1); // min
+ ASSERT_EQ(value_set[0], 1); // max
+ ASSERT_EQ(value_set[0], 1); // sum
+ ASSERT_EQ(value_set[0], 1); // count
+}
+
+TEST(MinMaxSumCountAggregator, Types)
+{
+ // This test verifies that we do not encounter any errors when
+ // using various numeric types.
+ MinMaxSumCountAggregator<int> agg_int(metrics_api::InstrumentKind::Counter);
+ MinMaxSumCountAggregator<long> agg_long(metrics_api::InstrumentKind::Counter);
+ MinMaxSumCountAggregator<float> agg_float(metrics_api::InstrumentKind::Counter);
+ MinMaxSumCountAggregator<double> agg_double(metrics_api::InstrumentKind::Counter);
+
+ for (int i = 1; i <= 10; ++i)
+ {
+ agg_int.update(i);
+ agg_long.update(i);
+ }
+
+ for (float i = 1.0; i <= 10.0; i += 1)
+ {
+ agg_float.update(i);
+ agg_double.update(i);
+ }
+
+ auto value_set = agg_int.get_values();
+ ASSERT_EQ(value_set[0], 1); // min
+ ASSERT_EQ(value_set[1], 10); // max
+ ASSERT_EQ(value_set[2], 55); // sum
+ ASSERT_EQ(value_set[3], 10); // count
+
+ auto value_set2 = agg_long.get_values();
+ ASSERT_EQ(value_set[0], 1); // min
+ ASSERT_EQ(value_set[1], 10); // max
+ ASSERT_EQ(value_set[2], 55); // sum
+ ASSERT_EQ(value_set[3], 10); // count
+
+ auto value_set3 = agg_float.get_values();
+ ASSERT_EQ(value_set[0], 1.0); // min
+ ASSERT_EQ(value_set[1], 10.0); // max
+ ASSERT_EQ(value_set[2], 55.0); // sum
+ ASSERT_EQ(value_set[3], 10); // count
+
+ auto value_set4 = agg_double.get_values();
+ ASSERT_EQ(value_set[0], 1.0); // min
+ ASSERT_EQ(value_set[1], 10.0); // max
+ ASSERT_EQ(value_set[2], 55.0); // sum
+ ASSERT_EQ(value_set[3], 10); // count
+}
+
+static void callback(MinMaxSumCountAggregator<int> &agg)
+{
+ // 1 + 2 + ... + 10000 = 50005000
+ for (int i = 1; i <= 10000; ++i)
+ {
+ agg.update(i);
+ }
+}
+
+TEST(MinMaxSumCountAggregator, Concurrency)
+{
+ // This test checks that the aggregator updates appropriately
+ // when called in a multi-threaded context.
+ MinMaxSumCountAggregator<int> agg(metrics_api::InstrumentKind::Counter);
+
+ std::thread first(&callback, std::ref(agg));
+ std::thread second(&callback, std::ref(agg));
+
+ first.join();
+ second.join();
+
+ auto value_set = agg.get_values();
+ ASSERT_EQ(value_set[0], 1);
+ ASSERT_EQ(value_set[1], 10000);
+ ASSERT_EQ(value_set[2], 2 * 50005000);
+ ASSERT_EQ(value_set[3], 2 * 10000);
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/sketch_aggregator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/sketch_aggregator_test.cc
new file mode 100644
index 000000000..6dc2fbef3
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/sketch_aggregator_test.cc
@@ -0,0 +1,254 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/_metrics/aggregator/sketch_aggregator.h"
+
+# include <gtest/gtest.h>
+# include <iostream>
+# include <numeric>
+# include <thread>
+
+namespace metrics_api = opentelemetry::metrics;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace sdk
+{
+namespace metrics
+{
+
+// Test updating with a uniform set of updates
+TEST(Sketch, UniformValues)
+{
+ SketchAggregator<int> alpha(metrics_api::InstrumentKind::ValueRecorder, .000005);
+
+ EXPECT_EQ(alpha.get_aggregator_kind(), AggregatorKind::Sketch);
+
+ alpha.checkpoint();
+ EXPECT_EQ(alpha.get_checkpoint().size(), 2);
+ EXPECT_EQ(alpha.get_boundaries().size(), 0);
+ EXPECT_EQ(alpha.get_counts().size(), 0);
+
+ for (int i = 0; i < 60; i++)
+ {
+ alpha.update(i);
+ }
+
+ alpha.checkpoint();
+
+ EXPECT_EQ(alpha.get_boundaries().size(), 60);
+ EXPECT_EQ(alpha.get_counts().size(), 60);
+
+ EXPECT_EQ(alpha.get_checkpoint()[0], 1770);
+ EXPECT_EQ(alpha.get_checkpoint()[1], 60);
+}
+
+// Test updating with a normal distribution
+TEST(Sketch, NormalValues)
+{
+ SketchAggregator<int> alpha(metrics_api::InstrumentKind::ValueRecorder, .0005);
+
+ std::vector<int> vals{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13};
+ for (int i : vals)
+ {
+ alpha.update(i);
+ }
+ alpha.checkpoint();
+
+ EXPECT_EQ(alpha.get_checkpoint()[0], std::accumulate(vals.begin(), vals.end(), 0));
+ EXPECT_EQ(alpha.get_checkpoint()[1], vals.size());
+
+ std::vector<int> correct = {1, 2, 3, 4, 3, 2, 1};
+ EXPECT_EQ(alpha.get_counts(), correct);
+
+ std::vector<double> captured_bounds = alpha.get_boundaries();
+ for (size_t i = 0; i < captured_bounds.size(); i++)
+ {
+ captured_bounds[i] = round(captured_bounds[i]);
+ }
+
+ // It is not guaranteed that bounds are correct once the bucket sizes pass 1000
+ std::vector<double> correct_bounds = {1, 3, 5, 7, 9, 11, 13};
+ EXPECT_EQ(captured_bounds, correct_bounds);
+}
+
+int randVal()
+{
+ return rand() % 100000;
+}
+
+/** Note that in this case, "Large" refers to a number of distinct values which exceed the maximum
+ * number of allowed buckets.
+ */
+TEST(Sketch, QuantileSmall)
+{
+ SketchAggregator<int> alpha(metrics_api::InstrumentKind::ValueRecorder, .00005);
+
+ std::vector<int> vals1(2048);
+ std::generate(vals1.begin(), vals1.end(), randVal);
+
+ std::vector<int> vals2(2048);
+ std::generate(vals1.begin(), vals1.end(), randVal);
+
+ for (int i : vals1)
+ {
+ alpha.update(i);
+ }
+ alpha.checkpoint();
+ std::sort(vals1.begin(), vals1.end());
+
+ EXPECT_TRUE(abs(alpha.get_quantiles(.25) - vals1[2048 * .25 - 1]) <= 10);
+ EXPECT_TRUE(abs(alpha.get_quantiles(.50) - vals1[2048 * .50 - 1]) <= 10);
+ EXPECT_TRUE(abs(alpha.get_quantiles(.75) - vals1[2048 * .75 - 1]) <= 10);
+}
+
+TEST(Sketch, UpdateQuantileLarge)
+{
+ SketchAggregator<int> alpha(metrics_api::InstrumentKind::ValueRecorder, .0005, 7);
+ std::vector<int> vals{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13};
+ for (int i : vals)
+ {
+ alpha.update(i);
+ }
+
+ // This addition should trigger the "1" and "3" buckets to merge
+ alpha.update(15);
+ alpha.checkpoint();
+
+ std::vector<int> correct = {3, 3, 4, 3, 2, 1, 1};
+ EXPECT_EQ(alpha.get_counts(), correct);
+
+ for (int i : vals)
+ {
+ alpha.update(i);
+ }
+ alpha.update(15);
+ alpha.update(17);
+ alpha.checkpoint();
+
+ correct = {6, 4, 3, 2, 1, 1, 1};
+ EXPECT_EQ(alpha.get_counts(), correct);
+}
+
+TEST(Sketch, MergeSmall)
+{
+ SketchAggregator<int> alpha(metrics_api::InstrumentKind::ValueRecorder, .0005);
+ SketchAggregator<int> beta(metrics_api::InstrumentKind::ValueRecorder, .0005);
+
+ std::vector<int> vals{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13};
+ for (int i : vals)
+ {
+ alpha.update(i);
+ }
+
+ std::vector<int> otherVals{1, 1, 1, 1, 11, 11, 13, 13, 13, 15};
+ for (int i : otherVals)
+ {
+ beta.update(i);
+ }
+
+ alpha.merge(beta);
+ alpha.checkpoint();
+
+ EXPECT_EQ(alpha.get_checkpoint()[0], std::accumulate(vals.begin(), vals.end(), 0) +
+ std::accumulate(otherVals.begin(), otherVals.end(), 0));
+ EXPECT_EQ(alpha.get_checkpoint()[1], vals.size() + otherVals.size());
+
+ std::vector<int> correct = {5, 2, 3, 4, 3, 4, 4, 1};
+ EXPECT_EQ(alpha.get_counts(), correct);
+}
+
+TEST(Sketch, MergeLarge)
+{
+ SketchAggregator<int> alpha(metrics_api::InstrumentKind::ValueRecorder, .0005, 7);
+ SketchAggregator<int> beta(metrics_api::InstrumentKind::ValueRecorder, .0005, 7);
+
+ std::vector<int> vals{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13};
+ for (int i : vals)
+ {
+ alpha.update(i);
+ }
+
+ std::vector<int> otherVals{1, 1, 1, 1, 11, 11, 13, 13, 13, 15};
+ for (int i : otherVals)
+ {
+ beta.update(i);
+ }
+
+ alpha.merge(beta);
+ alpha.checkpoint();
+
+ EXPECT_EQ(alpha.get_checkpoint()[0], std::accumulate(vals.begin(), vals.end(), 0) +
+ std::accumulate(otherVals.begin(), otherVals.end(), 0));
+ EXPECT_EQ(alpha.get_checkpoint()[1], vals.size() + otherVals.size());
+
+ std::vector<int> correct = {7, 3, 4, 3, 4, 4, 1};
+ EXPECT_EQ(alpha.get_counts(), correct);
+}
+
+// Update callback used to validate multi-threaded performance
+void sketchUpdateCallback(Aggregator<int> &agg, std::vector<int> vals)
+{
+ for (int i : vals)
+ {
+ agg.update(i);
+ }
+}
+
+TEST(Sketch, Concurrency)
+{
+ SketchAggregator<int> alpha(metrics_api::InstrumentKind::ValueRecorder, .0005, 20);
+
+ std::vector<int> vals1(1000);
+ std::generate(vals1.begin(), vals1.end(), randVal);
+
+ std::vector<int> vals2(1000);
+ std::generate(vals2.begin(), vals2.end(), randVal);
+
+ std::thread first(sketchUpdateCallback, std::ref(alpha), vals1);
+ std::thread second(sketchUpdateCallback, std::ref(alpha), vals2);
+
+ first.join();
+ second.join();
+
+ SketchAggregator<int> beta(metrics_api::InstrumentKind::ValueRecorder, .0005, 20);
+
+ for (int i : vals1)
+ {
+ beta.update(i);
+ }
+ for (int i : vals2)
+ {
+ beta.update(i);
+ }
+
+ alpha.checkpoint();
+ beta.checkpoint();
+
+ EXPECT_EQ(alpha.get_checkpoint(), beta.get_checkpoint());
+ EXPECT_EQ(alpha.get_counts(), beta.get_counts());
+ EXPECT_EQ(alpha.get_boundaries(), beta.get_boundaries());
+}
+
+# if __EXCEPTIONS
+
+TEST(Sketch, Errors)
+{
+
+ SketchAggregator<int> tol1(metrics_api::InstrumentKind::ValueRecorder, .000005);
+ SketchAggregator<int> tol2(metrics_api::InstrumentKind::ValueRecorder, .005);
+ SketchAggregator<int> sz1(metrics_api::InstrumentKind::ValueRecorder, .000005, 2938);
+ SketchAggregator<int> sz2(metrics_api::InstrumentKind::ValueRecorder, .000005);
+
+ EXPECT_ANY_THROW(tol1.merge(tol2));
+ EXPECT_ANY_THROW(sz1.merge(sz2));
+ EXPECT_ANY_THROW(tol1.get_quantiles(-.000001));
+ EXPECT_ANY_THROW(tol1.get_quantiles(1.000001));
+}
+
+# endif
+
+} // namespace metrics
+} // namespace sdk
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/ungrouped_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/ungrouped_processor_test.cc
new file mode 100644
index 000000000..934bfe23e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/ungrouped_processor_test.cc
@@ -0,0 +1,601 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/_metrics/ungrouped_processor.h"
+# include <gtest/gtest.h>
+# include "opentelemetry/nostd/shared_ptr.h"
+# include "opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h"
+
+namespace metric_sdk = opentelemetry::sdk::metrics;
+namespace metrics_api = opentelemetry::metrics;
+namespace nostd = opentelemetry::nostd;
+
+/* Test that CheckpointSelf() will return the amount of unique records in it, then
+ call FinishedCollection and see the map reset */
+TEST(UngroupedMetricsProcessor, UngroupedProcessorFinishedCollectionStateless)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(false));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::CounterAggregator<double>(metrics_api::InstrumentKind::Counter));
+
+ auto aggregator2 = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::CounterAggregator<double>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(5.5);
+ aggregator->checkpoint();
+
+ aggregator2->update(500.4);
+ aggregator2->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+ // Must have different (name, description, label, instrument) to map to
+ metric_sdk::Record r2("name2", "description2", "labels2", aggregator2);
+
+ processor->process(r);
+ processor->process(r2);
+
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint.size(), 2);
+
+ processor->FinishedCollection();
+
+ checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint.size(), 0);
+}
+
+/* Test that CheckpointSelf() will return the amount of unique records in it, then
+ call FinishedCollection and see the map stay the same */
+TEST(UngroupedMetricsProcessor, UngroupedProcessorFinishedCollectionStateful)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(true));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::CounterAggregator<double>(metrics_api::InstrumentKind::Counter));
+
+ auto aggregator2 = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::CounterAggregator<double>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(5.5);
+ aggregator->checkpoint();
+
+ aggregator2->update(500.4);
+ aggregator2->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+ // Must have different (name, description, label, instrument) to map to
+ metric_sdk::Record r2("name2", "description2", "labels2", aggregator2);
+
+ processor->process(r);
+ processor->process(r2);
+
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint.size(), 2);
+
+ processor->FinishedCollection();
+
+ ASSERT_EQ(checkpoint.size(), 2);
+}
+
+// Test to make sure we keep information from record(short) that goes through process()
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatelessShort)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(false));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<short>>(
+ new metric_sdk::CounterAggregator<short>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(4);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<short>>>(checkpoint[0].GetAggregator()),
+ aggregator);
+}
+
+// Test to make sure we keep information from record(int) that goes through process()
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatelessInt)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(false));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::CounterAggregator<int>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(5);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint[0].GetAggregator()),
+ aggregator);
+}
+
+// Test to make sure we keep information from record(float) that goes through process()
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatelessFloat)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(false));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<float>>(
+ new metric_sdk::CounterAggregator<float>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(8.5);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<float>>>(checkpoint[0].GetAggregator()),
+ aggregator);
+}
+
+// Test to make sure we keep information from record(double) that goes through process()
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatelessDouble)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(false));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::CounterAggregator<double>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(5.5);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(checkpoint[0].GetAggregator()),
+ aggregator);
+}
+
+/**
+ * The following tests are for the Stateful version of the processor. These tests will make sure
+ * that when we send the same aggreagtor twice through process(), that the values will be merged.
+ * We can easily recreate this expected value by making a test aggregator that is updated through
+ * both process functions but only checkpointed at the end.
+ */
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulShort)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(true));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<short>>(
+ new metric_sdk::CounterAggregator<short>(metrics_api::InstrumentKind::Counter));
+
+ auto aggregator_test = std::shared_ptr<metric_sdk::Aggregator<short>>(
+ new metric_sdk::CounterAggregator<short>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(5);
+ aggregator_test->update(5);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<short>>>(checkpoint[0].GetAggregator())
+ ->get_checkpoint(),
+ aggregator->get_checkpoint());
+
+ aggregator->update(4);
+ aggregator_test->update(4);
+ aggregator->checkpoint();
+ aggregator_test->checkpoint();
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint2 = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint2.size(), 1);
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<short>>>(checkpoint[0].GetAggregator())
+ ->get_checkpoint()[0],
+ aggregator_test->get_checkpoint()[0]);
+}
+
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulInt)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(true));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::CounterAggregator<int>(metrics_api::InstrumentKind::Counter));
+
+ auto aggregator_test = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::CounterAggregator<int>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(5);
+ aggregator_test->update(5);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint[0].GetAggregator())
+ ->get_checkpoint(),
+ aggregator->get_checkpoint());
+
+ aggregator->update(4);
+ aggregator_test->update(4);
+ aggregator->checkpoint();
+ aggregator_test->checkpoint();
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint2 = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint2.size(), 1);
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint[0].GetAggregator())
+ ->get_checkpoint()[0],
+ aggregator_test->get_checkpoint()[0]);
+}
+
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulFloat)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(true));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<float>>(
+ new metric_sdk::CounterAggregator<float>(metrics_api::InstrumentKind::Counter));
+
+ auto aggregator_test = std::shared_ptr<metric_sdk::Aggregator<float>>(
+ new metric_sdk::CounterAggregator<float>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(5);
+ aggregator_test->update(5);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<float>>>(checkpoint[0].GetAggregator())
+ ->get_checkpoint(),
+ aggregator->get_checkpoint());
+
+ aggregator->update(4);
+ aggregator_test->update(4);
+ aggregator->checkpoint();
+ aggregator_test->checkpoint();
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint2 = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint2.size(), 1);
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<float>>>(checkpoint[0].GetAggregator())
+ ->get_checkpoint()[0],
+ aggregator_test->get_checkpoint()[0]);
+}
+
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulDouble)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(true));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::CounterAggregator<double>(metrics_api::InstrumentKind::Counter));
+
+ auto aggregator_test = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::CounterAggregator<double>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(5.5);
+ aggregator_test->update(5.5);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(checkpoint[0].GetAggregator())
+ ->get_checkpoint(),
+ aggregator->get_checkpoint());
+
+ aggregator->update(4.4);
+ aggregator_test->update(4.4);
+ aggregator->checkpoint();
+ aggregator_test->checkpoint();
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint2 = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint2.size(), 1);
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(checkpoint[0].GetAggregator())
+ ->get_checkpoint()[0],
+ aggregator_test->get_checkpoint()[0]);
+}
+
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulMinMaxSumCount)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(true));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::MinMaxSumCountAggregator<double>(metrics_api::InstrumentKind::Counter));
+
+ auto aggregator2 = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::MinMaxSumCountAggregator<double>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(1.1);
+ aggregator->update(2.2);
+ aggregator2->update(1.1);
+ aggregator2->update(2.2);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(checkpoint[0].GetAggregator())
+ ->get_checkpoint(),
+ aggregator->get_checkpoint());
+
+ aggregator->update(5.5);
+ aggregator2->update(5.5);
+ aggregator->checkpoint();
+ aggregator2->checkpoint();
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint2 = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint2.size(), 1);
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(checkpoint2[0].GetAggregator())
+ ->get_checkpoint(),
+ aggregator2->get_checkpoint());
+}
+
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulGauge)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(true));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::GaugeAggregator<double>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(1.1);
+ aggregator->update(2.2);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(checkpoint[0].GetAggregator())
+ ->get_checkpoint(),
+ aggregator->get_checkpoint());
+
+ aggregator->update(5.4);
+ aggregator->checkpoint();
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint2 = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint2.size(), 1);
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(checkpoint2[0].GetAggregator())
+ ->get_checkpoint(),
+ aggregator->get_checkpoint());
+}
+
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulExact)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(true));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::ExactAggregator<double>(metrics_api::InstrumentKind::Counter, false));
+
+ auto aggregator2 = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::ExactAggregator<double>(metrics_api::InstrumentKind::Counter, false));
+
+ aggregator->update(1.1);
+ aggregator->update(2.2);
+ aggregator2->update(1.1);
+ aggregator2->update(2.2);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(checkpoint[0].GetAggregator())
+ ->get_checkpoint(),
+ aggregator->get_checkpoint());
+
+ aggregator->update(5.4);
+ aggregator2->update(5.4);
+ aggregator->checkpoint();
+ aggregator2->checkpoint();
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint2 = processor->CheckpointSelf();
+
+ ASSERT_EQ(checkpoint2.size(), 1);
+ ASSERT_EQ(
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(checkpoint2[0].GetAggregator())
+ ->get_checkpoint(),
+ aggregator2->get_checkpoint());
+}
+
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulHistogram)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(true));
+
+ std::vector<double> boundaries{10, 20, 30, 40, 50};
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::HistogramAggregator<int>(metrics_api::InstrumentKind::Counter, boundaries));
+
+ auto aggregator2 = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::HistogramAggregator<int>(metrics_api::InstrumentKind::Counter, boundaries));
+
+ for (int i = 0; i < 60; i++)
+ {
+ aggregator->update(i);
+ aggregator2->update(i);
+ }
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint[0].GetAggregator())
+ ->get_boundaries(),
+ aggregator->get_boundaries());
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint[0].GetAggregator())
+ ->get_counts(),
+ aggregator->get_counts());
+
+ for (int i = 0; i < 60; i++)
+ {
+ aggregator->update(i);
+ aggregator2->update(i);
+ }
+ aggregator->checkpoint();
+ aggregator2->checkpoint();
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint2 = processor->CheckpointSelf();
+
+ ASSERT_EQ(checkpoint2.size(), 1);
+ ASSERT_EQ(checkpoint2[0].GetName(), "name");
+ ASSERT_EQ(checkpoint2[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint2[0].GetDescription(), "description");
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint2[0].GetAggregator())
+ ->get_boundaries(),
+ aggregator->get_boundaries());
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint2[0].GetAggregator())
+ ->get_counts(),
+ aggregator2->get_counts());
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint2[0].GetAggregator())
+ ->get_checkpoint(),
+ aggregator2->get_checkpoint());
+}
+
+TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulSketch)
+{
+ auto processor = std::unique_ptr<metric_sdk::MetricsProcessor>(
+ new metric_sdk::UngroupedMetricsProcessor(true));
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::SketchAggregator<int>(metrics_api::InstrumentKind::Counter, .00005));
+
+ auto test_aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::SketchAggregator<int>(metrics_api::InstrumentKind::Counter, .00005));
+
+ for (int i = 0; i < 60; i++)
+ {
+ aggregator->update(i);
+ test_aggregator->update(i);
+ }
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint[0].GetName(), "name");
+ ASSERT_EQ(checkpoint[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint[0].GetDescription(), "description");
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint[0].GetAggregator())
+ ->get_boundaries(),
+ aggregator->get_boundaries());
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint[0].GetAggregator())
+ ->get_counts(),
+ aggregator->get_counts());
+
+ for (int i = 0; i < 60; i++)
+ {
+ aggregator->update(i);
+ test_aggregator->update(i);
+ }
+ aggregator->checkpoint();
+ test_aggregator->checkpoint();
+
+ processor->process(r);
+
+ std::vector<metric_sdk::Record> checkpoint2 = processor->CheckpointSelf();
+ ASSERT_EQ(checkpoint2[0].GetName(), "name");
+ ASSERT_EQ(checkpoint2[0].GetLabels(), "labels");
+ ASSERT_EQ(checkpoint2[0].GetDescription(), "description");
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint2[0].GetAggregator())
+ ->get_boundaries(),
+ test_aggregator->get_boundaries());
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint2[0].GetAggregator())
+ ->get_counts(),
+ test_aggregator->get_counts());
+ ASSERT_EQ(nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(checkpoint2[0].GetAggregator())
+ ->get_checkpoint(),
+ test_aggregator->get_checkpoint());
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/BUILD
new file mode 100644
index 000000000..91d56996f
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/BUILD
@@ -0,0 +1,153 @@
+load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark")
+
+cc_test(
+ name = "random_test",
+ srcs = [
+ "random_test.cc",
+ ],
+ tags = ["test"],
+ deps = [
+ "//sdk/src/common:random",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "fast_random_number_generator_test",
+ srcs = [
+ "fast_random_number_generator_test.cc",
+ ],
+ tags = ["test"],
+ deps = [
+ "//sdk/src/common:random",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+otel_cc_benchmark(
+ name = "random_benchmark",
+ srcs = ["random_benchmark.cc"],
+ tags = ["test"],
+ deps = ["//sdk/src/common:random"],
+)
+
+cc_test(
+ name = "atomic_unique_ptr_test",
+ srcs = [
+ "atomic_unique_ptr_test.cc",
+ ],
+ tags = ["test"],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "circular_buffer_range_test",
+ srcs = [
+ "circular_buffer_range_test.cc",
+ ],
+ tags = ["test"],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "random_fork_test",
+ srcs = [
+ "random_fork_test.cc",
+ ],
+ tags = ["test"],
+ deps = [
+ "//sdk/src/common:random",
+ ],
+)
+
+cc_library(
+ name = "baseline_circular_buffer",
+ hdrs = [
+ "baseline_circular_buffer.h",
+ ],
+ include_prefix = "test/common",
+ deps = [
+ "//api",
+ ],
+)
+
+otel_cc_benchmark(
+ name = "circular_buffer_benchmark",
+ srcs = ["circular_buffer_benchmark.cc"],
+ tags = ["test"],
+ deps = [
+ ":baseline_circular_buffer",
+ "//sdk:headers",
+ ],
+)
+
+cc_test(
+ name = "empty_attributes_test",
+ srcs = [
+ "empty_attributes_test.cc",
+ ],
+ tags = ["test"],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "attribute_utils_test",
+ srcs = [
+ "attribute_utils_test.cc",
+ ],
+ tags = ["test"],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "global_log_handle_test",
+ srcs = [
+ "global_log_handle_test.cc",
+ ],
+ tags = ["test"],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ "//sdk/src/common:global_log_handler",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "attributemap_hash_test",
+ srcs = [
+ "attributemap_hash_test.cc",
+ ],
+ tags = ["test"],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+otel_cc_benchmark(
+ name = "attributemap_hash_benchmark",
+ srcs = ["attributemap_hash_benchmark.cc"],
+ tags = ["test"],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/CMakeLists.txt
new file mode 100644
index 000000000..0fefc86b1
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/CMakeLists.txt
@@ -0,0 +1,37 @@
+foreach(
+ testname
+ random_test
+ fast_random_number_generator_test
+ atomic_unique_ptr_test
+ circular_buffer_range_test
+ circular_buffer_test
+ attribute_utils_test
+ attributemap_hash_test
+ global_log_handle_test)
+
+ add_executable(${testname} "${testname}.cc")
+ target_link_libraries(
+ ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ opentelemetry_common opentelemetry_trace)
+
+ gtest_add_tests(
+ TARGET ${testname}
+ TEST_PREFIX trace.
+ TEST_LIST ${testname})
+endforeach()
+
+add_executable(random_fork_test random_fork_test.cc)
+target_link_libraries(random_fork_test opentelemetry_common)
+add_test(random_fork_test random_fork_test)
+
+add_executable(random_benchmark random_benchmark.cc)
+target_link_libraries(random_benchmark benchmark::benchmark
+ ${CMAKE_THREAD_LIBS_INIT} opentelemetry_common)
+
+add_executable(circular_buffer_benchmark circular_buffer_benchmark.cc)
+target_link_libraries(circular_buffer_benchmark benchmark::benchmark
+ ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api)
+
+add_executable(attributemap_hash_benchmark attributemap_hash_benchmark.cc)
+target_link_libraries(attributemap_hash_benchmark benchmark::benchmark
+ ${CMAKE_THREAD_LIBS_INIT} opentelemetry_common)
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/atomic_unique_ptr_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/atomic_unique_ptr_test.cc
new file mode 100644
index 000000000..aa6d88a00
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/atomic_unique_ptr_test.cc
@@ -0,0 +1,42 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/common/atomic_unique_ptr.h"
+
+#include <gtest/gtest.h>
+using opentelemetry::sdk::common::AtomicUniquePtr;
+
+TEST(AtomicUniquePtrTest, SwapIfNullWithNull)
+{
+ AtomicUniquePtr<int> ptr;
+ EXPECT_TRUE(ptr.IsNull());
+
+ std::unique_ptr<int> x{new int{33}};
+ EXPECT_TRUE(ptr.SwapIfNull(x));
+ EXPECT_EQ(x, nullptr);
+}
+
+TEST(AtomicUniquePtrTest, SwapIfNullWithNonNull)
+{
+ AtomicUniquePtr<int> ptr;
+ ptr.Reset(new int{11});
+ std::unique_ptr<int> x{new int{33}};
+ EXPECT_TRUE(!ptr.SwapIfNull(x));
+ EXPECT_NE(x, nullptr);
+ EXPECT_EQ(*x, 33);
+ EXPECT_EQ(*ptr, 11);
+}
+
+TEST(AtomicUniquePtrTest, Swap)
+{
+ AtomicUniquePtr<int> ptr;
+ EXPECT_TRUE(ptr.IsNull());
+
+ ptr.Reset(new int{11});
+ std::unique_ptr<int> x{new int{33}};
+ ptr.Swap(x);
+ EXPECT_FALSE(ptr.IsNull());
+ EXPECT_NE(x, nullptr);
+ EXPECT_EQ(*x, 11);
+ EXPECT_EQ(*ptr, 33);
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attribute_utils_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attribute_utils_test.cc
new file mode 100644
index 000000000..b7ef17244
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attribute_utils_test.cc
@@ -0,0 +1,53 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/common/attribute_utils.h"
+
+#include <gtest/gtest.h>
+
+TEST(AttributeMapTest, DefaultConstruction)
+{
+
+ opentelemetry::sdk::common::AttributeMap attribute_map;
+ EXPECT_EQ(attribute_map.GetAttributes().size(), 0);
+}
+
+TEST(OrderedAttributeMapTest, DefaultConstruction)
+{
+ opentelemetry::sdk::common::OrderedAttributeMap attribute_map;
+ EXPECT_EQ(attribute_map.GetAttributes().size(), 0);
+}
+
+TEST(AttributeMapTest, AttributesConstruction)
+{
+ const int kNumAttributes = 3;
+ std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3"};
+ int values[kNumAttributes] = {15, 24, 37};
+ std::map<std::string, int> attributes = {
+ {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}};
+
+ opentelemetry::common::KeyValueIterableView<std::map<std::string, int>> iterable(attributes);
+ opentelemetry::sdk::common::AttributeMap attribute_map(iterable);
+
+ for (int i = 0; i < kNumAttributes; i++)
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<int>(attribute_map.GetAttributes().at(keys[i])), values[i]);
+ }
+}
+
+TEST(OrderedAttributeMapTest, AttributesConstruction)
+{
+ const int kNumAttributes = 3;
+ std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3"};
+ int values[kNumAttributes] = {15, 24, 37};
+ std::map<std::string, int> attributes = {
+ {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}};
+
+ opentelemetry::common::KeyValueIterableView<std::map<std::string, int>> iterable(attributes);
+ opentelemetry::sdk::common::OrderedAttributeMap attribute_map(iterable);
+
+ for (int i = 0; i < kNumAttributes; i++)
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<int>(attribute_map.GetAttributes().at(keys[i])), values[i]);
+ }
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_benchmark.cc
new file mode 100644
index 000000000..811ecb23d
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_benchmark.cc
@@ -0,0 +1,22 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <benchmark/benchmark.h>
+#include "opentelemetry/sdk/common/attributemap_hash.h"
+
+using namespace opentelemetry::sdk::common;
+namespace
+{
+void BM_AttributeMapHash(benchmark::State &state)
+{
+ OrderedAttributeMap map1 = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}, {"k4", "v4"},
+ {"k5", true}, {"k6", 12}, {"k7", 12.209}};
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(GetHashForAttributeMap(map1));
+ }
+}
+BENCHMARK(BM_AttributeMapHash);
+
+} // namespace
+BENCHMARK_MAIN();
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_test.cc
new file mode 100644
index 000000000..7d2748670
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_test.cc
@@ -0,0 +1,32 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/common/attributemap_hash.h"
+#include <gtest/gtest.h>
+
+using namespace opentelemetry::sdk::common;
+TEST(AttributeMapHashTest, BasicTests)
+{
+ {
+ OrderedAttributeMap map1 = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}};
+ OrderedAttributeMap map2 = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}, {"k4", "v4"}};
+ OrderedAttributeMap map3 = {{"k3", "v3"}, {"k1", "v1"}, {"k2", "v2"}};
+
+ EXPECT_TRUE(GetHashForAttributeMap(map1) != 0);
+ EXPECT_TRUE(GetHashForAttributeMap(map1) == GetHashForAttributeMap(map1));
+ EXPECT_TRUE(GetHashForAttributeMap(map1) != GetHashForAttributeMap(map2));
+ EXPECT_TRUE(GetHashForAttributeMap(map1) == GetHashForAttributeMap(map3));
+ }
+
+ {
+ OrderedAttributeMap map1 = {{"k1", 10}, {"k2", true}, {"k3", 12.22}};
+ OrderedAttributeMap map2 = {{"k3", 12.22}, {"k1", 10}, {"k2", true}};
+ EXPECT_TRUE(GetHashForAttributeMap(map1) == GetHashForAttributeMap(map2));
+ EXPECT_TRUE(GetHashForAttributeMap(map1) != 0);
+ }
+
+ {
+ OrderedAttributeMap map1 = {};
+ EXPECT_TRUE(GetHashForAttributeMap(map1) == 0);
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/baseline_circular_buffer.h b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/baseline_circular_buffer.h
new file mode 100644
index 000000000..398a4d038
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/baseline_circular_buffer.h
@@ -0,0 +1,88 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <cstdint>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include "opentelemetry/version.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace testing
+{
+/**
+ * A locking circular buffer.
+ *
+ * Used as a baseline in benchmarking.
+ */
+template <class T>
+class BaselineCircularBuffer
+{
+public:
+ explicit BaselineCircularBuffer(size_t max_size) : data_{max_size} {}
+
+ /**
+ * Add an element to the circular buffer.
+ * @param element the element to add
+ * @return true if the element was added successfully
+ */
+ bool Add(std::unique_ptr<T> &element) noexcept { return this->Add(std::move(element)); }
+
+ bool Add(std::unique_ptr<T> &&element) noexcept
+ {
+ std::lock_guard<std::mutex> lock_guard{mutex_};
+ if (tail_ + data_.size() == head_)
+ {
+ return false;
+ }
+ data_[head_ % data_.size()] = std::move(element);
+ head_ += 1;
+ return true;
+ }
+
+ /**
+ * Consume elements in the circular buffer.
+ * @param f the callback to call for each element
+ */
+ template <class F>
+ void Consume(F f) noexcept
+ {
+ std::lock_guard<std::mutex> lock_guard{mutex_};
+ if (head_ == tail_)
+ {
+ return;
+ }
+ auto tail_index = tail_ % data_.size();
+ auto head_index = head_ % data_.size();
+ if (tail_index < head_index)
+ {
+ for (auto i = tail_index; i < head_index; ++i)
+ {
+ f(std::move(data_[i]));
+ }
+ }
+ else
+ {
+ for (auto i = tail_index; i < data_.size(); ++i)
+ {
+ f(std::move(data_[i]));
+ }
+ for (auto i = 0ull; i < head_index; ++i)
+ {
+ f(std::move(data_[i]));
+ }
+ }
+ tail_ = head_;
+ }
+
+private:
+ std::mutex mutex_;
+ uint64_t head_{0};
+ uint64_t tail_{0};
+ std::vector<std::unique_ptr<T>> data_;
+};
+} // namespace testing
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_benchmark.cc
new file mode 100644
index 000000000..1f2b9b42c
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_benchmark.cc
@@ -0,0 +1,133 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "benchmark/benchmark.h"
+
+#include <atomic>
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <random>
+#include <thread>
+#include <vector>
+
+#include "opentelemetry/sdk/common/circular_buffer.h"
+#include "test/common/baseline_circular_buffer.h"
+using opentelemetry::sdk::common::AtomicUniquePtr;
+using opentelemetry::sdk::common::CircularBuffer;
+using opentelemetry::sdk::common::CircularBufferRange;
+using opentelemetry::testing::BaselineCircularBuffer;
+
+const int N = 10000;
+
+static uint64_t ConsumeBufferNumbers(BaselineCircularBuffer<uint64_t> &buffer) noexcept
+{
+ uint64_t result = 0;
+ buffer.Consume([&](std::unique_ptr<uint64_t> &&x) {
+ result += *x;
+ x.reset();
+ });
+ return result;
+}
+
+static uint64_t ConsumeBufferNumbers(CircularBuffer<uint64_t> &buffer) noexcept
+{
+ uint64_t result = 0;
+ buffer.Consume(buffer.size(),
+ [&](CircularBufferRange<AtomicUniquePtr<uint64_t>> &range) noexcept {
+ range.ForEach([&](AtomicUniquePtr<uint64_t> &ptr) noexcept {
+ result += *ptr;
+ ptr.Reset();
+ return true;
+ });
+ });
+ return result;
+}
+
+template <class Buffer>
+static void GenerateNumbersForThread(Buffer &buffer, int n, std::atomic<uint64_t> &sum) noexcept
+{
+ thread_local std::mt19937_64 random_number_generator{std::random_device{}()};
+ for (int i = 0; i < n; ++i)
+ {
+ auto x = random_number_generator();
+ std::unique_ptr<uint64_t> element{new uint64_t{x}};
+ if (buffer.Add(element))
+ {
+ sum += x;
+ }
+ }
+}
+
+template <class Buffer>
+static uint64_t GenerateNumbers(Buffer &buffer, int num_threads, int n) noexcept
+{
+ std::atomic<uint64_t> sum{0};
+ std::vector<std::thread> threads(num_threads);
+ for (auto &thread : threads)
+ {
+ thread = std::thread{GenerateNumbersForThread<Buffer>, std::ref(buffer), n, std::ref(sum)};
+ }
+ for (auto &thread : threads)
+ {
+ thread.join();
+ }
+ return sum;
+}
+
+template <class Buffer>
+static void ConsumeNumbers(Buffer &buffer, uint64_t &sum, std::atomic<bool> &finished) noexcept
+{
+ while (!finished)
+ {
+ sum += ConsumeBufferNumbers(buffer);
+ }
+ sum += ConsumeBufferNumbers(buffer);
+}
+
+template <class Buffer>
+static void RunSimulation(Buffer &buffer, int num_threads, int n) noexcept
+{
+ std::atomic<bool> finished{false};
+ uint64_t consumer_sum{0};
+ std::thread consumer_thread{ConsumeNumbers<Buffer>, std::ref(buffer), std::ref(consumer_sum),
+ std::ref(finished)};
+ uint64_t producer_sum = GenerateNumbers(buffer, num_threads, n);
+ finished = true;
+ consumer_thread.join();
+ if (consumer_sum != producer_sum)
+ {
+ std::cerr << "Sumulation failed: consumer_sum != producer_sum\n";
+ std::terminate();
+ }
+}
+
+static void BM_BaselineBuffer(benchmark::State &state)
+{
+ const size_t max_elements = 500;
+ const int num_threads = static_cast<int>(state.range(0));
+ const int n = static_cast<int>(N / num_threads);
+ BaselineCircularBuffer<uint64_t> buffer{max_elements};
+ for (auto _ : state)
+ {
+ RunSimulation(buffer, num_threads, n);
+ }
+}
+
+BENCHMARK(BM_BaselineBuffer)->Arg(1)->Arg(2)->Arg(4);
+
+static void BM_LockFreeBuffer(benchmark::State &state)
+{
+ const size_t max_elements = 500;
+ const int num_threads = static_cast<int>(state.range(0));
+ const int n = static_cast<int>(N / num_threads);
+ CircularBuffer<uint64_t> buffer{max_elements};
+ for (auto _ : state)
+ {
+ RunSimulation(buffer, num_threads, n);
+ }
+}
+
+BENCHMARK(BM_LockFreeBuffer)->Arg(1)->Arg(2)->Arg(4);
+
+BENCHMARK_MAIN();
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_range_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_range_test.cc
new file mode 100644
index 000000000..585270468
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_range_test.cc
@@ -0,0 +1,59 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/common/circular_buffer_range.h"
+
+#include <iterator>
+
+#include <gtest/gtest.h>
+using opentelemetry::sdk::common::CircularBufferRange;
+
+TEST(CircularBufferRangeTest, ForEach)
+{
+ int array1[] = {1, 2, 3, 4};
+ int array2[] = {5, 6, 7};
+ CircularBufferRange<int> range{array1, array2};
+
+ int x = 0;
+ range.ForEach([&](int y) {
+ EXPECT_EQ(++x, y);
+ return true;
+ });
+ EXPECT_EQ(x, 7);
+}
+
+TEST(CircularBufferRangeTest, ForEachWithExit)
+{
+ int array1[] = {1, 2, 3, 4};
+ int array2[] = {5, 6, 7};
+ CircularBufferRange<int> range{array1, array2};
+
+ int x = 0;
+ range.ForEach([&](int y) {
+ EXPECT_EQ(++x, y);
+ return false;
+ });
+ EXPECT_EQ(x, 1);
+
+ x = 0;
+ range.ForEach([&](int y) {
+ EXPECT_EQ(++x, y);
+ return y != 5;
+ });
+ EXPECT_EQ(x, 5);
+}
+
+TEST(CircularBufferRangeTest, Conversion)
+{
+ int array1[] = {1, 2, 3, 4};
+ int array2[] = {5, 6, 7};
+ CircularBufferRange<int> range{array1, array2};
+
+ CircularBufferRange<const int> range2{range};
+ int x = 0;
+ range2.ForEach([&](int y) {
+ EXPECT_EQ(++x, y);
+ return true;
+ });
+ EXPECT_EQ(x, 7);
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_test.cc
new file mode 100644
index 000000000..a20c3e42a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_test.cc
@@ -0,0 +1,161 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/common/circular_buffer.h"
+
+#include <algorithm>
+#include <cassert>
+#include <random>
+#include <thread>
+
+#include <gtest/gtest.h>
+using opentelemetry::sdk::common::AtomicUniquePtr;
+using opentelemetry::sdk::common::CircularBuffer;
+using opentelemetry::sdk::common::CircularBufferRange;
+
+static thread_local std::mt19937 RandomNumberGenerator{std::random_device{}()};
+
+static void GenerateRandomNumbers(CircularBuffer<uint32_t> &buffer,
+ std::vector<uint32_t> &numbers,
+ int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ auto value = static_cast<uint32_t>(RandomNumberGenerator());
+ std::unique_ptr<uint32_t> x{new uint32_t{value}};
+ if (buffer.Add(x))
+ {
+ numbers.push_back(value);
+ }
+ }
+}
+
+static void RunNumberProducers(CircularBuffer<uint32_t> &buffer,
+ std::vector<uint32_t> &numbers,
+ int num_threads,
+ int n)
+{
+ std::vector<std::vector<uint32_t>> thread_numbers(num_threads);
+ std::vector<std::thread> threads(num_threads);
+ for (int thread_index = 0; thread_index < num_threads; ++thread_index)
+ {
+ threads[thread_index] = std::thread{GenerateRandomNumbers, std::ref(buffer),
+ std::ref(thread_numbers[thread_index]), n};
+ }
+ for (auto &thread : threads)
+ {
+ thread.join();
+ }
+ for (int thread_index = 0; thread_index < num_threads; ++thread_index)
+ {
+ numbers.insert(numbers.end(), thread_numbers[thread_index].begin(),
+ thread_numbers[thread_index].end());
+ }
+}
+
+void RunNumberConsumer(CircularBuffer<uint32_t> &buffer,
+ std::atomic<bool> &exit,
+ std::vector<uint32_t> &numbers)
+{
+ while (true)
+ {
+ if (exit && buffer.Peek().empty())
+ {
+ return;
+ }
+ auto n = std::uniform_int_distribution<size_t>{0, buffer.Peek().size()}(RandomNumberGenerator);
+ buffer.Consume(n, [&](CircularBufferRange<AtomicUniquePtr<uint32_t>> range) noexcept {
+ assert(range.size() == n);
+ range.ForEach([&](AtomicUniquePtr<uint32_t> &ptr) noexcept {
+ assert(!ptr.IsNull());
+ numbers.push_back(*ptr);
+ ptr.Reset();
+ return true;
+ });
+ });
+ }
+}
+
+TEST(CircularBufferTest, Add)
+{
+ CircularBuffer<int> buffer{10};
+
+ std::unique_ptr<int> x{new int{11}};
+ EXPECT_TRUE(buffer.Add(x));
+ EXPECT_EQ(x, nullptr);
+ auto range = buffer.Peek();
+ EXPECT_EQ(range.size(), 1);
+ range.ForEach([](const AtomicUniquePtr<int> &y) {
+ EXPECT_EQ(*y, 11);
+ return true;
+ });
+}
+
+TEST(CircularBufferTest, Clear)
+{
+ CircularBuffer<int> buffer{10};
+
+ std::unique_ptr<int> x{new int{11}};
+ EXPECT_TRUE(buffer.Add(x));
+ EXPECT_EQ(x, nullptr);
+ buffer.Clear();
+ EXPECT_TRUE(buffer.empty());
+}
+
+TEST(CircularBufferTest, AddOnFull)
+{
+ CircularBuffer<int> buffer{10};
+ for (int i = 0; i < static_cast<int>(buffer.max_size()); ++i)
+ {
+ std::unique_ptr<int> x{new int{i}};
+ EXPECT_TRUE(buffer.Add(x));
+ }
+ std::unique_ptr<int> x{new int{33}};
+ EXPECT_FALSE(buffer.Add(x));
+ EXPECT_NE(x, nullptr);
+ EXPECT_EQ(*x, 33);
+}
+
+TEST(CircularBufferTest, Consume)
+{
+ CircularBuffer<int> buffer{10};
+ for (int i = 0; i < static_cast<int>(buffer.max_size()); ++i)
+ {
+ std::unique_ptr<int> x{new int{i}};
+ EXPECT_TRUE(buffer.Add(x));
+ }
+ int count = 0;
+ buffer.Consume(5, [&](CircularBufferRange<AtomicUniquePtr<int>> range) noexcept {
+ range.ForEach([&](AtomicUniquePtr<int> &ptr) {
+ EXPECT_EQ(*ptr, count++);
+ ptr.Reset();
+ return true;
+ });
+ });
+ EXPECT_EQ(count, 5);
+}
+
+TEST(CircularBufferTest, Simulation)
+{
+ const int num_producer_threads = 4;
+ const int n = 25000;
+ for (size_t max_size : {1, 2, 10, 50, 100, 1000})
+ {
+ CircularBuffer<uint32_t> buffer{max_size};
+ std::vector<uint32_t> producer_numbers;
+ std::vector<uint32_t> consumer_numbers;
+ auto producers = std::thread{RunNumberProducers, std::ref(buffer), std::ref(producer_numbers),
+ num_producer_threads, n};
+ std::atomic<bool> exit{false};
+ auto consumer = std::thread{RunNumberConsumer, std::ref(buffer), std::ref(exit),
+ std::ref(consumer_numbers)};
+ producers.join();
+ exit = true;
+ consumer.join();
+ std::sort(producer_numbers.begin(), producer_numbers.end());
+ std::sort(consumer_numbers.begin(), consumer_numbers.end());
+
+ EXPECT_EQ(producer_numbers.size(), consumer_numbers.size());
+ EXPECT_EQ(producer_numbers, consumer_numbers);
+ }
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/empty_attributes_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/empty_attributes_test.cc
new file mode 100644
index 000000000..f37ea0a5c
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/empty_attributes_test.cc
@@ -0,0 +1,19 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/common/empty_attributes.h"
+
+#include <gtest/gtest.h>
+
+TEST(EmptyAttributesTest, TestSize)
+{
+ EXPECT_EQ(opentelemetry::sdk::GetEmptyAttributes().size(), 0);
+}
+
+// Test that GetEmptyAttributes() always returns the same KeyValueIterableView
+TEST(EmptyAttributesTest, TestMemory)
+{
+ auto attributes1 = opentelemetry::sdk::GetEmptyAttributes();
+ auto attributes2 = opentelemetry::sdk::GetEmptyAttributes();
+ EXPECT_EQ(memcmp(&attributes1, &attributes2, sizeof(attributes1)), 0);
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/fast_random_number_generator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/fast_random_number_generator_test.cc
new file mode 100644
index 000000000..e9209fdb6
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/fast_random_number_generator_test.cc
@@ -0,0 +1,22 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "src/common/random.h"
+
+#include <random>
+
+#include <gtest/gtest.h>
+
+using opentelemetry::sdk::common::FastRandomNumberGenerator;
+
+TEST(FastRandomNumberGeneratorTest, GenerateUniqueNumbers)
+{
+ std::seed_seq seed_sequence{1, 2, 3};
+ FastRandomNumberGenerator random_number_generator;
+ random_number_generator.seed(seed_sequence);
+ std::set<uint64_t> values;
+ for (int i = 0; i < 1000; ++i)
+ {
+ EXPECT_TRUE(values.insert(random_number_generator()).second);
+ }
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/global_log_handle_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/global_log_handle_test.cc
new file mode 100644
index 000000000..a38bdc872
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/global_log_handle_test.cc
@@ -0,0 +1,67 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/common/global_log_handler.h"
+
+#include <gtest/gtest.h>
+
+#include <cstring>
+
+class CustomLogHandler : public opentelemetry::sdk::common::internal_log::LogHandler
+{
+public:
+ void Handle(opentelemetry::sdk::common::internal_log::LogLevel level,
+ const char *,
+ int,
+ const char *msg,
+ const opentelemetry::sdk::common::AttributeMap &) noexcept override
+ {
+ if (level == opentelemetry::sdk::common::internal_log::LogLevel::Debug)
+ {
+ EXPECT_EQ(0, strncmp(msg, "Debug message", 13));
+ }
+ else if (level == opentelemetry::sdk::common::internal_log::LogLevel::Error)
+ {
+ EXPECT_EQ(0, strncmp(msg, "Error message", 13));
+ }
+ else if (level == opentelemetry::sdk::common::internal_log::LogLevel::Info)
+ {
+ EXPECT_EQ(0, strncmp(msg, "Info message", 12));
+ }
+ else if (level == opentelemetry::sdk::common::internal_log::LogLevel::Warning)
+ {
+ EXPECT_EQ(0, strncmp(msg, "Warning message", 15));
+ }
+ ++count;
+ }
+
+ size_t count = 0;
+};
+
+TEST(GlobalLogHandleTest, CustomLogHandler)
+{
+ using opentelemetry::sdk::common::internal_log::LogHandler;
+ auto backup_log_handle =
+ opentelemetry::sdk::common::internal_log::GlobalLogHandler::GetLogHandler();
+ auto backup_log_level = opentelemetry::sdk::common::internal_log::GlobalLogHandler::GetLogLevel();
+
+ auto custom_log_handler = opentelemetry::nostd::shared_ptr<LogHandler>(new CustomLogHandler{});
+ opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogHandler(custom_log_handler);
+ auto before_count = static_cast<CustomLogHandler *>(custom_log_handler.get())->count;
+ opentelemetry::sdk::common::AttributeMap attributes = {
+ {"url", "https://opentelemetry.io/"}, {"content-length", 0}, {"content-type", "text/html"}};
+ OTEL_INTERNAL_LOG_ERROR("Error message");
+ OTEL_INTERNAL_LOG_DEBUG("Debug message. Headers:", attributes);
+ EXPECT_EQ(before_count + 1, static_cast<CustomLogHandler *>(custom_log_handler.get())->count);
+
+ opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogLevel(
+ opentelemetry::sdk::common::internal_log::LogLevel::Debug);
+ OTEL_INTERNAL_LOG_ERROR("Error message");
+ OTEL_INTERNAL_LOG_DEBUG("Debug message. Headers:", attributes);
+ OTEL_INTERNAL_LOG_INFO("Info message");
+ OTEL_INTERNAL_LOG_WARN("Warning message");
+ EXPECT_EQ(before_count + 5, static_cast<CustomLogHandler *>(custom_log_handler.get())->count);
+
+ opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogHandler(backup_log_handle);
+ opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogLevel(backup_log_level);
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_benchmark.cc
new file mode 100644
index 000000000..df2ebf95e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_benchmark.cc
@@ -0,0 +1,35 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "src/common/random.h"
+
+#include <cstdint>
+#include <random>
+
+#include <benchmark/benchmark.h>
+
+namespace
+{
+using opentelemetry::sdk::common::Random;
+
+void BM_RandomIdGeneration(benchmark::State &state)
+{
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(Random::GenerateRandom64());
+ }
+}
+BENCHMARK(BM_RandomIdGeneration);
+
+void BM_RandomIdStdGeneration(benchmark::State &state)
+{
+ std::mt19937_64 generator{0};
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(generator());
+ }
+}
+BENCHMARK(BM_RandomIdStdGeneration);
+
+} // namespace
+BENCHMARK_MAIN();
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_fork_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_fork_test.cc
new file mode 100644
index 000000000..2db8b9fcd
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_fork_test.cc
@@ -0,0 +1,53 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef __unix__
+// Verifies that IDs don't clash after forking the process.
+//
+// See https://github.com/opentracing-contrib/nginx-opentracing/issues/52
+# include "src/common/random.h"
+
+# include <sys/mman.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <unistd.h>
+# include <cstdio>
+# include <cstdlib>
+# include <iostream>
+using opentelemetry::sdk::common::Random;
+
+static uint64_t *child_id;
+
+int main()
+{
+ // Set up shared memory to communicate between parent and child processes.
+ //
+ // See https://stackoverflow.com/a/13274800/4447365
+ child_id = static_cast<uint64_t *>(
+ mmap(nullptr, sizeof(*child_id), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0));
+ *child_id = 0;
+ if (fork() == 0)
+ {
+ *child_id = Random::GenerateRandom64();
+ exit(EXIT_SUCCESS);
+ }
+ else
+ {
+ wait(nullptr);
+ auto parent_id = Random::GenerateRandom64();
+ auto child_id_copy = *child_id;
+ munmap(static_cast<void *>(child_id), sizeof(*child_id));
+ if (parent_id == child_id_copy)
+ {
+ std::cerr << "Child and parent ids are the same value " << parent_id << "\n";
+ return -1;
+ }
+ }
+ return 0;
+}
+#else
+int main()
+{
+ return 0;
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_test.cc
new file mode 100644
index 000000000..35cfd4a1e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_test.cc
@@ -0,0 +1,35 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "src/common/random.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include <gtest/gtest.h>
+using opentelemetry::sdk::common::Random;
+
+TEST(RandomTest, GenerateRandom64)
+{
+ EXPECT_NE(Random::GenerateRandom64(), Random::GenerateRandom64());
+}
+
+TEST(RandomTest, GenerateRandomBuffer)
+{
+ uint8_t buf1[8] = {0};
+ uint8_t buf2[8] = {0};
+ Random::GenerateRandomBuffer(buf1);
+ Random::GenerateRandomBuffer(buf2);
+ EXPECT_FALSE(std::equal(std::begin(buf1), std::end(buf1), std::begin(buf2)));
+
+ // Edge cases.
+ for (auto size : {7, 8, 9, 16, 17})
+ {
+ std::vector<uint8_t> buf1(size);
+ std::vector<uint8_t> buf2(size);
+
+ Random::GenerateRandomBuffer(buf1);
+ Random::GenerateRandomBuffer(buf2);
+ EXPECT_FALSE(std::equal(std::begin(buf1), std::end(buf1), std::begin(buf2)));
+ }
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/BUILD
new file mode 100644
index 000000000..38cc25300
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/BUILD
@@ -0,0 +1,14 @@
+load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark")
+
+cc_test(
+ name = "instrumentationlibrary_test",
+ srcs = [
+ "instrumentationlibrary_test.cc",
+ ],
+ tags = ["test"],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/CMakeLists.txt
new file mode 100644
index 000000000..512266dc8
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/CMakeLists.txt
@@ -0,0 +1,11 @@
+include(GoogleTest)
+
+foreach(testname instrumentationlibrary_test)
+ add_executable(${testname} "${testname}.cc")
+ target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api)
+ gtest_add_tests(
+ TARGET ${testname}
+ TEST_PREFIX instrumentationlibrary.
+ TEST_LIST ${testname})
+endforeach()
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc
new file mode 100644
index 000000000..a410ca99f
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc
@@ -0,0 +1,26 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/nostd/string_view.h"
+#include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h"
+
+#include <gtest/gtest.h>
+#include <string>
+#include <vector>
+
+using namespace opentelemetry;
+using namespace opentelemetry::sdk::instrumentationlibrary;
+
+TEST(InstrumentationLibrary, CreateInstrumentationLibrary)
+{
+
+ std::string library_name = "opentelemetry-cpp";
+ std::string library_version = "0.1.0";
+ std::string schema_url = "https://opentelemetry.io/schemas/1.2.0";
+ auto instrumentation_library =
+ InstrumentationLibrary::Create(library_name, library_version, schema_url);
+
+ EXPECT_EQ(instrumentation_library->GetName(), library_name);
+ EXPECT_EQ(instrumentation_library->GetVersion(), library_version);
+ EXPECT_EQ(instrumentation_library->GetSchemaURL(), schema_url);
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/BUILD
new file mode 100644
index 000000000..c8f051070
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/BUILD
@@ -0,0 +1,75 @@
+cc_test(
+ name = "logger_provider_sdk_test",
+ srcs = [
+ "logger_provider_sdk_test.cc",
+ ],
+ tags = [
+ "logs",
+ "test",
+ ],
+ deps = [
+ "//api",
+ "//sdk/src/logs",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "logger_sdk_test",
+ srcs = [
+ "logger_sdk_test.cc",
+ ],
+ tags = [
+ "logs",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/logs",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "simple_log_processor_test",
+ srcs = [
+ "simple_log_processor_test.cc",
+ ],
+ tags = [
+ "logs",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/logs",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "log_record_test",
+ srcs = [
+ "log_record_test.cc",
+ ],
+ tags = [
+ "logs",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/logs",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "batch_log_processor_test",
+ srcs = [
+ "batch_log_processor_test.cc",
+ ],
+ tags = [
+ "logs",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/logs",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/CMakeLists.txt
new file mode 100644
index 000000000..84b865d22
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/CMakeLists.txt
@@ -0,0 +1,10 @@
+foreach(testname logger_provider_sdk_test logger_sdk_test log_record_test
+ simple_log_processor_test batch_log_processor_test)
+ add_executable(${testname} "${testname}.cc")
+ target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT} opentelemetry_logs)
+ gtest_add_tests(
+ TARGET ${testname}
+ TEST_PREFIX logs.
+ TEST_LIST ${testname})
+endforeach()
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/batch_log_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/batch_log_processor_test.cc
new file mode 100644
index 000000000..63e44676c
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/batch_log_processor_test.cc
@@ -0,0 +1,269 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include "opentelemetry/sdk/logs/batch_log_processor.h"
+# include "opentelemetry/sdk/logs/exporter.h"
+# include "opentelemetry/sdk/logs/log_record.h"
+
+# include <gtest/gtest.h>
+# include <chrono>
+# include <thread>
+
+using namespace opentelemetry::sdk::logs;
+using namespace opentelemetry::sdk::common;
+
+/**
+ * A sample log exporter
+ * for testing the batch log processor
+ */
+class MockLogExporter final : public LogExporter
+{
+public:
+ MockLogExporter(std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received,
+ std::shared_ptr<std::atomic<bool>> is_shutdown,
+ std::shared_ptr<std::atomic<bool>> is_export_completed,
+ const std::chrono::milliseconds export_delay = std::chrono::milliseconds(0))
+ : logs_received_(logs_received),
+ is_shutdown_(is_shutdown),
+ is_export_completed_(is_export_completed),
+ export_delay_(export_delay)
+ {}
+
+ std::unique_ptr<Recordable> MakeRecordable() noexcept
+ {
+ return std::unique_ptr<Recordable>(new LogRecord());
+ }
+
+ // Export method stores the logs received into a shared list of record names
+ ExportResult Export(
+ const opentelemetry::nostd::span<std::unique_ptr<Recordable>> &records) noexcept override
+ {
+ *is_export_completed_ = false; // Meant exclusively to test scheduled_delay_millis
+
+ for (auto &record : records)
+ {
+ auto log = std::unique_ptr<LogRecord>(static_cast<LogRecord *>(record.release()));
+ if (log != nullptr)
+ {
+ logs_received_->push_back(std::move(log));
+ }
+ }
+
+ *is_export_completed_ = true;
+ return ExportResult::kSuccess;
+ }
+
+ // toggles the boolean flag marking this exporter as shut down
+ bool Shutdown(
+ std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override
+ {
+ *is_shutdown_ = true;
+ return true;
+ }
+
+private:
+ std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received_;
+ std::shared_ptr<std::atomic<bool>> is_shutdown_;
+ std::shared_ptr<std::atomic<bool>> is_export_completed_;
+ const std::chrono::milliseconds export_delay_;
+};
+
+/**
+ * A fixture class for testing the BatchLogProcessor class that uses the TestExporter defined above.
+ */
+class BatchLogProcessorTest : public testing::Test // ::testing::Test
+{
+public:
+ // returns a batch log processor that received a batch of log records, a shared pointer to a
+ // is_shutdown flag, and the processor configuration options (default if unspecified)
+ std::shared_ptr<LogProcessor> GetMockProcessor(
+ std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received,
+ std::shared_ptr<std::atomic<bool>> is_shutdown,
+ std::shared_ptr<std::atomic<bool>> is_export_completed =
+ std::shared_ptr<std::atomic<bool>>(new std::atomic<bool>(false)),
+ const std::chrono::milliseconds export_delay = std::chrono::milliseconds(0),
+ const std::chrono::milliseconds scheduled_delay_millis = std::chrono::milliseconds(5000),
+ const size_t max_queue_size = 2048,
+ const size_t max_export_batch_size = 512)
+ {
+ return std::shared_ptr<LogProcessor>(
+ new BatchLogProcessor(std::unique_ptr<LogExporter>(new MockLogExporter(
+ logs_received, is_shutdown, is_export_completed, export_delay)),
+ max_queue_size, scheduled_delay_millis, max_export_batch_size));
+ }
+};
+
+TEST_F(BatchLogProcessorTest, TestShutdown)
+{
+ // initialize a batch log processor with the test exporter
+ std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received(
+ new std::vector<std::unique_ptr<LogRecord>>);
+ std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false));
+
+ auto batch_processor = GetMockProcessor(logs_received, is_shutdown);
+
+ // Create a few test log records and send them to the processor
+ const int num_logs = 3;
+
+ for (int i = 0; i < num_logs; ++i)
+ {
+ auto log = batch_processor->MakeRecordable();
+ log->SetBody("Log" + std::to_string(i));
+ batch_processor->OnReceive(std::move(log));
+ }
+
+ // Test that shutting down the processor will first wait for the
+ // current batch of logs to be sent to the log exporter
+ // by checking the number of logs sent and the names of the logs sent
+ EXPECT_EQ(true, batch_processor->Shutdown());
+ // It's safe to shutdown again
+ EXPECT_TRUE(batch_processor->Shutdown());
+
+ EXPECT_EQ(num_logs, logs_received->size());
+
+ // Assume logs are received by exporter in same order as sent by processor
+ for (int i = 0; i < num_logs; ++i)
+ {
+ EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetBody());
+ }
+
+ // Also check that the processor is shut down at the end
+ EXPECT_TRUE(is_shutdown->load());
+}
+
+TEST_F(BatchLogProcessorTest, TestForceFlush)
+{
+ std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false));
+ std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received(
+ new std::vector<std::unique_ptr<LogRecord>>);
+
+ auto batch_processor = GetMockProcessor(logs_received, is_shutdown);
+ const int num_logs = 2048;
+
+ for (int i = 0; i < num_logs; ++i)
+ {
+ auto log = batch_processor->MakeRecordable();
+ log->SetBody("Log" + std::to_string(i));
+ batch_processor->OnReceive(std::move(log));
+ }
+
+ EXPECT_TRUE(batch_processor->ForceFlush());
+
+ EXPECT_EQ(num_logs, logs_received->size());
+ for (int i = 0; i < num_logs; ++i)
+ {
+ EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetBody());
+ }
+
+ // Create some more logs to make sure that the processor still works
+ for (int i = 0; i < num_logs; ++i)
+ {
+ auto log = batch_processor->MakeRecordable();
+ log->SetBody("Log" + std::to_string(i));
+ batch_processor->OnReceive(std::move(log));
+ }
+
+ EXPECT_TRUE(batch_processor->ForceFlush());
+
+ EXPECT_EQ(num_logs * 2, logs_received->size());
+ for (int i = 0; i < num_logs * 2; ++i)
+ {
+ EXPECT_EQ("Log" + std::to_string(i % num_logs), logs_received->at(i)->GetBody());
+ }
+}
+
+TEST_F(BatchLogProcessorTest, TestManyLogsLoss)
+{
+ /* Test that when exporting more than max_queue_size logs, some are most likely lost*/
+
+ std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false));
+ std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received(
+ new std::vector<std::unique_ptr<LogRecord>>);
+
+ const int max_queue_size = 4096;
+
+ auto batch_processor = GetMockProcessor(logs_received, is_shutdown);
+
+ // Create max_queue_size log records
+ for (int i = 0; i < max_queue_size; ++i)
+ {
+ auto log = batch_processor->MakeRecordable();
+ log->SetBody("Log" + std::to_string(i));
+ batch_processor->OnReceive(std::move(log));
+ }
+
+ EXPECT_TRUE(batch_processor->ForceFlush());
+
+ // Log should be exported by now
+ EXPECT_GE(max_queue_size, logs_received->size());
+}
+
+TEST_F(BatchLogProcessorTest, TestManyLogsLossLess)
+{
+ /* Test that no logs are lost when sending max_queue_size logs */
+
+ std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false));
+ std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received(
+ new std::vector<std::unique_ptr<LogRecord>>);
+ auto batch_processor = GetMockProcessor(logs_received, is_shutdown);
+
+ const int num_logs = 2048;
+
+ for (int i = 0; i < num_logs; ++i)
+ {
+ auto log = batch_processor->MakeRecordable();
+ log->SetBody("Log" + std::to_string(i));
+ batch_processor->OnReceive(std::move(log));
+ }
+
+ EXPECT_TRUE(batch_processor->ForceFlush());
+
+ EXPECT_EQ(num_logs, logs_received->size());
+ for (int i = 0; i < num_logs; ++i)
+ {
+ EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetBody());
+ }
+}
+
+TEST_F(BatchLogProcessorTest, TestScheduledDelayMillis)
+{
+ /* Test that max_export_batch_size logs are exported every scheduled_delay_millis
+ seconds */
+
+ std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false));
+ std::shared_ptr<std::atomic<bool>> is_export_completed(new std::atomic<bool>(false));
+ std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received(
+ new std::vector<std::unique_ptr<LogRecord>>);
+
+ const std::chrono::milliseconds export_delay(0);
+ const std::chrono::milliseconds scheduled_delay_millis(2000);
+ const size_t max_export_batch_size = 512;
+
+ auto batch_processor = GetMockProcessor(logs_received, is_shutdown, is_export_completed,
+ export_delay, scheduled_delay_millis);
+
+ for (std::size_t i = 0; i < max_export_batch_size; ++i)
+ {
+ auto log = batch_processor->MakeRecordable();
+ log->SetBody("Log" + std::to_string(i));
+ batch_processor->OnReceive(std::move(log));
+ }
+ // Sleep for scheduled_delay_millis milliseconds
+ std::this_thread::sleep_for(scheduled_delay_millis);
+
+ // small delay to give time to export, which is being performed
+ // asynchronously by the worker thread (this thread will not
+ // forcibly join() the main thread unless processor's shutdown() is called).
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+
+ // Logs should be exported by now
+ EXPECT_TRUE(is_export_completed->load());
+ EXPECT_EQ(max_export_batch_size, logs_received->size());
+ for (size_t i = 0; i < max_export_batch_size; ++i)
+ {
+ EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetBody());
+ }
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/log_record_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/log_record_test.cc
new file mode 100644
index 000000000..89b07473a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/log_record_test.cc
@@ -0,0 +1,66 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include "opentelemetry/sdk/logs/log_record.h"
+# include "opentelemetry/nostd/variant.h"
+# include "opentelemetry/trace/span_id.h"
+# include "opentelemetry/trace/trace_id.h"
+
+# include <gtest/gtest.h>
+
+using opentelemetry::sdk::logs::LogRecord;
+namespace trace_api = opentelemetry::trace;
+namespace logs_api = opentelemetry::logs;
+namespace nostd = opentelemetry::nostd;
+
+// Test what a default LogRecord with no fields set holds
+TEST(LogRecord, GetDefaultValues)
+{
+ trace_api::TraceId zero_trace_id;
+ trace_api::SpanId zero_span_id;
+ trace_api::TraceFlags zero_trace_flags;
+ LogRecord record;
+
+ ASSERT_EQ(record.GetSeverity(), logs_api::Severity::kInvalid);
+ ASSERT_EQ(record.GetBody(), "");
+ ASSERT_NE(record.GetResource().GetAttributes().size(), 0);
+ ASSERT_EQ(record.GetAttributes().size(), 0);
+ ASSERT_EQ(record.GetTraceId(), zero_trace_id);
+ ASSERT_EQ(record.GetSpanId(), zero_span_id);
+ ASSERT_EQ(record.GetTraceFlags(), zero_trace_flags);
+ ASSERT_EQ(record.GetTimestamp().time_since_epoch(), std::chrono::nanoseconds(0));
+}
+
+// Test LogRecord fields are properly set and get
+TEST(LogRecord, SetAndGet)
+{
+ trace_api::TraceId trace_id;
+ trace_api::SpanId span_id;
+ trace_api::TraceFlags trace_flags;
+ opentelemetry::common::SystemTimestamp now(std::chrono::system_clock::now());
+
+ // Set all fields of the LogRecord
+ LogRecord record;
+ auto resource = opentelemetry::sdk::resource::Resource::Create({{"res1", true}});
+ record.SetSeverity(logs_api::Severity::kInvalid);
+ record.SetBody("Message");
+ record.SetResource(resource);
+ record.SetAttribute("attr1", (int64_t)314159);
+ record.SetTraceId(trace_id);
+ record.SetSpanId(span_id);
+ record.SetTraceFlags(trace_flags);
+ record.SetTimestamp(now);
+
+ // Test that all fields match what was set
+ ASSERT_EQ(record.GetSeverity(), logs_api::Severity::kInvalid);
+ ASSERT_EQ(record.GetBody(), "Message");
+ ASSERT_TRUE(nostd::get<bool>(record.GetResource().GetAttributes().at("res1")));
+ ASSERT_EQ(nostd::get<int64_t>(record.GetAttributes().at("attr1")), 314159);
+ ASSERT_EQ(record.GetTraceId(), trace_id);
+ ASSERT_EQ(record.GetSpanId(), span_id);
+ ASSERT_EQ(record.GetTraceFlags(), trace_flags);
+ ASSERT_EQ(record.GetTimestamp().time_since_epoch(), now.time_since_epoch());
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_provider_sdk_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_provider_sdk_test.cc
new file mode 100644
index 000000000..3e5e8dfb2
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_provider_sdk_test.cc
@@ -0,0 +1,133 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include <array>
+# include "opentelemetry/logs/provider.h"
+# include "opentelemetry/nostd/shared_ptr.h"
+# include "opentelemetry/nostd/string_view.h"
+# include "opentelemetry/sdk/logs/log_record.h"
+# include "opentelemetry/sdk/logs/logger.h"
+# include "opentelemetry/sdk/logs/logger_provider.h"
+# include "opentelemetry/sdk/logs/simple_log_processor.h"
+
+# include <gtest/gtest.h>
+
+using namespace opentelemetry::sdk::logs;
+namespace logs_api = opentelemetry::logs;
+namespace nostd = opentelemetry::nostd;
+
+TEST(LoggerProviderSDK, PushToAPI)
+{
+ auto lp =
+ nostd::shared_ptr<logs_api::LoggerProvider>(new opentelemetry::sdk::logs::LoggerProvider());
+ logs_api::Provider::SetLoggerProvider(lp);
+
+ // Check that the loggerprovider was correctly pushed into the API
+ ASSERT_EQ(lp, logs_api::Provider::GetLoggerProvider());
+}
+
+TEST(LoggerProviderSDK, LoggerProviderGetLoggerSimple)
+{
+ auto lp = std::shared_ptr<logs_api::LoggerProvider>(new LoggerProvider());
+
+ nostd::string_view schema_url{"https://opentelemetry.io/schemas/1.11.0"};
+ auto logger1 = lp->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url);
+ auto logger2 = lp->GetLogger("logger2", "", "", "", schema_url);
+
+ // Check that the logger is not nullptr
+ ASSERT_NE(logger1, nullptr);
+ ASSERT_NE(logger2, nullptr);
+
+ auto sdk_logger1 = static_cast<opentelemetry::sdk::logs::Logger *>(logger1.get());
+ auto sdk_logger2 = static_cast<opentelemetry::sdk::logs::Logger *>(logger2.get());
+ ASSERT_EQ(sdk_logger1->GetInstrumentationLibrary().GetName(), "opentelelemtry_library");
+ ASSERT_EQ(sdk_logger1->GetInstrumentationLibrary().GetVersion(), "");
+ ASSERT_EQ(sdk_logger1->GetInstrumentationLibrary().GetSchemaURL(), schema_url);
+
+ ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary().GetName(), "logger2");
+ ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary().GetVersion(), "");
+ ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary().GetSchemaURL(), schema_url);
+
+ // Check that two loggers with different names aren't the same instance
+ ASSERT_NE(logger1, logger2);
+
+ // Check that two loggers with the same name are the same instance
+ auto logger3 = lp->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url);
+ ASSERT_EQ(logger1, logger3);
+ auto sdk_logger3 = static_cast<opentelemetry::sdk::logs::Logger *>(logger3.get());
+ ASSERT_EQ(sdk_logger3->GetInstrumentationLibrary(), sdk_logger1->GetInstrumentationLibrary());
+}
+
+TEST(LoggerProviderSDK, LoggerProviderLoggerArguments)
+{
+ // Currently, arguments are not supported by the loggers.
+ // TODO: Once the logging spec defines what arguments are allowed, add more
+ // detail to this test
+ auto lp = std::shared_ptr<logs_api::LoggerProvider>(new LoggerProvider());
+
+ nostd::string_view schema_url{"https://opentelemetry.io/schemas/1.11.0"};
+ auto logger1 = lp->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url);
+
+ // Check GetLogger(logger_name, args)
+ std::array<nostd::string_view, 1> sv{"string"};
+ nostd::span<nostd::string_view> args{sv};
+ auto logger2 = lp->GetLogger("logger2", args, "opentelelemtry_library", "", schema_url);
+ auto sdk_logger1 = static_cast<opentelemetry::sdk::logs::Logger *>(logger1.get());
+ auto sdk_logger2 = static_cast<opentelemetry::sdk::logs::Logger *>(logger2.get());
+ ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary(), sdk_logger1->GetInstrumentationLibrary());
+}
+
+class DummyProcessor : public LogProcessor
+{
+ std::unique_ptr<Recordable> MakeRecordable() noexcept
+ {
+ return std::unique_ptr<Recordable>(new LogRecord);
+ }
+
+ void OnReceive(std::unique_ptr<Recordable> &&record) noexcept {}
+ bool ForceFlush(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept
+ {
+ return true;
+ }
+ bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept
+ {
+ return true;
+ }
+};
+
+TEST(LoggerProviderSDK, GetResource)
+{
+ // Create a LoggerProvider without a processor
+ auto resource = opentelemetry::sdk::resource::Resource::Create({{"key", "value"}});
+ LoggerProvider lp{nullptr, resource};
+ ASSERT_EQ(nostd::get<std::string>(lp.GetResource().GetAttributes().at("key")), "value");
+}
+
+TEST(LoggerProviderSDK, Shutdown)
+{
+ std::unique_ptr<SimpleLogProcessor> processor(new SimpleLogProcessor(nullptr));
+ std::vector<std::unique_ptr<LogProcessor>> processors;
+ processors.push_back(std::move(processor));
+
+ LoggerProvider lp(std::make_shared<LoggerContext>(std::move(processors)));
+
+ EXPECT_TRUE(lp.Shutdown());
+
+ // It's safe to shutdown again
+ EXPECT_TRUE(lp.Shutdown());
+}
+
+TEST(LoggerProviderSDK, ForceFlush)
+{
+ std::unique_ptr<SimpleLogProcessor> processor(new SimpleLogProcessor(nullptr));
+ std::vector<std::unique_ptr<LogProcessor>> processors;
+ processors.push_back(std::move(processor));
+
+ LoggerProvider lp(std::make_shared<LoggerContext>(std::move(processors)));
+
+ EXPECT_TRUE(lp.ForceFlush());
+}
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_sdk_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_sdk_test.cc
new file mode 100644
index 000000000..3747731f1
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_sdk_test.cc
@@ -0,0 +1,97 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include "opentelemetry/sdk/logs/log_record.h"
+# include "opentelemetry/sdk/logs/logger.h"
+
+# include <gtest/gtest.h>
+
+using namespace opentelemetry::sdk::logs;
+namespace logs_api = opentelemetry::logs;
+
+TEST(LoggerSDK, LogToNullProcessor)
+{
+ // Confirm Logger::Log() does not have undefined behavior
+ // even when there is no processor set
+ // since it calls Processor::OnReceive()
+
+ auto lp = std::shared_ptr<logs_api::LoggerProvider>(new LoggerProvider());
+ const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"};
+ auto logger = lp->GetLogger("logger", "", "opentelelemtry_library", "", schema_url);
+
+ auto sdk_logger = static_cast<opentelemetry::sdk::logs::Logger *>(logger.get());
+ ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetName(), "opentelelemtry_library");
+ ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetVersion(), "");
+ ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetSchemaURL(), schema_url);
+ // Log a sample log record to a nullptr processor
+ logger->Debug("Test log");
+}
+
+class MockProcessor final : public LogProcessor
+{
+private:
+ std::shared_ptr<LogRecord> record_received_;
+
+public:
+ // A processor used for testing that keeps a track of the recordable it received
+ explicit MockProcessor(std::shared_ptr<LogRecord> record_received) noexcept
+ : record_received_(record_received)
+ {}
+
+ std::unique_ptr<Recordable> MakeRecordable() noexcept
+ {
+ return std::unique_ptr<Recordable>(new LogRecord);
+ }
+ // OnReceive stores the record it receives into the shared_ptr recordable passed into its
+ // constructor
+ void OnReceive(std::unique_ptr<Recordable> &&record) noexcept
+ {
+ // Cast the recordable received into a concrete LogRecord type
+ auto copy = std::shared_ptr<LogRecord>(static_cast<LogRecord *>(record.release()));
+
+ // Copy over the received log record's severity, name, and body fields over to the recordable
+ // passed in the constructor
+ record_received_->SetSeverity(copy->GetSeverity());
+ record_received_->SetBody(copy->GetBody());
+ }
+ bool ForceFlush(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept
+ {
+ return true;
+ }
+ bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept
+ {
+ return true;
+ }
+};
+
+TEST(LoggerSDK, LogToAProcessor)
+{
+ // Create an API LoggerProvider and logger
+ auto api_lp = std::shared_ptr<logs_api::LoggerProvider>(new LoggerProvider());
+ const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"};
+ auto logger = api_lp->GetLogger("logger", "", "opentelelemtry_library", "", schema_url);
+
+ // Cast the API LoggerProvider to an SDK Logger Provider and assert that it is still the same
+ // LoggerProvider by checking that getting a logger with the same name as the previously defined
+ // logger is the same instance
+ auto lp = static_cast<LoggerProvider *>(api_lp.get());
+ auto logger2 = lp->GetLogger("logger", "", "opentelelemtry_library", "", schema_url);
+ ASSERT_EQ(logger, logger2);
+
+ auto sdk_logger = static_cast<opentelemetry::sdk::logs::Logger *>(logger.get());
+ ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetName(), "opentelelemtry_library");
+ ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetVersion(), "");
+ ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetSchemaURL(), schema_url);
+ // Set a processor for the LoggerProvider
+ auto shared_recordable = std::shared_ptr<LogRecord>(new LogRecord());
+ lp->AddProcessor(std::unique_ptr<LogProcessor>(new MockProcessor(shared_recordable)));
+
+ // Check that the recordable created by the Log() statement is set properly
+ logger->Log(logs_api::Severity::kWarn, "Log Message");
+
+ ASSERT_EQ(shared_recordable->GetSeverity(), logs_api::Severity::kWarn);
+ ASSERT_EQ(shared_recordable->GetBody(), "Log Message");
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/simple_log_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/simple_log_processor_test.cc
new file mode 100644
index 000000000..32c62a508
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/simple_log_processor_test.cc
@@ -0,0 +1,152 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include "opentelemetry/sdk/logs/simple_log_processor.h"
+# include "opentelemetry/nostd/span.h"
+# include "opentelemetry/sdk/logs/exporter.h"
+# include "opentelemetry/sdk/logs/log_record.h"
+
+# include <gtest/gtest.h>
+
+# include <chrono>
+# include <thread>
+
+using namespace opentelemetry::sdk::logs;
+using namespace opentelemetry::sdk::common;
+namespace nostd = opentelemetry::nostd;
+
+/*
+ * A test exporter that can return a vector of all the records it has received,
+ * and keep track of the number of times its Shutdown() function was called.
+ */
+class TestExporter final : public LogExporter
+{
+public:
+ TestExporter(int *shutdown_counter,
+ std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received,
+ size_t *batch_size_received)
+ : shutdown_counter_(shutdown_counter),
+ logs_received_(logs_received),
+ batch_size_received(batch_size_received)
+ {}
+
+ std::unique_ptr<Recordable> MakeRecordable() noexcept override
+ {
+ return std::unique_ptr<Recordable>(new LogRecord());
+ }
+
+ // Stores the names of the log records this exporter receives to an internal list
+ ExportResult Export(const nostd::span<std::unique_ptr<Recordable>> &records) noexcept override
+ {
+ *batch_size_received = records.size();
+ for (auto &record : records)
+ {
+ auto log_record = std::unique_ptr<LogRecord>(static_cast<LogRecord *>(record.release()));
+
+ if (log_record != nullptr)
+ {
+ logs_received_->push_back(std::move(log_record));
+ }
+ }
+ return ExportResult::kSuccess;
+ }
+
+ // Increment the shutdown counter everytime this method is called
+ bool Shutdown(std::chrono::microseconds timeout) noexcept override
+ {
+ *shutdown_counter_ += 1;
+ return true;
+ }
+
+private:
+ int *shutdown_counter_;
+ std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received_;
+ size_t *batch_size_received;
+};
+
+// Tests whether the simple processor successfully creates a batch of size 1
+// and whether the contents of the record is sent to the exporter correctly
+TEST(SimpleLogProcessorTest, SendReceivedLogsToExporter)
+{
+ // Create a simple processor with a TestExporter attached
+ std::shared_ptr<std::vector<std::unique_ptr<LogRecord>>> logs_received(
+ new std::vector<std::unique_ptr<LogRecord>>);
+ size_t batch_size_received = 0;
+
+ std::unique_ptr<TestExporter> exporter(
+ new TestExporter(nullptr, logs_received, &batch_size_received));
+
+ SimpleLogProcessor processor(std::move(exporter));
+
+ // Send some log records to the processor (which should then send to the TestExporter)
+ const int num_logs = 5;
+ for (int i = 0; i < num_logs; i++)
+ {
+ auto recordable = processor.MakeRecordable();
+ recordable->SetBody("Log Body");
+ processor.OnReceive(std::move(recordable));
+
+ // Verify that the batch of 1 log record sent by processor matches what exporter received
+ EXPECT_EQ(1, batch_size_received);
+ }
+
+ // Test whether the processor's log sent matches the log record received by the exporter
+ EXPECT_EQ(logs_received->size(), num_logs);
+ for (int i = 0; i < num_logs; i++)
+ {
+ EXPECT_EQ("Log Body", logs_received->at(i)->GetBody());
+ }
+}
+
+// Tests behavior when calling the processor's ShutDown() multiple times
+TEST(SimpleLogProcessorTest, ShutdownCalledOnce)
+{
+ // Create a TestExporter
+ int num_shutdowns = 0;
+
+ std::unique_ptr<TestExporter> exporter(new TestExporter(&num_shutdowns, nullptr, nullptr));
+
+ // Create a processor with the previous test exporter
+ SimpleLogProcessor processor(std::move(exporter));
+
+ // The first time processor shutdown is called
+ EXPECT_EQ(0, num_shutdowns);
+ EXPECT_EQ(true, processor.Shutdown());
+ EXPECT_EQ(1, num_shutdowns);
+
+ EXPECT_EQ(true, processor.Shutdown());
+ // Processor::ShutDown(), even if called more than once, should only shutdown exporter once
+ EXPECT_EQ(1, num_shutdowns);
+}
+
+// A test exporter that always returns failure when shut down
+class FailShutDownExporter final : public LogExporter
+{
+public:
+ FailShutDownExporter() {}
+
+ std::unique_ptr<Recordable> MakeRecordable() noexcept override
+ {
+ return std::unique_ptr<Recordable>(new LogRecord());
+ }
+
+ ExportResult Export(const nostd::span<std::unique_ptr<Recordable>> &records) noexcept override
+ {
+ return ExportResult::kSuccess;
+ }
+
+ bool Shutdown(std::chrono::microseconds timeout) noexcept override { return false; }
+};
+
+// Tests for when when processor should fail to shutdown
+TEST(SimpleLogProcessorTest, ShutDownFail)
+{
+ std::unique_ptr<FailShutDownExporter> exporter(new FailShutDownExporter());
+ SimpleLogProcessor processor(std::move(exporter));
+
+ // Expect failure result when exporter fails to shutdown
+ EXPECT_EQ(false, processor.Shutdown());
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/BUILD
new file mode 100644
index 000000000..819a8d225
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/BUILD
@@ -0,0 +1,203 @@
+load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark")
+
+cc_test(
+ name = "meter_provider_sdk_test",
+ srcs = [
+ "meter_provider_sdk_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ "//sdk/src/resource",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "metric_reader_test",
+ srcs = [
+ "metric_reader_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ "//sdk/src/resource",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "view_registry_test",
+ srcs = [
+ "view_registry_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ "//sdk/src/resource",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "aggregation_test",
+ srcs = [
+ "aggregation_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ "//sdk/src/resource",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "sync_metric_storage_test",
+ srcs = [
+ "sync_metric_storage_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ "//sdk/src/resource",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "sync_instruments_test",
+ srcs = [
+ "sync_instruments_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ "//sdk/src/resource",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "async_metric_storage_test",
+ srcs = [
+ "async_metric_storage_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ "//sdk/src/resource",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "observer_result_test",
+ srcs = [
+ "observer_result_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ "//sdk/src/resource",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "multi_metric_storage_test",
+ srcs = [
+ "multi_metric_storage_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ "//sdk/src/resource",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "attributes_processor_test",
+ srcs = [
+ "attributes_processor_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "attributes_hashmap_test",
+ srcs = [
+ "attributes_hashmap_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+otel_cc_benchmark(
+ name = "attributes_processor_benchmark",
+ srcs = [
+ "attributes_processor_benchmark.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ ],
+)
+
+otel_cc_benchmark(
+ name = "attributes_hashmap_benchmark",
+ srcs = [
+ "attributes_hashmap_benchmark.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/CMakeLists.txt
new file mode 100644
index 000000000..faf2f2b49
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/CMakeLists.txt
@@ -0,0 +1,34 @@
+foreach(
+ testname
+ meter_provider_sdk_test
+ view_registry_test
+ aggregation_test
+ attributes_processor_test
+ attributes_hashmap_test
+ sync_metric_storage_test
+ async_metric_storage_test
+ multi_metric_storage_test
+ observer_result_test
+ sync_instruments_test
+ async_instruments_test
+ metric_reader_test
+ periodic_exporting_metric_reader_test)
+ add_executable(${testname} "${testname}.cc")
+ target_link_libraries(
+ ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ opentelemetry_resources opentelemetry_metrics)
+ gtest_add_tests(
+ TARGET ${testname}
+ TEST_PREFIX metrics.
+ TEST_LIST ${testname})
+endforeach()
+
+add_executable(attributes_processor_benchmark attributes_processor_benchmark.cc)
+target_link_libraries(attributes_processor_benchmark benchmark::benchmark
+ ${CMAKE_THREAD_LIBS_INIT} opentelemetry_common)
+
+add_executable(attributes_hashmap_benchmark attributes_hashmap_benchmark.cc)
+target_link_libraries(attributes_hashmap_benchmark benchmark::benchmark
+ ${CMAKE_THREAD_LIBS_INIT} opentelemetry_common)
+
+add_subdirectory(exemplar)
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/aggregation_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/aggregation_test.cc
new file mode 100644
index 000000000..f8051776d
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/aggregation_test.cc
@@ -0,0 +1,182 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include <gtest/gtest.h>
+# include "opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h"
+# include "opentelemetry/sdk/metrics/aggregation/lastvalue_aggregation.h"
+# include "opentelemetry/sdk/metrics/aggregation/sum_aggregation.h"
+
+# include "opentelemetry/nostd/variant.h"
+
+using namespace opentelemetry::sdk::metrics;
+namespace nostd = opentelemetry::nostd;
+TEST(Aggregation, LongSumAggregation)
+{
+ LongSumAggregation aggr;
+ auto data = aggr.ToPoint();
+ ASSERT_TRUE(nostd::holds_alternative<SumPointData>(data));
+ auto sum_data = nostd::get<SumPointData>(data);
+ ASSERT_TRUE(nostd::holds_alternative<long>(sum_data.value_));
+ EXPECT_EQ(nostd::get<long>(sum_data.value_), 0l);
+ EXPECT_NO_THROW(aggr.Aggregate(12l, {}));
+ EXPECT_NO_THROW(aggr.Aggregate(0l, {}));
+ sum_data = nostd::get<SumPointData>(aggr.ToPoint());
+ EXPECT_EQ(nostd::get<long>(sum_data.value_), 12l);
+}
+
+TEST(Aggregation, DoubleSumAggregation)
+{
+ DoubleSumAggregation aggr;
+ auto data = aggr.ToPoint();
+ ASSERT_TRUE(nostd::holds_alternative<SumPointData>(data));
+ auto sum_data = nostd::get<SumPointData>(data);
+ ASSERT_TRUE(nostd::holds_alternative<double>(sum_data.value_));
+ EXPECT_EQ(nostd::get<double>(sum_data.value_), 0);
+ EXPECT_NO_THROW(aggr.Aggregate(12.0, {}));
+ EXPECT_NO_THROW(aggr.Aggregate(1.0, {}));
+ sum_data = nostd::get<SumPointData>(aggr.ToPoint());
+ EXPECT_EQ(nostd::get<double>(sum_data.value_), 13.0);
+}
+
+TEST(Aggregation, LongLastValueAggregation)
+{
+ LongLastValueAggregation aggr;
+ auto data = aggr.ToPoint();
+ ASSERT_TRUE(nostd::holds_alternative<LastValuePointData>(data));
+ auto lastvalue_data = nostd::get<LastValuePointData>(data);
+ ASSERT_TRUE(nostd::holds_alternative<long>(lastvalue_data.value_));
+ EXPECT_EQ(lastvalue_data.is_lastvalue_valid_, false);
+ EXPECT_NO_THROW(aggr.Aggregate(12l, {}));
+ EXPECT_NO_THROW(aggr.Aggregate(1l, {}));
+ lastvalue_data = nostd::get<LastValuePointData>(aggr.ToPoint());
+ EXPECT_EQ(nostd::get<long>(lastvalue_data.value_), 1.0);
+}
+
+TEST(Aggregation, DoubleLastValueAggregation)
+{
+ DoubleLastValueAggregation aggr;
+ auto data = aggr.ToPoint();
+ ASSERT_TRUE(nostd::holds_alternative<LastValuePointData>(data));
+ auto lastvalue_data = nostd::get<LastValuePointData>(data);
+ ASSERT_TRUE(nostd::holds_alternative<double>(lastvalue_data.value_));
+ EXPECT_EQ(lastvalue_data.is_lastvalue_valid_, false);
+ EXPECT_NO_THROW(aggr.Aggregate(12.0, {}));
+ EXPECT_NO_THROW(aggr.Aggregate(1.0, {}));
+ lastvalue_data = nostd::get<LastValuePointData>(aggr.ToPoint());
+ EXPECT_EQ(nostd::get<double>(lastvalue_data.value_), 1.0);
+}
+
+TEST(Aggregation, LongHistogramAggregation)
+{
+ LongHistogramAggregation aggr;
+ auto data = aggr.ToPoint();
+ ASSERT_TRUE(nostd::holds_alternative<HistogramPointData>(data));
+ auto histogram_data = nostd::get<HistogramPointData>(data);
+ ASSERT_TRUE(nostd::holds_alternative<long>(histogram_data.sum_));
+ ASSERT_TRUE(nostd::holds_alternative<std::list<long>>(histogram_data.boundaries_));
+ EXPECT_EQ(nostd::get<long>(histogram_data.sum_), 0);
+ EXPECT_EQ(histogram_data.count_, 0);
+ EXPECT_NO_THROW(aggr.Aggregate(12l, {})); // lies in fourth bucket
+ EXPECT_NO_THROW(aggr.Aggregate(100l, {})); // lies in eight bucket
+ histogram_data = nostd::get<HistogramPointData>(aggr.ToPoint());
+ EXPECT_EQ(nostd::get<long>(histogram_data.sum_), 112);
+ EXPECT_EQ(histogram_data.count_, 2);
+ EXPECT_EQ(histogram_data.counts_[3], 1);
+ EXPECT_EQ(histogram_data.counts_[7], 1);
+ EXPECT_NO_THROW(aggr.Aggregate(13l, {})); // lies in fourth bucket
+ EXPECT_NO_THROW(aggr.Aggregate(252l, {})); // lies in ninth bucket
+ histogram_data = nostd::get<HistogramPointData>(aggr.ToPoint());
+ EXPECT_EQ(histogram_data.count_, 4);
+ EXPECT_EQ(histogram_data.counts_[3], 2);
+ EXPECT_EQ(histogram_data.counts_[8], 1);
+
+ // Merge
+ LongHistogramAggregation aggr1;
+ aggr1.Aggregate(1l, {});
+ aggr1.Aggregate(11l, {});
+ aggr1.Aggregate(26l, {});
+
+ LongHistogramAggregation aggr2;
+ aggr2.Aggregate(2l, {});
+ aggr2.Aggregate(3l, {});
+ aggr2.Aggregate(13l, {});
+ aggr2.Aggregate(28l, {});
+ aggr2.Aggregate(105l, {});
+
+ auto aggr3 = aggr1.Merge(aggr2);
+ histogram_data = nostd::get<HistogramPointData>(aggr3->ToPoint());
+
+ EXPECT_EQ(histogram_data.count_, 8); // 3 each from aggr1 and aggr2
+ EXPECT_EQ(histogram_data.counts_[1], 3); // 1, 2, 3
+ EXPECT_EQ(histogram_data.counts_[3], 2); // 11, 13
+ EXPECT_EQ(histogram_data.counts_[4], 2); // 25, 28
+ EXPECT_EQ(histogram_data.counts_[7], 1); // 105
+
+ // Diff
+ auto aggr4 = aggr1.Diff(aggr2);
+ histogram_data = nostd::get<HistogramPointData>(aggr4->ToPoint());
+ EXPECT_EQ(histogram_data.count_, 2); // aggr2:5 - aggr1:3
+ EXPECT_EQ(histogram_data.counts_[1], 1); // aggr2(2, 3) - aggr1(1)
+ EXPECT_EQ(histogram_data.counts_[3], 0); // aggr2(13) - aggr1(11)
+ EXPECT_EQ(histogram_data.counts_[4], 0); // aggr2(28) - aggr1(25)
+ EXPECT_EQ(histogram_data.counts_[7], 1); // aggr2(105) - aggr1(0)
+}
+
+TEST(Aggregation, DoubleHistogramAggregation)
+{
+ DoubleHistogramAggregation aggr;
+ auto data = aggr.ToPoint();
+ ASSERT_TRUE(nostd::holds_alternative<HistogramPointData>(data));
+ auto histogram_data = nostd::get<HistogramPointData>(data);
+ ASSERT_TRUE(nostd::holds_alternative<double>(histogram_data.sum_));
+ ASSERT_TRUE(nostd::holds_alternative<std::list<double>>(histogram_data.boundaries_));
+ EXPECT_EQ(nostd::get<double>(histogram_data.sum_), 0);
+ EXPECT_EQ(histogram_data.count_, 0);
+ EXPECT_NO_THROW(aggr.Aggregate(12.0, {})); // lies in fourth bucket
+ EXPECT_NO_THROW(aggr.Aggregate(100.0, {})); // lies in eight bucket
+ histogram_data = nostd::get<HistogramPointData>(aggr.ToPoint());
+ EXPECT_EQ(nostd::get<double>(histogram_data.sum_), 112);
+ EXPECT_EQ(histogram_data.count_, 2);
+ EXPECT_EQ(histogram_data.counts_[3], 1);
+ EXPECT_EQ(histogram_data.counts_[7], 1);
+ EXPECT_NO_THROW(aggr.Aggregate(13.0, {})); // lies in fourth bucket
+ EXPECT_NO_THROW(aggr.Aggregate(252.0, {})); // lies in ninth bucket
+ histogram_data = nostd::get<HistogramPointData>(aggr.ToPoint());
+ EXPECT_EQ(histogram_data.count_, 4);
+ EXPECT_EQ(histogram_data.counts_[3], 2);
+ EXPECT_EQ(histogram_data.counts_[8], 1);
+ EXPECT_EQ(nostd::get<double>(histogram_data.sum_), 377);
+
+ // Merge
+ DoubleHistogramAggregation aggr1;
+ aggr1.Aggregate(1.0, {});
+ aggr1.Aggregate(11.0, {});
+ aggr1.Aggregate(25.1, {});
+
+ DoubleHistogramAggregation aggr2;
+ aggr2.Aggregate(2.0, {});
+ aggr2.Aggregate(3.0, {});
+ aggr2.Aggregate(13.0, {});
+ aggr2.Aggregate(28.1, {});
+ aggr2.Aggregate(105.0, {});
+
+ auto aggr3 = aggr1.Merge(aggr2);
+ histogram_data = nostd::get<HistogramPointData>(aggr3->ToPoint());
+
+ EXPECT_EQ(histogram_data.count_, 8); // 3 each from aggr1 and aggr2
+ EXPECT_EQ(histogram_data.counts_[1], 3); // 1.0, 2.0, 3.0
+ EXPECT_EQ(histogram_data.counts_[3], 2); // 11.0, 13.0
+ EXPECT_EQ(histogram_data.counts_[4], 2); // 25.1, 28.1
+ EXPECT_EQ(histogram_data.counts_[7], 1); // 105.0
+
+ // Diff
+ auto aggr4 = aggr1.Diff(aggr2);
+ histogram_data = nostd::get<HistogramPointData>(aggr4->ToPoint());
+ EXPECT_EQ(histogram_data.count_, 2); // aggr2:5 - aggr1:3
+ EXPECT_EQ(histogram_data.counts_[1], 1); // aggr2(2.0, 3.0) - aggr1(1.0)
+ EXPECT_EQ(histogram_data.counts_[3], 0); // aggr2(13.0) - aggr1(11.0)
+ EXPECT_EQ(histogram_data.counts_[4], 0); // aggr2(28.1) - aggr1(25.1)
+ EXPECT_EQ(histogram_data.counts_[7], 1); // aggr2(105.0) - aggr1(0)
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_instruments_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_instruments_test.cc
new file mode 100644
index 000000000..ff9504f78
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_instruments_test.cc
@@ -0,0 +1,60 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/metrics/async_instruments.h"
+
+# include <gtest/gtest.h>
+
+using namespace opentelemetry;
+using namespace opentelemetry::sdk::metrics;
+
+using M = std::map<std::string, std::string>;
+
+void asyc_generate_measurements_long(opentelemetry::metrics::ObserverResult<long> &observer) {}
+
+void asyc_generate_measurements_double(opentelemetry::metrics::ObserverResult<double> &observer) {}
+
+TEST(AsyncInstruments, LongObservableCounter)
+{
+ auto asyc_generate_meas_long = [](opentelemetry::metrics::ObserverResult<long> &observer) {};
+ EXPECT_NO_THROW(
+ LongObservableCounter counter("long_counter", asyc_generate_meas_long, "description", "1"));
+}
+
+TEST(AsyncInstruments, DoubleObservableCounter)
+{
+ auto asyc_generate_meas_double = [](opentelemetry::metrics::ObserverResult<double> &observer) {};
+ EXPECT_NO_THROW(DoubleObservableCounter counter("long_counter", asyc_generate_meas_double,
+ "description", "1"));
+}
+
+TEST(AsyncInstruments, LongObservableGauge)
+{
+ auto asyc_generate_meas_long = [](opentelemetry::metrics::ObserverResult<long> &observer) {};
+ EXPECT_NO_THROW(
+ LongObservableGauge counter("long_counter", asyc_generate_meas_long, "description", "1"));
+}
+
+TEST(AsyncInstruments, DoubleObservableGauge)
+{
+ auto asyc_generate_meas_double = [](opentelemetry::metrics::ObserverResult<double> &observer) {};
+ EXPECT_NO_THROW(
+ DoubleObservableGauge counter("long_counter", asyc_generate_meas_double, "description", "1"));
+}
+
+TEST(AsyncInstruments, LongObservableUpDownCounter)
+{
+ auto asyc_generate_meas_long = [](opentelemetry::metrics::ObserverResult<long> &observer) {};
+ EXPECT_NO_THROW(LongObservableUpDownCounter counter("long_counter", asyc_generate_meas_long,
+ "description", "1"));
+}
+
+TEST(AsyncInstruments, DoubleObservableUpDownCounter)
+{
+ auto asyc_generate_meas_double = [](opentelemetry::metrics::ObserverResult<double> &observer) {};
+ EXPECT_NO_THROW(DoubleObservableUpDownCounter counter("long_counter", asyc_generate_meas_double,
+ "description", "1"));
+}
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_metric_storage_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_metric_storage_test.cc
new file mode 100644
index 000000000..2be5332a8
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_metric_storage_test.cc
@@ -0,0 +1,132 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/metrics/state/async_metric_storage.h"
+# include "opentelemetry/common/key_value_iterable_view.h"
+# include "opentelemetry/sdk/metrics/instruments.h"
+# include "opentelemetry/sdk/metrics/meter_context.h"
+# include "opentelemetry/sdk/metrics/metric_exporter.h"
+# include "opentelemetry/sdk/metrics/metric_reader.h"
+# include "opentelemetry/sdk/metrics/observer_result.h"
+# include "opentelemetry/sdk/metrics/state/metric_collector.h"
+
+# include <gtest/gtest.h>
+# include <vector>
+
+using namespace opentelemetry::sdk::metrics;
+using namespace opentelemetry::sdk::instrumentationlibrary;
+using namespace opentelemetry::sdk::resource;
+
+using namespace opentelemetry::sdk::metrics;
+using namespace opentelemetry::common;
+using M = std::map<std::string, std::string>;
+
+class MockCollectorHandle : public CollectorHandle
+{
+public:
+ MockCollectorHandle(AggregationTemporality temp) : temporality(temp) {}
+
+ AggregationTemporality GetAggregationTemporality() noexcept override { return temporality; }
+
+private:
+ AggregationTemporality temporality;
+};
+
+class WritableMetricStorageTestFixture : public ::testing::TestWithParam<AggregationTemporality>
+{};
+
+class MeasurementFetcher
+{
+public:
+ static void Fetcher(opentelemetry::metrics::ObserverResult<long> &observer_result,
+ void * /*state*/)
+ {
+ fetch_count++;
+ if (fetch_count == 1)
+ {
+ observer_result.Observe(20l, {{"RequestType", "GET"}});
+ observer_result.Observe(10l, {{"RequestType", "PUT"}});
+ number_of_get += 20l;
+ number_of_put += 10l;
+ }
+ else if (fetch_count == 2)
+ {
+ observer_result.Observe(40l, {{"RequestType", "GET"}});
+ observer_result.Observe(20l, {{"RequestType", "PUT"}});
+ number_of_get += 40l;
+ number_of_put += 20l;
+ }
+ }
+
+ static void init_values()
+ {
+ fetch_count = 0;
+ number_of_get = 0;
+ number_of_put = 0;
+ }
+
+ static size_t fetch_count;
+ static long number_of_get;
+ static long number_of_put;
+ static const size_t number_of_attributes = 2; // GET , PUT
+};
+
+size_t MeasurementFetcher::fetch_count;
+long MeasurementFetcher::number_of_get;
+long MeasurementFetcher::number_of_put;
+const size_t MeasurementFetcher::number_of_attributes;
+
+TEST_P(WritableMetricStorageTestFixture, TestAggregation)
+{
+ MeasurementFetcher::init_values();
+ AggregationTemporality temporality = GetParam();
+
+ InstrumentDescriptor instr_desc = {"name", "desc", "1unit", InstrumentType::kObservableCounter,
+ InstrumentValueType::kLong};
+
+ auto sdk_start_ts = std::chrono::system_clock::now();
+ // Some computation here
+ auto collection_ts = std::chrono::system_clock::now() + std::chrono::seconds(5);
+
+ std::shared_ptr<CollectorHandle> collector(new MockCollectorHandle(temporality));
+ std::vector<std::shared_ptr<CollectorHandle>> collectors;
+ collectors.push_back(collector);
+ size_t count_attributes = 0;
+ long value = 0;
+
+ MeasurementFetcher measurement_fetcher;
+ opentelemetry::sdk::metrics::AsyncMetricStorage<long> storage(instr_desc, AggregationType::kSum,
+ MeasurementFetcher::Fetcher,
+ new DefaultAttributesProcessor());
+
+ storage.Collect(collector.get(), collectors, sdk_start_ts, collection_ts,
+ [&](const MetricData data) {
+ for (auto data_attr : data.point_data_attr_)
+ {
+ auto data = opentelemetry::nostd::get<SumPointData>(data_attr.point_data);
+ if (opentelemetry::nostd::get<std::string>(
+ data_attr.attributes.find("RequestType")->second) == "GET")
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<long>(data.value_),
+ MeasurementFetcher::number_of_get);
+ }
+ else if (opentelemetry::nostd::get<std::string>(
+ data_attr.attributes.find("RequestType")->second) == "PUT")
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<long>(data.value_),
+ MeasurementFetcher::number_of_put);
+ }
+ count_attributes++;
+ }
+ return true;
+ });
+ EXPECT_EQ(MeasurementFetcher::number_of_attributes, count_attributes);
+}
+
+INSTANTIATE_TEST_SUITE_P(WritableMetricStorageTestLong,
+ WritableMetricStorageTestFixture,
+ ::testing::Values(AggregationTemporality::kCumulative,
+ AggregationTemporality::kDelta));
+
+#endif \ No newline at end of file
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_benchmark.cc
new file mode 100644
index 000000000..38d515a7e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_benchmark.cc
@@ -0,0 +1,53 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <benchmark/benchmark.h>
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/common/attributemap_hash.h"
+# include "opentelemetry/sdk/metrics/aggregation/aggregation.h"
+# include "opentelemetry/sdk/metrics/aggregation/drop_aggregation.h"
+# include "opentelemetry/sdk/metrics/instruments.h"
+# include "opentelemetry/sdk/metrics/state/attributes_hashmap.h"
+
+# include <functional>
+# include <vector>
+
+using namespace opentelemetry::sdk::metrics;
+constexpr size_t MAX_THREADS = 500;
+namespace
+{
+
+void BM_AttributseHashMap(benchmark::State &state)
+{
+
+ AttributesHashMap hash_map;
+ std::vector<std::thread> workers;
+ std::vector<MetricAttributes> attributes = {{{"k1", "v1"}, {"k2", "v2"}},
+ {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}}};
+
+ std::function<std::unique_ptr<Aggregation>()> create_default_aggregation =
+ []() -> std::unique_ptr<Aggregation> {
+ return std::unique_ptr<Aggregation>(new DropAggregation);
+ };
+
+ while (state.KeepRunning())
+ {
+ for (size_t i = 0; i < MAX_THREADS; i++)
+ {
+ workers.push_back(std::thread([&]() {
+ hash_map.GetOrSetDefault(attributes[i % 2], create_default_aggregation)->Aggregate(1l);
+ benchmark::DoNotOptimize(hash_map.Has(attributes[i % 2]));
+ }));
+ }
+ }
+
+ for (auto &t : workers)
+ {
+ t.join();
+ }
+}
+
+BENCHMARK(BM_AttributseHashMap);
+} // namespace
+#endif
+BENCHMARK_MAIN(); \ No newline at end of file
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_test.cc
new file mode 100644
index 000000000..610744c8e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_test.cc
@@ -0,0 +1,71 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/metrics/state/attributes_hashmap.h"
+# include <gtest/gtest.h>
+# include "opentelemetry/sdk/metrics/aggregation/drop_aggregation.h"
+# include "opentelemetry/sdk/metrics/instruments.h"
+
+# include <functional>
+
+using namespace opentelemetry::sdk::metrics;
+namespace nostd = opentelemetry::nostd;
+
+TEST(AttributesHashMap, BasicTests)
+{
+
+ // Empty map
+ AttributesHashMap hash_map;
+ EXPECT_EQ(hash_map.Size(), 0);
+ MetricAttributes m1 = {{"k1", "v1"}};
+ EXPECT_EQ(hash_map.Get(m1), nullptr);
+ EXPECT_EQ(hash_map.Has(m1), false);
+
+ // Set
+ std::unique_ptr<Aggregation> aggregation1(
+ new DropAggregation()); // = std::unique_ptr<Aggregation>(new DropAggregation);
+ hash_map.Set(m1, std::move(aggregation1));
+ EXPECT_NO_THROW(hash_map.Get(m1)->Aggregate(1l));
+ EXPECT_EQ(hash_map.Size(), 1);
+ EXPECT_EQ(hash_map.Has(m1), true);
+
+ // Set same key again
+ auto aggregation2 = std::unique_ptr<Aggregation>(new DropAggregation());
+ hash_map.Set(m1, std::move(aggregation2));
+ EXPECT_NO_THROW(hash_map.Get(m1)->Aggregate(1l));
+ EXPECT_EQ(hash_map.Size(), 1);
+ EXPECT_EQ(hash_map.Has(m1), true);
+
+ // Set more enteria
+ auto aggregation3 = std::unique_ptr<Aggregation>(new DropAggregation());
+ MetricAttributes m3 = {{"k1", "v1"}, {"k2", "v2"}};
+ hash_map.Set(m3, std::move(aggregation3));
+ EXPECT_EQ(hash_map.Has(m1), true);
+ EXPECT_EQ(hash_map.Has(m3), true);
+ EXPECT_NO_THROW(hash_map.Get(m3)->Aggregate(1l));
+ EXPECT_EQ(hash_map.Size(), 2);
+
+ // GetOrSetDefault
+ std::function<std::unique_ptr<Aggregation>()> create_default_aggregation =
+ []() -> std::unique_ptr<Aggregation> {
+ return std::unique_ptr<Aggregation>(new DropAggregation);
+ };
+ MetricAttributes m4 = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}};
+ EXPECT_NO_THROW(hash_map.GetOrSetDefault(m4, create_default_aggregation)->Aggregate(1l));
+ EXPECT_EQ(hash_map.Size(), 3);
+
+ // Set attributes with different order - shouldn't create a new entry.
+ MetricAttributes m5 = {{"k2", "v2"}, {"k1", "v1"}};
+ EXPECT_EQ(hash_map.Has(m5), true);
+
+ // GetAllEnteries
+ size_t count = 0;
+ hash_map.GetAllEnteries([&count](const MetricAttributes &attributes, Aggregation &aggregation) {
+ count++;
+ return true;
+ });
+ EXPECT_EQ(count, hash_map.Size());
+}
+
+#endif \ No newline at end of file
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_benchmark.cc
new file mode 100644
index 000000000..d558a668f
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_benchmark.cc
@@ -0,0 +1,27 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <benchmark/benchmark.h>
+#ifndef ENABLE_METRICS_PREVIEW
+# include <map>
+# include "opentelemetry/sdk/metrics/view/attributes_processor.h"
+using namespace opentelemetry::sdk::metrics;
+namespace
+{
+void BM_AttributseProcessorFilter(benchmark::State &state)
+{
+ std::map<std::string, int> attributes = {
+ {"att1", 10}, {"attr1", 20}, {"attr3", 30}, {"attr4", 40}};
+ FilteringAttributesProcessor attributes_processor(
+ {{"attr2", true}, {"attr4", true}, {"attr6", true}});
+ opentelemetry::common::KeyValueIterableView<std::map<std::string, int>> iterable(attributes);
+ while (state.KeepRunning())
+ {
+ auto filtered_attributes = attributes_processor.process(iterable);
+ }
+}
+
+BENCHMARK(BM_AttributseProcessorFilter);
+} // namespace
+#endif
+BENCHMARK_MAIN();
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_test.cc
new file mode 100644
index 000000000..d496cc7b0
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_test.cc
@@ -0,0 +1,49 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/metrics/view/attributes_processor.h"
+# include <gtest/gtest.h>
+
+using namespace opentelemetry::sdk::metrics;
+using namespace opentelemetry::common;
+using namespace opentelemetry::sdk::common;
+
+TEST(AttributesProcessor, FilteringAttributesProcessor)
+{
+ const int kNumFilterAttributes = 3;
+ std::unordered_map<std::string, bool> filter = {
+ {"attr2", true}, {"attr4", true}, {"attr6", true}};
+ const int kNumAttributes = 6;
+ std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3", "attr4", "attr5", "attr6"};
+ int values[kNumAttributes] = {10, 20, 30, 40, 50, 60};
+ std::map<std::string, int> attributes = {{keys[0], values[0]}, {keys[1], values[1]},
+ {keys[2], values[2]}, {keys[3], values[3]},
+ {keys[4], values[4]}, {keys[5], values[5]}};
+ FilteringAttributesProcessor attributes_processor(filter);
+ opentelemetry::common::KeyValueIterableView<std::map<std::string, int>> iterable(attributes);
+ auto filtered_attributes = attributes_processor.process(iterable);
+ for (auto &e : filtered_attributes)
+ {
+ EXPECT_FALSE(filter.find(e.first) == filter.end());
+ }
+ EXPECT_EQ(filter.size(), kNumFilterAttributes);
+}
+
+TEST(AttributesProcessor, FilteringAllAttributesProcessor)
+{
+ const int kNumFilterAttributes = 0;
+ std::unordered_map<std::string, bool> filter = {};
+ const int kNumAttributes = 6;
+ std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3", "attr4", "attr5", "attr6"};
+ int values[kNumAttributes] = {10, 20, 30, 40, 50, 60};
+ std::map<std::string, int> attributes = {{keys[0], values[0]}, {keys[1], values[1]},
+ {keys[2], values[2]}, {keys[3], values[3]},
+ {keys[4], values[4]}, {keys[5], values[5]}};
+ FilteringAttributesProcessor attributes_processor(filter);
+ opentelemetry::common::KeyValueIterableView<std::map<std::string, int>> iterable(attributes);
+ auto filtered_attributes = attributes_processor.process(iterable);
+ EXPECT_EQ(filter.size(), kNumFilterAttributes);
+}
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/BUILD
new file mode 100644
index 000000000..6481f679d
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/BUILD
@@ -0,0 +1,47 @@
+cc_test(
+ name = "no_exemplar_reservoir_test",
+ srcs = [
+ "no_exemplar_reservoir_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "never_sample_filter_test",
+ srcs = [
+ "never_sample_filter_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "always_sample_filter_test",
+ srcs = [
+ "always_sample_filter_test.cc",
+ ],
+ tags = [
+ "metrics",
+ "test",
+ ],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/CMakeLists.txt
new file mode 100644
index 000000000..303294761
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/CMakeLists.txt
@@ -0,0 +1,10 @@
+foreach(testname no_exemplar_reservoir_test never_sample_filter_test
+ always_sample_filter_test)
+ add_executable(${testname} "${testname}.cc")
+ target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT} opentelemetry_metrics)
+ gtest_add_tests(
+ TARGET ${testname}
+ TEST_PREFIX metrics.
+ TEST_LIST ${testname})
+endforeach()
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/always_sample_filter_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/always_sample_filter_test.cc
new file mode 100644
index 000000000..cf4e44995
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/always_sample_filter_test.cc
@@ -0,0 +1,19 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/metrics/exemplar/always_sample_filter.h"
+# include <gtest/gtest.h>
+
+using namespace opentelemetry::sdk::metrics;
+
+TEST(AlwaysSampleFilter, SampleMeasurement)
+{
+ auto filter = opentelemetry::sdk::metrics::AlwaysSampleFilter::GetAlwaysSampleFilter();
+ ASSERT_TRUE(
+ filter->ShouldSampleMeasurement(1.0, MetricAttributes{}, opentelemetry::context::Context{}));
+ ASSERT_TRUE(
+ filter->ShouldSampleMeasurement(1l, MetricAttributes{}, opentelemetry::context::Context{}));
+}
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/never_sample_filter_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/never_sample_filter_test.cc
new file mode 100644
index 000000000..930c57220
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/never_sample_filter_test.cc
@@ -0,0 +1,20 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/context/context.h"
+#ifndef ENABLE_METRICS_PREVIEW
+# include <gtest/gtest.h>
+# include "opentelemetry/sdk/metrics/exemplar/never_sample_filter.h"
+
+using namespace opentelemetry::sdk::metrics;
+
+TEST(NeverSampleFilter, SampleMeasurement)
+{
+ auto filter = opentelemetry::sdk::metrics::NeverSampleFilter::GetNeverSampleFilter();
+ ASSERT_FALSE(
+ filter->ShouldSampleMeasurement(1.0, MetricAttributes{}, opentelemetry::context::Context{}));
+ ASSERT_FALSE(
+ filter->ShouldSampleMeasurement(1l, MetricAttributes{}, opentelemetry::context::Context{}));
+}
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/no_exemplar_reservoir_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/no_exemplar_reservoir_test.cc
new file mode 100644
index 000000000..3e16940ff
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/no_exemplar_reservoir_test.cc
@@ -0,0 +1,22 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h"
+# include <gtest/gtest.h>
+
+using namespace opentelemetry::sdk::metrics;
+
+TEST(NoExemplarReservoir, OfferMeasurement)
+{
+ auto reservoir = opentelemetry::sdk::metrics::NoExemplarReservoir::GetNoExemplarReservoir();
+ EXPECT_NO_THROW(reservoir->OfferMeasurement(1.0, MetricAttributes{},
+ opentelemetry::context::Context{},
+ std::chrono::system_clock::now()));
+ EXPECT_NO_THROW(reservoir->OfferMeasurement(
+ 1l, MetricAttributes{}, opentelemetry::context::Context{}, std::chrono::system_clock::now()));
+ auto exemplar_data = reservoir->CollectAndReset(MetricAttributes{});
+ ASSERT_TRUE(exemplar_data.empty());
+}
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/meter_provider_sdk_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/meter_provider_sdk_test.cc
new file mode 100644
index 000000000..b0fabe50b
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/meter_provider_sdk_test.cc
@@ -0,0 +1,91 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include <gtest/gtest.h>
+# include "opentelemetry/sdk/metrics/export/metric_producer.h"
+# include "opentelemetry/sdk/metrics/meter.h"
+# include "opentelemetry/sdk/metrics/meter_provider.h"
+# include "opentelemetry/sdk/metrics/metric_exporter.h"
+# include "opentelemetry/sdk/metrics/metric_reader.h"
+# include "opentelemetry/sdk/metrics/view/instrument_selector.h"
+# include "opentelemetry/sdk/metrics/view/meter_selector.h"
+
+using namespace opentelemetry::sdk::metrics;
+
+class MockMetricExporter : public MetricExporter
+{
+
+public:
+ MockMetricExporter() = default;
+ opentelemetry::sdk::common::ExportResult Export(const ResourceMetrics &records) noexcept override
+ {
+ return opentelemetry::sdk::common::ExportResult::kSuccess;
+ }
+
+ bool ForceFlush(
+ std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override
+ {
+ return true;
+ }
+
+ bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override
+ {
+ return true;
+ }
+};
+
+class MockMetricReader : public MetricReader
+{
+public:
+ MockMetricReader(std::unique_ptr<MetricExporter> exporter) : exporter_(std::move(exporter)) {}
+ virtual bool OnForceFlush(std::chrono::microseconds timeout) noexcept override { return true; }
+ virtual bool OnShutDown(std::chrono::microseconds timeout) noexcept override { return true; }
+ virtual void OnInitialized() noexcept override {}
+
+private:
+ std::unique_ptr<MetricExporter> exporter_;
+};
+
+TEST(MeterProvider, GetMeter)
+{
+
+ MeterProvider mp1;
+ // std::unique_ptr<View> view{std::unique_ptr<View>()};
+ // MeterProvider mp1(std::move(exporters), std::move(readers), std::move(views);
+ auto m1 = mp1.GetMeter("test");
+ auto m2 = mp1.GetMeter("test");
+ auto m3 = mp1.GetMeter("different", "1.0.0");
+ auto m4 = mp1.GetMeter("");
+ auto m5 = mp1.GetMeter(opentelemetry::nostd::string_view{});
+ auto m6 = mp1.GetMeter("different", "1.0.0", "https://opentelemetry.io/schemas/1.2.0");
+ ASSERT_NE(nullptr, m1);
+ ASSERT_NE(nullptr, m2);
+ ASSERT_NE(nullptr, m3);
+ ASSERT_NE(nullptr, m6);
+
+ // Should return the same instance each time.
+ ASSERT_EQ(m1, m2);
+ ASSERT_NE(m1, m3);
+ ASSERT_EQ(m4, m5);
+ ASSERT_NE(m3, m6);
+
+ // Should be an sdk::trace::Tracer with the processor attached.
+# ifdef OPENTELEMETRY_RTTI_ENABLED
+ auto sdkMeter1 = dynamic_cast<Meter *>(m1.get());
+# else
+ auto sdkMeter1 = static_cast<Meter *>(m1.get());
+# endif
+ ASSERT_NE(nullptr, sdkMeter1);
+ std::unique_ptr<MockMetricExporter> exporter(new MockMetricExporter());
+ std::unique_ptr<MetricReader> reader{new MockMetricReader(std::move(exporter))};
+ ASSERT_NO_THROW(mp1.AddMetricReader(std::move(reader)));
+
+ std::unique_ptr<View> view{std::unique_ptr<View>()};
+ std::unique_ptr<InstrumentSelector> instrument_selector{
+ new InstrumentSelector(InstrumentType::kCounter, "instru1")};
+ std::unique_ptr<MeterSelector> meter_selector{new MeterSelector("name1", "version1", "schema1")};
+ ASSERT_NO_THROW(
+ mp1.AddView(std::move(instrument_selector), std::move(meter_selector), std::move(view)));
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/metric_reader_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/metric_reader_test.cc
new file mode 100644
index 000000000..c9c30853d
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/metric_reader_test.cc
@@ -0,0 +1,39 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/metrics/metric_reader.h"
+# include <gtest/gtest.h>
+# include "opentelemetry/sdk/metrics/meter_context.h"
+# include "opentelemetry/sdk/metrics/metric_exporter.h"
+
+using namespace opentelemetry;
+using namespace opentelemetry::sdk::instrumentationlibrary;
+using namespace opentelemetry::sdk::metrics;
+
+class MockMetricReader : public MetricReader
+{
+public:
+ MockMetricReader(AggregationTemporality aggr_temporality) : MetricReader(aggr_temporality) {}
+
+ virtual bool OnForceFlush(std::chrono::microseconds timeout) noexcept override { return true; }
+ virtual bool OnShutDown(std::chrono::microseconds timeout) noexcept override { return true; }
+ virtual void OnInitialized() noexcept override {}
+};
+
+TEST(MetricReaderTest, BasicTests)
+{
+ AggregationTemporality aggr_temporality = AggregationTemporality::kDelta;
+ std::unique_ptr<MetricReader> metric_reader1(new MockMetricReader(aggr_temporality));
+ EXPECT_EQ(metric_reader1->GetAggregationTemporality(), aggr_temporality);
+
+ std::shared_ptr<MeterContext> meter_context1(new MeterContext());
+ EXPECT_NO_THROW(meter_context1->AddMetricReader(std::move(metric_reader1)));
+
+ std::unique_ptr<MetricReader> metric_reader2(new MockMetricReader(aggr_temporality));
+ std::shared_ptr<MeterContext> meter_context2(new MeterContext());
+ MetricProducer *metric_producer =
+ new MetricCollector(std::move(meter_context2), std::move(metric_reader2));
+ EXPECT_NO_THROW(metric_producer->Collect([](ResourceMetrics &metric_data) { return true; }));
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/multi_metric_storage_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/multi_metric_storage_test.cc
new file mode 100644
index 000000000..d88946485
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/multi_metric_storage_test.cc
@@ -0,0 +1,62 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/metrics/state/multi_metric_storage.h"
+# include "opentelemetry/common/key_value_iterable_view.h"
+# include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h"
+# include "opentelemetry/sdk/metrics/instruments.h"
+
+# include <gtest/gtest.h>
+
+using namespace opentelemetry;
+using namespace opentelemetry::sdk::instrumentationlibrary;
+using namespace opentelemetry::sdk::metrics;
+
+class TestMetricStorage : public WritableMetricStorage
+{
+public:
+ void RecordLong(long value, const opentelemetry::context::Context &context) noexcept override
+ {
+ num_calls_long++;
+ }
+
+ void RecordLong(long value,
+ const opentelemetry::common::KeyValueIterable &attributes,
+ const opentelemetry::context::Context &context) noexcept override
+ {
+ num_calls_long++;
+ }
+
+ void RecordDouble(double value, const opentelemetry::context::Context &context) noexcept override
+ {
+ num_calls_double++;
+ }
+
+ void RecordDouble(double value,
+ const opentelemetry::common::KeyValueIterable &attributes,
+ const opentelemetry::context::Context &context) noexcept override
+ {
+ num_calls_double++;
+ }
+
+ size_t num_calls_long;
+ size_t num_calls_double;
+};
+
+TEST(MultiMetricStorageTest, BasicTests)
+{
+ std::shared_ptr<opentelemetry::sdk::metrics::WritableMetricStorage> storage(
+ new TestMetricStorage());
+ MultiMetricStorage storages{};
+ storages.AddStorage(storage);
+ EXPECT_NO_THROW(storages.RecordLong(10l, opentelemetry::context::Context{}));
+ EXPECT_NO_THROW(storages.RecordLong(20l, opentelemetry::context::Context{}));
+
+ EXPECT_NO_THROW(storages.RecordDouble(10.0, opentelemetry::context::Context{}));
+ EXPECT_NO_THROW(storages.RecordLong(30l, opentelemetry::context::Context{}));
+
+ EXPECT_EQ(static_cast<TestMetricStorage *>(storage.get())->num_calls_long, 3);
+ EXPECT_EQ(static_cast<TestMetricStorage *>(storage.get())->num_calls_double, 1);
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/observer_result_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/observer_result_test.cc
new file mode 100644
index 000000000..a4cc28ae5
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/observer_result_test.cc
@@ -0,0 +1,38 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/metrics/observer_result.h"
+# include "opentelemetry/sdk/metrics/view/attributes_processor.h"
+
+# include <gtest/gtest.h>
+
+using namespace opentelemetry::sdk::metrics;
+TEST(ObserverResult, BasicTests)
+{
+ const AttributesProcessor *attributes_processor = new DefaultAttributesProcessor();
+
+ ObserverResult<long> observer_result(attributes_processor);
+
+ observer_result.Observe(10l);
+ observer_result.Observe(20l);
+ EXPECT_EQ(observer_result.GetMeasurements().size(), 1);
+
+ std::map<std::string, int64_t> m1 = {{"k2", 12}};
+ observer_result.Observe(
+ 30l, opentelemetry::common::KeyValueIterableView<std::map<std::string, int64_t>>(m1));
+ EXPECT_EQ(observer_result.GetMeasurements().size(), 2);
+
+ observer_result.Observe(
+ 40l, opentelemetry::common::KeyValueIterableView<std::map<std::string, int64_t>>(m1));
+ EXPECT_EQ(observer_result.GetMeasurements().size(), 2);
+
+ std::map<std::string, int64_t> m2 = {{"k2", 12}, {"k4", 12}};
+ observer_result.Observe(
+ 40l, opentelemetry::common::KeyValueIterableView<std::map<std::string, int64_t>>(m2));
+ EXPECT_EQ(observer_result.GetMeasurements().size(), 3);
+
+ delete attributes_processor;
+}
+
+#endif \ No newline at end of file
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/periodic_exporting_metric_reader_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/periodic_exporting_metric_reader_test.cc
new file mode 100644
index 000000000..5219f3110
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/periodic_exporting_metric_reader_test.cc
@@ -0,0 +1,81 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+
+# include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h"
+# include "opentelemetry/sdk/metrics/export/metric_producer.h"
+# include "opentelemetry/sdk/metrics/metric_exporter.h"
+
+# include <gtest/gtest.h>
+
+using namespace opentelemetry;
+using namespace opentelemetry::sdk::instrumentationlibrary;
+using namespace opentelemetry::sdk::metrics;
+
+class MockPushMetricExporter : public MetricExporter
+{
+public:
+ opentelemetry::sdk::common::ExportResult Export(const ResourceMetrics &record) noexcept override
+ {
+ records_.push_back(record);
+ return opentelemetry::sdk::common::ExportResult::kSuccess;
+ }
+
+ bool ForceFlush(
+ std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override
+ {
+ return false;
+ }
+
+ bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override
+ {
+ return true;
+ }
+
+ size_t GetDataCount() { return records_.size(); }
+
+private:
+ std::vector<ResourceMetrics> records_;
+};
+
+class MockMetricProducer : public MetricProducer
+{
+public:
+ MockMetricProducer(std::chrono::microseconds sleep_ms = std::chrono::microseconds::zero())
+ : sleep_ms_{sleep_ms}, data_sent_size_(0)
+ {}
+
+ bool Collect(nostd::function_ref<bool(ResourceMetrics &)> callback) noexcept override
+ {
+ std::this_thread::sleep_for(sleep_ms_);
+ data_sent_size_++;
+ ResourceMetrics data;
+ callback(data);
+ return true;
+ }
+
+ size_t GetDataCount() { return data_sent_size_; }
+
+private:
+ std::chrono::microseconds sleep_ms_;
+ size_t data_sent_size_;
+};
+
+TEST(PeriodicExporingMetricReader, BasicTests)
+{
+ std::unique_ptr<MetricExporter> exporter(new MockPushMetricExporter());
+ PeriodicExportingMetricReaderOptions options;
+ options.export_timeout_millis = std::chrono::milliseconds(200);
+ options.export_interval_millis = std::chrono::milliseconds(500);
+ auto exporter_ptr = exporter.get();
+ PeriodicExportingMetricReader reader(std::move(exporter), options);
+ MockMetricProducer producer;
+ reader.SetMetricProducer(&producer);
+ std::this_thread::sleep_for(std::chrono::milliseconds(2000));
+ reader.Shutdown();
+ EXPECT_EQ(static_cast<MockPushMetricExporter *>(exporter_ptr)->GetDataCount(),
+ static_cast<MockMetricProducer *>(&producer)->GetDataCount());
+}
+
+#endif \ No newline at end of file
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_instruments_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_instruments_test.cc
new file mode 100644
index 000000000..1a590d8d2
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_instruments_test.cc
@@ -0,0 +1,138 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/metrics/sync_instruments.h"
+# include "opentelemetry/context/context.h"
+# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h"
+# include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h"
+# include "opentelemetry/sdk/metrics/state/multi_metric_storage.h"
+
+# include <gtest/gtest.h>
+# include <cmath>
+# include <limits>
+
+using namespace opentelemetry;
+using namespace opentelemetry::sdk::instrumentationlibrary;
+using namespace opentelemetry::sdk::metrics;
+
+auto instrumentation_library = InstrumentationLibrary::Create("opentelemetry-cpp", "0.1.0");
+
+using M = std::map<std::string, std::string>;
+
+TEST(SyncInstruments, LongCounter)
+{
+ InstrumentDescriptor instrument_descriptor = {
+ "long_counter", "description", "1", InstrumentType::kCounter, InstrumentValueType::kLong};
+ std::unique_ptr<WritableMetricStorage> metric_storage(new MultiMetricStorage());
+ LongCounter counter(instrument_descriptor, std::move(metric_storage));
+ EXPECT_NO_THROW(counter.Add(10l));
+ EXPECT_NO_THROW(counter.Add(10l, opentelemetry::context::Context{}));
+
+ EXPECT_NO_THROW(counter.Add(
+ 10l, opentelemetry::common::KeyValueIterableView<M>({{"abc", "123"}, {"xyz", "456"}})));
+ EXPECT_NO_THROW(counter.Add(
+ 10l, opentelemetry::common::KeyValueIterableView<M>({{"abc", "123"}, {"xyz", "456"}}),
+ opentelemetry::context::Context{}));
+ EXPECT_NO_THROW(counter.Add(10l, opentelemetry::common::KeyValueIterableView<M>({})));
+ EXPECT_NO_THROW(counter.Add(10l, opentelemetry::common::KeyValueIterableView<M>({}),
+ opentelemetry::context::Context{}));
+}
+
+TEST(SyncInstruments, DoubleCounter)
+{
+ InstrumentDescriptor instrument_descriptor = {
+ "double_counter", "description", "1", InstrumentType::kCounter, InstrumentValueType::kDouble};
+ std::unique_ptr<WritableMetricStorage> metric_storage(new MultiMetricStorage());
+ DoubleCounter counter(instrument_descriptor, std::move(metric_storage));
+ EXPECT_NO_THROW(counter.Add(10.10));
+ EXPECT_NO_THROW(counter.Add(10.10, opentelemetry::context::Context{}));
+
+ EXPECT_NO_THROW(counter.Add(
+ 10.10, opentelemetry::common::KeyValueIterableView<M>({{"abc", "123"}, {"xyz", "456"}})));
+ EXPECT_NO_THROW(counter.Add(
+ 10.10, opentelemetry::common::KeyValueIterableView<M>({{"abc", "123"}, {"xyz", "456"}}),
+ opentelemetry::context::Context{}));
+ EXPECT_NO_THROW(counter.Add(10.10, opentelemetry::common::KeyValueIterableView<M>({})));
+ EXPECT_NO_THROW(counter.Add(10.10, opentelemetry::common::KeyValueIterableView<M>({}),
+ opentelemetry::context::Context{}));
+}
+
+TEST(SyncInstruments, LongUpDownCounter)
+{
+ InstrumentDescriptor instrument_descriptor = {"long_updowncounter", "description", "1",
+ InstrumentType::kUpDownCounter,
+ InstrumentValueType::kLong};
+ std::unique_ptr<WritableMetricStorage> metric_storage(new MultiMetricStorage());
+ LongUpDownCounter counter(instrument_descriptor, std::move(metric_storage));
+ EXPECT_NO_THROW(counter.Add(10l));
+ EXPECT_NO_THROW(counter.Add(10l, opentelemetry::context::Context{}));
+
+ EXPECT_NO_THROW(counter.Add(
+ 10l, opentelemetry::common::KeyValueIterableView<M>({{"abc", "123"}, {"xyz", "456"}})));
+ EXPECT_NO_THROW(counter.Add(
+ 10l, opentelemetry::common::KeyValueIterableView<M>({{"abc", "123"}, {"xyz", "456"}}),
+ opentelemetry::context::Context{}));
+ EXPECT_NO_THROW(counter.Add(10l, opentelemetry::common::KeyValueIterableView<M>({})));
+ EXPECT_NO_THROW(counter.Add(10l, opentelemetry::common::KeyValueIterableView<M>({}),
+ opentelemetry::context::Context{}));
+}
+
+TEST(SyncInstruments, DoubleUpDownCounter)
+{
+ InstrumentDescriptor instrument_descriptor = {"double_updowncounter", "description", "1",
+ InstrumentType::kUpDownCounter,
+ InstrumentValueType::kDouble};
+ std::unique_ptr<WritableMetricStorage> metric_storage(new MultiMetricStorage());
+ DoubleUpDownCounter counter(instrument_descriptor, std::move(metric_storage));
+ EXPECT_NO_THROW(counter.Add(10.10));
+ EXPECT_NO_THROW(counter.Add(10.10, opentelemetry::context::Context{}));
+
+ EXPECT_NO_THROW(counter.Add(
+ 10.10, opentelemetry::common::KeyValueIterableView<M>({{"abc", "123"}, {"xyz", "456"}}),
+ opentelemetry::context::Context{}));
+ EXPECT_NO_THROW(counter.Add(
+ 10.10, opentelemetry::common::KeyValueIterableView<M>({{"abc", "123"}, {"xyz", "456"}})));
+ EXPECT_NO_THROW(counter.Add(10.10, opentelemetry::common::KeyValueIterableView<M>({}),
+ opentelemetry::context::Context{}));
+ EXPECT_NO_THROW(counter.Add(10.10, opentelemetry::common::KeyValueIterableView<M>({})));
+}
+
+TEST(SyncInstruments, LongHistogram)
+{
+ InstrumentDescriptor instrument_descriptor = {
+ "long_histogram", "description", "1", InstrumentType::kHistogram, InstrumentValueType::kLong};
+ std::unique_ptr<WritableMetricStorage> metric_storage(new MultiMetricStorage());
+ LongHistogram counter(instrument_descriptor, std::move(metric_storage));
+ EXPECT_NO_THROW(counter.Record(10l, opentelemetry::context::Context{}));
+ EXPECT_NO_THROW(counter.Record(-10l, opentelemetry::context::Context{})); // This is ignored
+
+ EXPECT_NO_THROW(counter.Record(
+ 10l, opentelemetry::common::KeyValueIterableView<M>({{"abc", "123"}, {"xyz", "456"}}),
+ opentelemetry::context::Context{}));
+ EXPECT_NO_THROW(counter.Record(10l, opentelemetry::common::KeyValueIterableView<M>({}),
+ opentelemetry::context::Context{}));
+}
+
+TEST(SyncInstruments, DoubleHistogram)
+{
+ InstrumentDescriptor instrument_descriptor = {"double_histogram", "description", "1",
+ InstrumentType::kHistogram,
+ InstrumentValueType::kDouble};
+ std::unique_ptr<WritableMetricStorage> metric_storage(new MultiMetricStorage());
+ DoubleHistogram counter(instrument_descriptor, std::move(metric_storage));
+ EXPECT_NO_THROW(counter.Record(10.10, opentelemetry::context::Context{}));
+ EXPECT_NO_THROW(counter.Record(-10.10, opentelemetry::context::Context{})); // This is ignored.
+ EXPECT_NO_THROW(counter.Record(std::numeric_limits<double>::quiet_NaN(),
+ opentelemetry::context::Context{})); // This is ignored too
+ EXPECT_NO_THROW(counter.Record(std::numeric_limits<double>::infinity(),
+ opentelemetry::context::Context{})); // This is ignored too
+
+ EXPECT_NO_THROW(counter.Record(
+ 10.10, opentelemetry::common::KeyValueIterableView<M>({{"abc", "123"}, {"xyz", "456"}}),
+ opentelemetry::context::Context{}));
+ EXPECT_NO_THROW(counter.Record(10.10, opentelemetry::common::KeyValueIterableView<M>({}),
+ opentelemetry::context::Context{}));
+}
+
+#endif \ No newline at end of file
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_metric_storage_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_metric_storage_test.cc
new file mode 100644
index 000000000..7dfc4f947
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_metric_storage_test.cc
@@ -0,0 +1,246 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/metrics/state/sync_metric_storage.h"
+# include "opentelemetry/common/key_value_iterable_view.h"
+# include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h"
+# include "opentelemetry/sdk/metrics/instruments.h"
+# include "opentelemetry/sdk/metrics/view/attributes_processor.h"
+
+# include <gtest/gtest.h>
+# include <map>
+
+using namespace opentelemetry::sdk::metrics;
+using namespace opentelemetry::common;
+using M = std::map<std::string, std::string>;
+
+class MockCollectorHandle : public CollectorHandle
+{
+public:
+ MockCollectorHandle(AggregationTemporality temp) : temporality(temp) {}
+
+ AggregationTemporality GetAggregationTemporality() noexcept override { return temporality; }
+
+private:
+ AggregationTemporality temporality;
+};
+
+class WritableMetricStorageTestFixture : public ::testing::TestWithParam<AggregationTemporality>
+{};
+
+TEST_P(WritableMetricStorageTestFixture, LongSumAggregation)
+{
+ AggregationTemporality temporality = GetParam();
+ auto sdk_start_ts = std::chrono::system_clock::now();
+ long expected_total_get_requests = 0;
+ long expected_total_put_requests = 0;
+ InstrumentDescriptor instr_desc = {"name", "desc", "1unit", InstrumentType::kCounter,
+ InstrumentValueType::kLong};
+ std::map<std::string, std::string> attributes_get = {{"RequestType", "GET"}};
+ std::map<std::string, std::string> attributes_put = {{"RequestType", "PUT"}};
+
+ opentelemetry::sdk::metrics::SyncMetricStorage storage(
+ instr_desc, AggregationType::kSum, new DefaultAttributesProcessor(),
+ NoExemplarReservoir::GetNoExemplarReservoir());
+
+ storage.RecordLong(10l, KeyValueIterableView<std::map<std::string, std::string>>(attributes_get),
+ opentelemetry::context::Context{});
+ expected_total_get_requests += 10;
+
+ EXPECT_NO_THROW(storage.RecordLong(
+ 30l, KeyValueIterableView<std::map<std::string, std::string>>(attributes_put),
+ opentelemetry::context::Context{}));
+ expected_total_put_requests += 30;
+
+ storage.RecordLong(20l, KeyValueIterableView<std::map<std::string, std::string>>(attributes_get),
+ opentelemetry::context::Context{});
+ expected_total_get_requests += 20;
+
+ EXPECT_NO_THROW(storage.RecordLong(
+ 40l, KeyValueIterableView<std::map<std::string, std::string>>(attributes_put),
+ opentelemetry::context::Context{}));
+ expected_total_put_requests += 40;
+
+ std::shared_ptr<CollectorHandle> collector(new MockCollectorHandle(temporality));
+ std::vector<std::shared_ptr<CollectorHandle>> collectors;
+ collectors.push_back(collector);
+
+ // Some computation here
+ auto collection_ts = std::chrono::system_clock::now();
+ size_t count_attributes = 0;
+ storage.Collect(
+ collector.get(), collectors, sdk_start_ts, collection_ts, [&](const MetricData data) {
+ for (auto data_attr : data.point_data_attr_)
+ {
+ auto data = opentelemetry::nostd::get<SumPointData>(data_attr.point_data);
+ if (opentelemetry::nostd::get<std::string>(
+ data_attr.attributes.find("RequestType")->second) == "GET")
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<long>(data.value_), expected_total_get_requests);
+ count_attributes++;
+ }
+ else if (opentelemetry::nostd::get<std::string>(
+ data_attr.attributes.find("RequestType")->second) == "PUT")
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<long>(data.value_), expected_total_put_requests);
+ count_attributes++;
+ }
+ }
+ return true;
+ });
+
+ // In case of delta temporarily, subsequent collection would contain new data points, so resetting
+ // the counts
+ if (temporality == AggregationTemporality::kDelta)
+ {
+ expected_total_get_requests = 0;
+ expected_total_put_requests = 0;
+ }
+
+ EXPECT_NO_THROW(storage.RecordLong(
+ 50l, KeyValueIterableView<std::map<std::string, std::string>>(attributes_get),
+ opentelemetry::context::Context{}));
+ expected_total_get_requests += 50;
+ EXPECT_NO_THROW(storage.RecordLong(
+ 40l, KeyValueIterableView<std::map<std::string, std::string>>(attributes_put),
+ opentelemetry::context::Context{}));
+ expected_total_put_requests += 40;
+
+ collection_ts = std::chrono::system_clock::now();
+ count_attributes = 0;
+ storage.Collect(
+ collector.get(), collectors, sdk_start_ts, collection_ts, [&](const MetricData data) {
+ for (auto data_attr : data.point_data_attr_)
+ {
+ auto data = opentelemetry::nostd::get<SumPointData>(data_attr.point_data);
+ if (opentelemetry::nostd::get<std::string>(
+ data_attr.attributes.find("RequestType")->second) == "GET")
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<long>(data.value_), expected_total_get_requests);
+ count_attributes++;
+ }
+ else if (opentelemetry::nostd::get<std::string>(
+ data_attr.attributes.find("RequestType")->second) == "PUT")
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<long>(data.value_), expected_total_put_requests);
+ count_attributes++;
+ }
+ }
+ return true;
+ });
+}
+INSTANTIATE_TEST_SUITE_P(WritableMetricStorageTestLong,
+ WritableMetricStorageTestFixture,
+ ::testing::Values(AggregationTemporality::kCumulative,
+ AggregationTemporality::kDelta));
+
+TEST_P(WritableMetricStorageTestFixture, DoubleSumAggregation)
+{
+ AggregationTemporality temporality = GetParam();
+ auto sdk_start_ts = std::chrono::system_clock::now();
+ double expected_total_get_requests = 0;
+ double expected_total_put_requests = 0;
+ InstrumentDescriptor instr_desc = {"name", "desc", "1unit", InstrumentType::kCounter,
+ InstrumentValueType::kDouble};
+ std::map<std::string, std::string> attributes_get = {{"RequestType", "GET"}};
+ std::map<std::string, std::string> attributes_put = {{"RequestType", "PUT"}};
+
+ opentelemetry::sdk::metrics::SyncMetricStorage storage(
+ instr_desc, AggregationType::kSum, new DefaultAttributesProcessor(),
+ NoExemplarReservoir::GetNoExemplarReservoir());
+
+ storage.RecordDouble(10.0,
+ KeyValueIterableView<std::map<std::string, std::string>>(attributes_get),
+ opentelemetry::context::Context{});
+ expected_total_get_requests += 10;
+
+ EXPECT_NO_THROW(storage.RecordDouble(
+ 30.0, KeyValueIterableView<std::map<std::string, std::string>>(attributes_put),
+ opentelemetry::context::Context{}));
+ expected_total_put_requests += 30;
+
+ storage.RecordDouble(20.0,
+ KeyValueIterableView<std::map<std::string, std::string>>(attributes_get),
+ opentelemetry::context::Context{});
+ expected_total_get_requests += 20;
+
+ EXPECT_NO_THROW(storage.RecordDouble(
+ 40.0, KeyValueIterableView<std::map<std::string, std::string>>(attributes_put),
+ opentelemetry::context::Context{}));
+ expected_total_put_requests += 40;
+
+ std::shared_ptr<CollectorHandle> collector(new MockCollectorHandle(temporality));
+ std::vector<std::shared_ptr<CollectorHandle>> collectors;
+ collectors.push_back(collector);
+
+ // Some computation here
+ auto collection_ts = std::chrono::system_clock::now();
+ size_t count_attributes = 0;
+ storage.Collect(
+ collector.get(), collectors, sdk_start_ts, collection_ts, [&](const MetricData data) {
+ for (auto data_attr : data.point_data_attr_)
+ {
+ auto data = opentelemetry::nostd::get<SumPointData>(data_attr.point_data);
+ if (opentelemetry::nostd::get<std::string>(
+ data_attr.attributes.find("RequestType")->second) == "GET")
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<double>(data.value_), expected_total_get_requests);
+ count_attributes++;
+ }
+ else if (opentelemetry::nostd::get<std::string>(
+ data_attr.attributes.find("RequestType")->second) == "PUT")
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<double>(data.value_), expected_total_put_requests);
+ count_attributes++;
+ }
+ }
+ return true;
+ });
+
+ // In case of delta temporarily, subsequent collection would contain new data points, so resetting
+ // the counts
+ if (temporality == AggregationTemporality::kDelta)
+ {
+ expected_total_get_requests = 0;
+ expected_total_put_requests = 0;
+ }
+
+ EXPECT_NO_THROW(storage.RecordDouble(
+ 50.0, KeyValueIterableView<std::map<std::string, std::string>>(attributes_get),
+ opentelemetry::context::Context{}));
+ expected_total_get_requests += 50;
+ EXPECT_NO_THROW(storage.RecordDouble(
+ 40.0, KeyValueIterableView<std::map<std::string, std::string>>(attributes_put),
+ opentelemetry::context::Context{}));
+ expected_total_put_requests += 40;
+
+ collection_ts = std::chrono::system_clock::now();
+ count_attributes = 0;
+ storage.Collect(
+ collector.get(), collectors, sdk_start_ts, collection_ts, [&](const MetricData data) {
+ for (auto data_attr : data.point_data_attr_)
+ {
+ auto data = opentelemetry::nostd::get<SumPointData>(data_attr.point_data);
+ if (opentelemetry::nostd::get<std::string>(
+ data_attr.attributes.find("RequestType")->second) == "GET")
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<double>(data.value_), expected_total_get_requests);
+ count_attributes++;
+ }
+ else if (opentelemetry::nostd::get<std::string>(
+ data_attr.attributes.find("RequestType")->second) == "PUT")
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<double>(data.value_), expected_total_put_requests);
+ count_attributes++;
+ }
+ }
+ return true;
+ });
+}
+INSTANTIATE_TEST_SUITE_P(WritableMetricStorageTestDouble,
+ WritableMetricStorageTestFixture,
+ ::testing::Values(AggregationTemporality::kCumulative,
+ AggregationTemporality::kDelta));
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/view_registry_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/view_registry_test.cc
new file mode 100644
index 000000000..8151d3754
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/view_registry_test.cc
@@ -0,0 +1,82 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/sdk/metrics/view/view_registry.h"
+# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h"
+# include "opentelemetry/sdk/metrics/instruments.h"
+# include "opentelemetry/sdk/metrics/view/predicate.h"
+
+# include <gtest/gtest.h>
+
+using namespace opentelemetry::sdk::metrics;
+using namespace opentelemetry::sdk::instrumentationlibrary;
+
+TEST(ViewRegistry, FindViewsEmptyRegistry)
+{
+ InstrumentDescriptor default_instrument_descriptor = {
+ "test_name", // name
+ "test_descr", // description
+ "1", // unit
+ InstrumentType::kCounter, // instrument type
+ InstrumentValueType::kLong};
+
+ auto default_instrumentation_lib =
+ InstrumentationLibrary::Create("default", "1.0.0", "https://opentelemetry.io/schemas/1.7.0");
+ int count = 0;
+ ViewRegistry registry;
+ auto status =
+ registry.FindViews(default_instrument_descriptor, *default_instrumentation_lib.get(),
+ [&count](const View &view) {
+ count++;
+ EXPECT_EQ(view.GetName(), "otel-default-view");
+ EXPECT_EQ(view.GetDescription(), "");
+ EXPECT_EQ(view.GetAggregationType(), AggregationType::kDefault);
+ return true;
+ });
+ EXPECT_EQ(count, 1);
+ EXPECT_EQ(status, true);
+}
+
+TEST(ViewRegistry, FindNonExistingView)
+{
+ // Add view
+ const std::string view_name = "test_view";
+ const std::string view_description = "test description";
+ const std::string instrumentation_name = "name1";
+ const std::string instrumentation_version = "version1";
+ const std::string instrumentation_schema = "schema1";
+ const std::string instrument_name = "testname";
+ const InstrumentType instrument_type = InstrumentType::kCounter;
+
+ std::unique_ptr<InstrumentSelector> instrument_selector{
+ new InstrumentSelector(instrument_type, instrument_name)};
+ std::unique_ptr<MeterSelector> meter_selector{
+ new MeterSelector(instrumentation_name, instrumentation_version, instrumentation_schema)};
+ std::unique_ptr<View> view = std::unique_ptr<View>(new View(view_name, view_description));
+
+ ViewRegistry registry;
+ registry.AddView(std::move(instrument_selector), std::move(meter_selector), std::move(view));
+ InstrumentDescriptor default_instrument_descriptor = {instrument_name, // name
+ "test_descr", // description
+ "1", // unit
+ instrument_type, // instrument type
+ InstrumentValueType::kLong};
+
+ auto default_instrumentation_lib = InstrumentationLibrary::Create(
+ instrumentation_name, instrumentation_version, instrumentation_schema);
+ int count = 0;
+ auto status =
+ registry.FindViews(default_instrument_descriptor, *default_instrumentation_lib.get(),
+ [&count, &view_name, &view_description](const View &view) {
+ count++;
+# if HAVE_WORKING_REGEX
+ EXPECT_EQ(view.GetName(), view_name);
+ EXPECT_EQ(view.GetDescription(), view_description);
+# endif
+ return true;
+ });
+ EXPECT_EQ(count, 1);
+ EXPECT_EQ(status, true);
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/BUILD
new file mode 100644
index 000000000..e70f16ee7
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/BUILD
@@ -0,0 +1,12 @@
+cc_test(
+ name = "resource_test",
+ srcs = [
+ "resource_test.cc",
+ ],
+ tags = ["test"],
+ deps = [
+ "//api",
+ "//sdk/src/resource",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/CMakeLists.txt
new file mode 100644
index 000000000..514b98680
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/CMakeLists.txt
@@ -0,0 +1,9 @@
+foreach(testname resource_test)
+ add_executable(${testname} "${testname}.cc")
+ target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT} opentelemetry_resources)
+ gtest_add_tests(
+ TARGET ${testname}
+ TEST_PREFIX resources.
+ TEST_LIST ${testname})
+endforeach()
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/resource_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/resource_test.cc
new file mode 100644
index 000000000..5f058eafb
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/resource_test.cc
@@ -0,0 +1,213 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/resource/resource.h"
+#include "opentelemetry/common/key_value_iterable_view.h"
+#include "opentelemetry/nostd/string_view.h"
+#include "opentelemetry/sdk/common/attribute_utils.h"
+#include "opentelemetry/sdk/resource/experimental_semantic_conventions.h"
+#include "opentelemetry/sdk/resource/resource_detector.h"
+
+#include <cstdlib>
+#include <string>
+#include <unordered_map>
+
+#include <gtest/gtest.h>
+
+#if defined(_MSC_VER)
+# include "opentelemetry/sdk/common/env_variables.h"
+using opentelemetry::sdk::common::setenv;
+using opentelemetry::sdk::common::unsetenv;
+#endif
+
+using namespace opentelemetry::sdk::resource;
+namespace nostd = opentelemetry::nostd;
+
+class TestResource : public Resource
+{
+public:
+ TestResource(ResourceAttributes attributes = ResourceAttributes()) : Resource(attributes) {}
+};
+
+TEST(ResourceTest, create_without_servicename)
+{
+ ResourceAttributes expected_attributes = {
+ {"service", "backend"},
+ {"version", (uint32_t)1},
+ {"cost", 234.23},
+ {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkLanguage), "cpp"},
+ {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkName), "opentelemetry"},
+ {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkVersion), OPENTELEMETRY_SDK_VERSION},
+ {OTEL_GET_RESOURCE_ATTR(AttrServiceName), "unknown_service"}};
+
+ ResourceAttributes attributes = {
+ {"service", "backend"}, {"version", (uint32_t)1}, {"cost", 234.23}};
+ auto resource = Resource::Create(attributes);
+ auto received_attributes = resource.GetAttributes();
+ for (auto &e : received_attributes)
+ {
+ EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end());
+ if (expected_attributes.find(e.first) != expected_attributes.end())
+ {
+ if (e.first == "version")
+ EXPECT_EQ(nostd::get<uint32_t>(expected_attributes.find(e.first)->second),
+ nostd::get<uint32_t>(e.second));
+ else if (e.first == "cost")
+ EXPECT_EQ(nostd::get<double>(expected_attributes.find(e.first)->second),
+ nostd::get<double>(e.second));
+ else
+ EXPECT_EQ(opentelemetry::nostd::get<std::string>(expected_attributes.find(e.first)->second),
+ opentelemetry::nostd::get<std::string>(e.second));
+ }
+ }
+ EXPECT_EQ(received_attributes.size(), expected_attributes.size()); // for missing service.name
+}
+
+TEST(ResourceTest, create_with_servicename)
+{
+ ResourceAttributes expected_attributes = {
+ {"version", (uint32_t)1},
+ {"cost", 234.23},
+ {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkLanguage), "cpp"},
+ {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkName), "opentelemetry"},
+ {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkVersion), OPENTELEMETRY_SDK_VERSION},
+ {OTEL_GET_RESOURCE_ATTR(AttrServiceName), "backend"},
+ };
+ ResourceAttributes attributes = {
+ {"service.name", "backend"}, {"version", (uint32_t)1}, {"cost", 234.23}};
+ auto resource = Resource::Create(attributes);
+ auto received_attributes = resource.GetAttributes();
+ for (auto &e : received_attributes)
+ {
+ EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end());
+ if (expected_attributes.find(e.first) != expected_attributes.end())
+ {
+ if (e.first == "version")
+ EXPECT_EQ(nostd::get<uint32_t>(expected_attributes.find(e.first)->second),
+ nostd::get<uint32_t>(e.second));
+ else if (e.first == "cost")
+ EXPECT_EQ(nostd::get<double>(expected_attributes.find(e.first)->second),
+ nostd::get<double>(e.second));
+ else
+ EXPECT_EQ(nostd::get<std::string>(expected_attributes.find(e.first)->second),
+ nostd::get<std::string>(e.second));
+ }
+ }
+ EXPECT_EQ(received_attributes.size(), expected_attributes.size()); // for missing service.name
+}
+
+TEST(ResourceTest, create_with_emptyatrributes)
+{
+ ResourceAttributes expected_attributes = {
+ {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkLanguage), "cpp"},
+ {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkName), "opentelemetry"},
+ {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkVersion), OPENTELEMETRY_SDK_VERSION},
+ {OTEL_GET_RESOURCE_ATTR(AttrServiceName), "unknown_service"},
+ };
+ ResourceAttributes attributes = {};
+ auto resource = Resource::Create(attributes);
+ auto received_attributes = resource.GetAttributes();
+ for (auto &e : received_attributes)
+ {
+ EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end());
+ if (expected_attributes.find(e.first) != expected_attributes.end())
+ {
+ EXPECT_EQ(opentelemetry::nostd::get<std::string>(expected_attributes.find(e.first)->second),
+ opentelemetry::nostd::get<std::string>(e.second));
+ }
+ }
+ EXPECT_EQ(received_attributes.size(), expected_attributes.size()); // for missing service.name
+}
+
+TEST(ResourceTest, create_with_schemaurl)
+{
+ const std::string schema_url = "https://opentelemetry.io/schemas/1.2.0";
+ ResourceAttributes attributes = {};
+ auto resource = Resource::Create(attributes, schema_url);
+ auto received_schema_url = resource.GetSchemaURL();
+
+ EXPECT_EQ(received_schema_url, schema_url);
+}
+
+TEST(ResourceTest, Merge)
+{
+ TestResource resource1(ResourceAttributes({{"service", "backend"}}));
+ TestResource resource2(ResourceAttributes({{"host", "service-host"}}));
+ std::map<std::string, std::string> expected_attributes = {{"service", "backend"},
+ {"host", "service-host"}};
+
+ auto merged_resource = resource1.Merge(resource2);
+ auto received_attributes = merged_resource.GetAttributes();
+ for (auto &e : received_attributes)
+ {
+ EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end());
+ if (expected_attributes.find(e.first) != expected_attributes.end())
+ {
+ EXPECT_EQ(expected_attributes.find(e.first)->second, nostd::get<std::string>(e.second));
+ }
+ }
+ EXPECT_EQ(received_attributes.size(), expected_attributes.size());
+}
+
+TEST(ResourceTest, MergeEmptyString)
+{
+ TestResource resource1({{"service", "backend"}, {"host", "service-host"}});
+ TestResource resource2({{"service", ""}, {"host", "another-service-host"}});
+ std::map<std::string, std::string> expected_attributes = {{"service", ""},
+ {"host", "another-service-host"}};
+
+ auto merged_resource = resource1.Merge(resource2);
+ auto received_attributes = merged_resource.GetAttributes();
+
+ for (auto &e : received_attributes)
+ {
+ EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end());
+ if (expected_attributes.find(e.first) != expected_attributes.end())
+ {
+ EXPECT_EQ(expected_attributes.find(e.first)->second, nostd::get<std::string>(e.second));
+ }
+ }
+ EXPECT_EQ(received_attributes.size(), expected_attributes.size());
+}
+
+#ifndef NO_GETENV
+TEST(ResourceTest, OtelResourceDetector)
+{
+ std::map<std::string, std::string> expected_attributes = {{"k", "v"}};
+
+ setenv("OTEL_RESOURCE_ATTRIBUTES", "k=v", 1);
+
+ OTELResourceDetector detector;
+ auto resource = detector.Detect();
+ auto received_attributes = resource.GetAttributes();
+ for (auto &e : received_attributes)
+ {
+ EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end());
+ if (expected_attributes.find(e.first) != expected_attributes.end())
+ {
+ EXPECT_EQ(expected_attributes.find(e.first)->second, nostd::get<std::string>(e.second));
+ }
+ }
+ EXPECT_EQ(received_attributes.size(), expected_attributes.size());
+
+ unsetenv("OTEL_RESOURCE_ATTRIBUTES");
+}
+
+TEST(ResourceTest, OtelResourceDetectorEmptyEnv)
+{
+ std::map<std::string, std::string> expected_attributes = {};
+ unsetenv("OTEL_RESOURCE_ATTRIBUTES");
+ OTELResourceDetector detector;
+ auto resource = detector.Detect();
+ auto received_attributes = resource.GetAttributes();
+ for (auto &e : received_attributes)
+ {
+ EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end());
+ if (expected_attributes.find(e.first) != expected_attributes.end())
+ {
+ EXPECT_EQ(expected_attributes.find(e.first)->second, nostd::get<std::string>(e.second));
+ }
+ }
+ EXPECT_EQ(received_attributes.size(), expected_attributes.size());
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/BUILD
new file mode 100644
index 000000000..70e517684
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/BUILD
@@ -0,0 +1,155 @@
+load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark")
+
+cc_test(
+ name = "tracer_provider_test",
+ srcs = [
+ "tracer_provider_test.cc",
+ ],
+ tags = [
+ "test",
+ "trace",
+ ],
+ deps = [
+ "//sdk/src/resource",
+ "//sdk/src/trace",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "span_data_test",
+ srcs = [
+ "span_data_test.cc",
+ ],
+ tags = [
+ "test",
+ "trace",
+ ],
+ deps = [
+ "//sdk/src/trace",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "simple_processor_test",
+ srcs = [
+ "simple_processor_test.cc",
+ ],
+ tags = [
+ "test",
+ "trace",
+ ],
+ deps = [
+ "//exporters/memory:in_memory_span_exporter",
+ "//sdk/src/trace",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "batch_span_processor_test",
+ srcs = [
+ "batch_span_processor_test.cc",
+ ],
+ tags = [
+ "test",
+ "trace",
+ ],
+ deps = [
+ "//sdk/src/trace",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "tracer_test",
+ srcs = [
+ "tracer_test.cc",
+ ],
+ tags = [
+ "test",
+ "trace",
+ ],
+ deps = [
+ "//exporters/memory:in_memory_span_exporter",
+ "//sdk/src/resource",
+ "//sdk/src/trace",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "always_on_sampler_test",
+ srcs = [
+ "always_on_sampler_test.cc",
+ ],
+ tags = [
+ "test",
+ "trace",
+ ],
+ deps = [
+ "//sdk/src/trace",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "always_off_sampler_test",
+ srcs = [
+ "always_off_sampler_test.cc",
+ ],
+ tags = [
+ "test",
+ "trace",
+ ],
+ deps = [
+ "//sdk/src/trace",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "parent_sampler_test",
+ srcs = [
+ "parent_sampler_test.cc",
+ ],
+ tags = [
+ "test",
+ "trace",
+ ],
+ deps = [
+ "//sdk/src/trace",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "trace_id_ratio_sampler_test",
+ srcs = [
+ "trace_id_ratio_sampler_test.cc",
+ ],
+ tags = [
+ "test",
+ "trace",
+ ],
+ deps = [
+ "//sdk/src/common:random",
+ "//sdk/src/trace",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+otel_cc_benchmark(
+ name = "sampler_benchmark",
+ srcs = ["sampler_benchmark.cc"],
+ tags = [
+ "test",
+ "trace",
+ ],
+ deps = [
+ "//exporters/memory:in_memory_span_exporter",
+ "//sdk/src/resource",
+ "//sdk/src/trace",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/CMakeLists.txt
new file mode 100644
index 000000000..b02ff705f
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/CMakeLists.txt
@@ -0,0 +1,30 @@
+foreach(
+ testname
+ tracer_provider_test
+ span_data_test
+ simple_processor_test
+ tracer_test
+ always_off_sampler_test
+ always_on_sampler_test
+ parent_sampler_test
+ trace_id_ratio_sampler_test
+ batch_span_processor_test)
+ add_executable(${testname} "${testname}.cc")
+ target_link_libraries(
+ ${testname}
+ ${GTEST_BOTH_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT}
+ opentelemetry_common
+ opentelemetry_trace
+ opentelemetry_resources
+ opentelemetry_exporter_in_memory)
+ gtest_add_tests(
+ TARGET ${testname}
+ TEST_PREFIX trace.
+ TEST_LIST ${testname})
+endforeach()
+
+add_executable(sampler_benchmark sampler_benchmark.cc)
+target_link_libraries(
+ sampler_benchmark benchmark::benchmark ${CMAKE_THREAD_LIBS_INIT}
+ opentelemetry_trace opentelemetry_resources opentelemetry_exporter_in_memory)
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_off_sampler_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_off_sampler_test.cc
new file mode 100644
index 000000000..1c32bd5a8
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_off_sampler_test.cc
@@ -0,0 +1,42 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <gtest/gtest.h>
+#include "opentelemetry/sdk/trace/samplers/always_off.h"
+#include "opentelemetry/trace/span_context_kv_iterable_view.h"
+
+using opentelemetry::sdk::trace::AlwaysOffSampler;
+using opentelemetry::sdk::trace::Decision;
+using opentelemetry::trace::SpanContext;
+namespace trace_api = opentelemetry::trace;
+
+TEST(AlwaysOffSampler, ShouldSample)
+{
+ AlwaysOffSampler sampler;
+
+ trace_api::TraceId trace_id;
+ trace_api::SpanKind span_kind = trace_api::SpanKind::kInternal;
+
+ using M = std::map<std::string, int>;
+ M m1 = {{}};
+
+ using L = std::vector<std::pair<SpanContext, std::map<std::string, std::string>>>;
+ L l1 = {{SpanContext(false, false), {}}, {SpanContext(false, false), {}}};
+
+ opentelemetry::common::KeyValueIterableView<M> view{m1};
+ trace_api::SpanContextKeyValueIterableView<L> links{l1};
+
+ auto sampling_result =
+ sampler.ShouldSample(SpanContext::GetInvalid(), trace_id, "", span_kind, view, links);
+
+ ASSERT_EQ(Decision::DROP, sampling_result.decision);
+ ASSERT_EQ(nullptr, sampling_result.attributes);
+ ASSERT_EQ("", sampling_result.trace_state->ToHeader());
+}
+
+TEST(AlwaysOffSampler, GetDescription)
+{
+ AlwaysOffSampler sampler;
+
+ ASSERT_EQ("AlwaysOffSampler", sampler.GetDescription());
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_on_sampler_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_on_sampler_test.cc
new file mode 100644
index 000000000..c483f9b8b
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_on_sampler_test.cc
@@ -0,0 +1,60 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/nostd/span.h"
+#include "opentelemetry/sdk/trace/samplers/always_on.h"
+#include "opentelemetry/trace/span_context_kv_iterable_view.h"
+
+#include <gtest/gtest.h>
+#include <map>
+
+using namespace opentelemetry::sdk::trace;
+using namespace opentelemetry::nostd;
+using opentelemetry::trace::SpanContext;
+namespace trace_api = opentelemetry::trace;
+
+TEST(AlwaysOnSampler, ShouldSample)
+{
+ AlwaysOnSampler sampler;
+
+ // A buffer of trace_id with no specific meaning
+ constexpr uint8_t buf[] = {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7};
+
+ trace_api::TraceId trace_id_invalid;
+ trace_api::TraceId trace_id_valid(buf);
+ std::map<std::string, int> key_value_container = {{"key", 0}};
+
+ using L = std::vector<std::pair<trace_api::SpanContext, std::map<std::string, std::string>>>;
+ L l1 = {{trace_api::SpanContext(false, false), {}}, {trace_api::SpanContext(false, false), {}}};
+
+ opentelemetry::trace::SpanContextKeyValueIterableView<L> links{l1};
+
+ // Test with invalid (empty) trace id and empty parent context
+ auto sampling_result = sampler.ShouldSample(
+ SpanContext::GetInvalid(), trace_id_invalid, "invalid trace id test",
+ trace_api::SpanKind::kServer,
+ opentelemetry::common::KeyValueIterableView<std::map<std::string, int>>(key_value_container),
+ links);
+
+ ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision);
+ ASSERT_EQ(nullptr, sampling_result.attributes);
+ ASSERT_EQ("", sampling_result.trace_state->ToHeader());
+
+ // Test with a valid trace id and empty parent context
+ sampling_result = sampler.ShouldSample(
+ SpanContext::GetInvalid(), trace_id_valid, "valid trace id test",
+ trace_api::SpanKind::kServer,
+ opentelemetry::common::KeyValueIterableView<std::map<std::string, int>>(key_value_container),
+ links);
+
+ ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision);
+ ASSERT_EQ(nullptr, sampling_result.attributes);
+ ASSERT_EQ("", sampling_result.trace_state->ToHeader());
+}
+
+TEST(AlwaysOnSampler, GetDescription)
+{
+ AlwaysOnSampler sampler;
+
+ ASSERT_EQ("AlwaysOnSampler", sampler.GetDescription());
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/batch_span_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/batch_span_processor_test.cc
new file mode 100644
index 000000000..0e6f9c35a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/batch_span_processor_test.cc
@@ -0,0 +1,291 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/trace/batch_span_processor.h"
+#include "opentelemetry/sdk/trace/span_data.h"
+#include "opentelemetry/sdk/trace/tracer.h"
+
+#include <gtest/gtest.h>
+#include <chrono>
+#include <thread>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+
+/**
+ * Returns a mock span exporter meant exclusively for testing only
+ */
+class MockSpanExporter final : public sdk::trace::SpanExporter
+{
+public:
+ MockSpanExporter(
+ std::shared_ptr<std::vector<std::unique_ptr<sdk::trace::SpanData>>> spans_received,
+ std::shared_ptr<std::atomic<bool>> is_shutdown,
+ std::shared_ptr<std::atomic<bool>> is_export_completed =
+ std::shared_ptr<std::atomic<bool>>(new std::atomic<bool>(false)),
+ const std::chrono::milliseconds export_delay = std::chrono::milliseconds(0)) noexcept
+ : spans_received_(spans_received),
+ is_shutdown_(is_shutdown),
+ is_export_completed_(is_export_completed),
+ export_delay_(export_delay)
+ {}
+
+ std::unique_ptr<sdk::trace::Recordable> MakeRecordable() noexcept override
+ {
+ return std::unique_ptr<sdk::trace::Recordable>(new sdk::trace::SpanData);
+ }
+
+ sdk::common::ExportResult Export(
+ const nostd::span<std::unique_ptr<sdk::trace::Recordable>> &recordables) noexcept override
+ {
+ *is_export_completed_ = false;
+
+ std::this_thread::sleep_for(export_delay_);
+
+ for (auto &recordable : recordables)
+ {
+ auto span = std::unique_ptr<sdk::trace::SpanData>(
+ static_cast<sdk::trace::SpanData *>(recordable.release()));
+
+ if (span != nullptr)
+ {
+ spans_received_->push_back(std::move(span));
+ }
+ }
+
+ *is_export_completed_ = true;
+ return sdk::common::ExportResult::kSuccess;
+ }
+
+ bool Shutdown(
+ std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override
+ {
+ *is_shutdown_ = true;
+ return true;
+ }
+
+ bool IsExportCompleted() { return is_export_completed_->load(); }
+
+private:
+ std::shared_ptr<std::vector<std::unique_ptr<sdk::trace::SpanData>>> spans_received_;
+ std::shared_ptr<std::atomic<bool>> is_shutdown_;
+ std::shared_ptr<std::atomic<bool>> is_export_completed_;
+ // Meant exclusively to test force flush timeout
+ const std::chrono::milliseconds export_delay_;
+};
+
+/**
+ * Fixture Class
+ */
+class BatchSpanProcessorTestPeer : public testing::Test
+{
+public:
+ std::unique_ptr<std::vector<std::unique_ptr<sdk::trace::Recordable>>> GetTestSpans(
+ std::shared_ptr<sdk::trace::SpanProcessor> processor,
+ const int num_spans)
+ {
+ std::unique_ptr<std::vector<std::unique_ptr<sdk::trace::Recordable>>> test_spans(
+ new std::vector<std::unique_ptr<sdk::trace::Recordable>>);
+
+ for (int i = 0; i < num_spans; ++i)
+ {
+ test_spans->push_back(processor->MakeRecordable());
+ static_cast<sdk::trace::SpanData *>(test_spans->at(i).get())
+ ->SetName("Span " + std::to_string(i));
+ }
+
+ return test_spans;
+ }
+};
+
+/* ################################## TESTS ############################################ */
+
+TEST_F(BatchSpanProcessorTestPeer, TestShutdown)
+{
+ std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false));
+ std::shared_ptr<std::vector<std::unique_ptr<sdk::trace::SpanData>>> spans_received(
+ new std::vector<std::unique_ptr<sdk::trace::SpanData>>);
+
+ auto batch_processor =
+ std::shared_ptr<sdk::trace::BatchSpanProcessor>(new sdk::trace::BatchSpanProcessor(
+ std::unique_ptr<MockSpanExporter>(new MockSpanExporter(spans_received, is_shutdown)),
+ sdk::trace::BatchSpanProcessorOptions()));
+ const int num_spans = 3;
+
+ auto test_spans = GetTestSpans(batch_processor, num_spans);
+
+ for (int i = 0; i < num_spans; ++i)
+ {
+ batch_processor->OnEnd(std::move(test_spans->at(i)));
+ }
+
+ EXPECT_TRUE(batch_processor->Shutdown());
+ // It's safe to shutdown again
+ EXPECT_TRUE(batch_processor->Shutdown());
+
+ EXPECT_EQ(num_spans, spans_received->size());
+ for (int i = 0; i < num_spans; ++i)
+ {
+ EXPECT_EQ("Span " + std::to_string(i), spans_received->at(i)->GetName());
+ }
+
+ EXPECT_TRUE(is_shutdown->load());
+}
+
+TEST_F(BatchSpanProcessorTestPeer, TestForceFlush)
+{
+ std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false));
+ std::shared_ptr<std::vector<std::unique_ptr<sdk::trace::SpanData>>> spans_received(
+ new std::vector<std::unique_ptr<sdk::trace::SpanData>>);
+
+ auto batch_processor =
+ std::shared_ptr<sdk::trace::BatchSpanProcessor>(new sdk::trace::BatchSpanProcessor(
+ std::unique_ptr<MockSpanExporter>(new MockSpanExporter(spans_received, is_shutdown)),
+ sdk::trace::BatchSpanProcessorOptions()));
+ const int num_spans = 2048;
+
+ auto test_spans = GetTestSpans(batch_processor, num_spans);
+
+ for (int i = 0; i < num_spans; ++i)
+ {
+ batch_processor->OnEnd(std::move(test_spans->at(i)));
+ }
+
+ // Give some time to export
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+
+ EXPECT_TRUE(batch_processor->ForceFlush());
+
+ EXPECT_EQ(num_spans, spans_received->size());
+ for (int i = 0; i < num_spans; ++i)
+ {
+ EXPECT_EQ("Span " + std::to_string(i), spans_received->at(i)->GetName());
+ }
+
+ // Create some more spans to make sure that the processor still works
+ auto more_test_spans = GetTestSpans(batch_processor, num_spans);
+ for (int i = 0; i < num_spans; ++i)
+ {
+ batch_processor->OnEnd(std::move(more_test_spans->at(i)));
+ }
+
+ // Give some time to export the spans
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+
+ EXPECT_TRUE(batch_processor->ForceFlush());
+
+ EXPECT_EQ(num_spans * 2, spans_received->size());
+ for (int i = 0; i < num_spans; ++i)
+ {
+ EXPECT_EQ("Span " + std::to_string(i % num_spans),
+ spans_received->at(num_spans + i)->GetName());
+ }
+}
+
+TEST_F(BatchSpanProcessorTestPeer, TestManySpansLoss)
+{
+ /* Test that when exporting more than max_queue_size spans, some are most likely lost*/
+
+ std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false));
+ std::shared_ptr<std::vector<std::unique_ptr<sdk::trace::SpanData>>> spans_received(
+ new std::vector<std::unique_ptr<sdk::trace::SpanData>>);
+
+ const int max_queue_size = 4096;
+
+ auto batch_processor =
+ std::shared_ptr<sdk::trace::BatchSpanProcessor>(new sdk::trace::BatchSpanProcessor(
+ std::unique_ptr<MockSpanExporter>(new MockSpanExporter(spans_received, is_shutdown)),
+ sdk::trace::BatchSpanProcessorOptions()));
+
+ auto test_spans = GetTestSpans(batch_processor, max_queue_size);
+
+ for (int i = 0; i < max_queue_size; ++i)
+ {
+ batch_processor->OnEnd(std::move(test_spans->at(i)));
+ }
+
+ // Give some time to export the spans
+ std::this_thread::sleep_for(std::chrono::milliseconds(700));
+
+ EXPECT_TRUE(batch_processor->ForceFlush());
+
+ // Span should be exported by now
+ EXPECT_GE(max_queue_size, spans_received->size());
+}
+
+TEST_F(BatchSpanProcessorTestPeer, TestManySpansLossLess)
+{
+ /* Test that no spans are lost when sending max_queue_size spans */
+
+ std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false));
+ std::shared_ptr<std::vector<std::unique_ptr<sdk::trace::SpanData>>> spans_received(
+ new std::vector<std::unique_ptr<sdk::trace::SpanData>>);
+
+ const int num_spans = 2048;
+
+ auto batch_processor =
+ std::shared_ptr<sdk::trace::BatchSpanProcessor>(new sdk::trace::BatchSpanProcessor(
+ std::unique_ptr<MockSpanExporter>(new MockSpanExporter(spans_received, is_shutdown)),
+ sdk::trace::BatchSpanProcessorOptions()));
+
+ auto test_spans = GetTestSpans(batch_processor, num_spans);
+
+ for (int i = 0; i < num_spans; ++i)
+ {
+ batch_processor->OnEnd(std::move(test_spans->at(i)));
+ }
+
+ // Give some time to export the spans
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+
+ EXPECT_TRUE(batch_processor->ForceFlush());
+
+ EXPECT_EQ(num_spans, spans_received->size());
+ for (int i = 0; i < num_spans; ++i)
+ {
+ EXPECT_EQ("Span " + std::to_string(i), spans_received->at(i)->GetName());
+ }
+}
+
+TEST_F(BatchSpanProcessorTestPeer, TestScheduleDelayMillis)
+{
+ /* Test that max_export_batch_size spans are exported every schedule_delay_millis
+ seconds */
+
+ std::shared_ptr<std::atomic<bool>> is_shutdown(new std::atomic<bool>(false));
+ std::shared_ptr<std::atomic<bool>> is_export_completed(new std::atomic<bool>(false));
+ std::shared_ptr<std::vector<std::unique_ptr<sdk::trace::SpanData>>> spans_received(
+ new std::vector<std::unique_ptr<sdk::trace::SpanData>>);
+ const std::chrono::milliseconds export_delay(0);
+ const size_t max_export_batch_size = 512;
+ sdk::trace::BatchSpanProcessorOptions options{};
+ options.schedule_delay_millis = std::chrono::milliseconds(2000);
+
+ auto batch_processor =
+ std::shared_ptr<sdk::trace::BatchSpanProcessor>(new sdk::trace::BatchSpanProcessor(
+ std::unique_ptr<MockSpanExporter>(
+ new MockSpanExporter(spans_received, is_shutdown, is_export_completed, export_delay)),
+ options));
+
+ auto test_spans = GetTestSpans(batch_processor, max_export_batch_size);
+
+ for (size_t i = 0; i < max_export_batch_size; ++i)
+ {
+ batch_processor->OnEnd(std::move(test_spans->at(i)));
+ }
+
+ // Sleep for schedule_delay_millis milliseconds
+ std::this_thread::sleep_for(options.schedule_delay_millis);
+
+ // small delay to give time to export
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+
+ // Spans should be exported by now
+ EXPECT_TRUE(is_export_completed->load());
+ EXPECT_EQ(max_export_batch_size, spans_received->size());
+ for (size_t i = 0; i < max_export_batch_size; ++i)
+ {
+ EXPECT_EQ("Span " + std::to_string(i), spans_received->at(i)->GetName());
+ }
+}
+
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/parent_sampler_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/parent_sampler_test.cc
new file mode 100644
index 000000000..124287598
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/parent_sampler_test.cc
@@ -0,0 +1,73 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <gtest/gtest.h>
+#include <memory>
+#include "opentelemetry/sdk/trace/samplers/always_off.h"
+#include "opentelemetry/sdk/trace/samplers/always_on.h"
+#include "opentelemetry/sdk/trace/samplers/parent.h"
+#include "opentelemetry/trace/span_context_kv_iterable_view.h"
+
+using opentelemetry::sdk::trace::AlwaysOffSampler;
+using opentelemetry::sdk::trace::AlwaysOnSampler;
+using opentelemetry::sdk::trace::Decision;
+using opentelemetry::sdk::trace::ParentBasedSampler;
+namespace trace_api = opentelemetry::trace;
+
+TEST(ParentBasedSampler, ShouldSample)
+{
+ ParentBasedSampler sampler_off(std::make_shared<AlwaysOffSampler>());
+ ParentBasedSampler sampler_on(std::make_shared<AlwaysOnSampler>());
+
+ // Set up parameters
+ uint8_t trace_id_buffer[trace_api::TraceId::kSize] = {1};
+ trace_api::TraceId trace_id{trace_id_buffer};
+ uint8_t span_id_buffer[trace_api::SpanId::kSize] = {1};
+ trace_api::SpanId span_id{span_id_buffer};
+
+ trace_api::SpanKind span_kind = trace_api::SpanKind::kInternal;
+ using M = std::map<std::string, int>;
+ M m1 = {{}};
+
+ using L = std::vector<std::pair<trace_api::SpanContext, std::map<std::string, std::string>>>;
+ L l1 = {{trace_api::SpanContext(false, false), {}}, {trace_api::SpanContext(false, false), {}}};
+
+ opentelemetry::common::KeyValueIterableView<M> view{m1};
+ trace_api::SpanContextKeyValueIterableView<L> links{l1};
+ auto trace_state = trace_api::TraceState::FromHeader("congo=t61rcWkgMzE");
+ trace_api::SpanContext parent_context_sampled(trace_id, span_id, trace_api::TraceFlags{1}, false,
+ trace_state);
+ trace_api::SpanContext parent_context_nonsampled(trace_id, span_id, trace_api::TraceFlags{0},
+ false, trace_state);
+
+ // Case 1: Parent doesn't exist. Return result of delegateSampler()
+ auto sampling_result = sampler_off.ShouldSample(trace_api::SpanContext::GetInvalid(), trace_id,
+ "", span_kind, view, links);
+ auto sampling_result2 = sampler_on.ShouldSample(trace_api::SpanContext::GetInvalid(), trace_id,
+ "", span_kind, view, links);
+
+ ASSERT_EQ(Decision::DROP, sampling_result.decision);
+ ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result2.decision);
+ ASSERT_EQ("", sampling_result.trace_state->ToHeader());
+ ASSERT_EQ("", sampling_result2.trace_state->ToHeader());
+
+ // Case 2: Parent exists and SampledFlag is true
+ auto sampling_result3 =
+ sampler_off.ShouldSample(parent_context_sampled, trace_id, "", span_kind, view, links);
+ ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result3.decision);
+ ASSERT_EQ("congo=t61rcWkgMzE", sampling_result3.trace_state->ToHeader());
+
+ // Case 3: Parent exists and SampledFlag is false
+ auto sampling_result4 =
+ sampler_on.ShouldSample(parent_context_nonsampled, trace_id, "", span_kind, view, links);
+ ASSERT_EQ(Decision::DROP, sampling_result4.decision);
+ ASSERT_EQ("congo=t61rcWkgMzE", sampling_result4.trace_state->ToHeader());
+}
+
+TEST(ParentBasedSampler, GetDescription)
+{
+ ParentBasedSampler sampler(std::make_shared<AlwaysOffSampler>());
+ ASSERT_EQ("ParentBased{AlwaysOffSampler}", sampler.GetDescription());
+ ParentBasedSampler sampler2(std::make_shared<AlwaysOnSampler>());
+ ASSERT_EQ("ParentBased{AlwaysOnSampler}", sampler2.GetDescription());
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/sampler_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/sampler_benchmark.cc
new file mode 100644
index 000000000..b09cb5b0c
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/sampler_benchmark.cc
@@ -0,0 +1,156 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/memory/in_memory_span_exporter.h"
+#include "opentelemetry/sdk/resource/resource.h"
+#include "opentelemetry/sdk/trace/sampler.h"
+#include "opentelemetry/sdk/trace/samplers/always_off.h"
+#include "opentelemetry/sdk/trace/samplers/always_on.h"
+#include "opentelemetry/sdk/trace/samplers/parent.h"
+#include "opentelemetry/sdk/trace/samplers/trace_id_ratio.h"
+#include "opentelemetry/sdk/trace/simple_processor.h"
+#include "opentelemetry/sdk/trace/span_data.h"
+#include "opentelemetry/sdk/trace/tracer.h"
+
+#include <cstdint>
+
+#include <benchmark/benchmark.h>
+
+using namespace opentelemetry::sdk::trace;
+using opentelemetry::exporter::memory::InMemorySpanExporter;
+using opentelemetry::trace::SpanContext;
+
+namespace
+{
+// Sampler constructor used as a baseline to compare with other samplers
+void BM_AlwaysOffSamplerConstruction(benchmark::State &state)
+{
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(AlwaysOffSampler());
+ }
+}
+BENCHMARK(BM_AlwaysOffSamplerConstruction);
+
+// Sampler constructor used as a baseline to compare with other samplers
+void BM_AlwaysOnSamplerConstruction(benchmark::State &state)
+{
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(AlwaysOnSampler());
+ }
+}
+BENCHMARK(BM_AlwaysOnSamplerConstruction);
+
+void BM_ParentBasedSamplerConstruction(benchmark::State &state)
+{
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(ParentBasedSampler(std::make_shared<AlwaysOnSampler>()));
+ }
+}
+BENCHMARK(BM_ParentBasedSamplerConstruction);
+
+void BM_TraceIdRatioBasedSamplerConstruction(benchmark::State &state)
+{
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(TraceIdRatioBasedSampler(0.01));
+ }
+}
+BENCHMARK(BM_TraceIdRatioBasedSamplerConstruction);
+
+// Sampler Helper Function
+void BenchmarkShouldSampler(Sampler &sampler, benchmark::State &state)
+{
+ opentelemetry::trace::TraceId trace_id;
+ opentelemetry::trace::SpanKind span_kind = opentelemetry::trace::SpanKind::kInternal;
+
+ using M = std::map<std::string, int>;
+ M m1 = {{}};
+
+ using L = std::vector<std::pair<trace_api::SpanContext, std::map<std::string, std::string>>>;
+ L l1 = {{trace_api::SpanContext(false, false), {}}, {trace_api::SpanContext(false, false), {}}};
+
+ opentelemetry::common::KeyValueIterableView<M> view{m1};
+ trace_api::SpanContextKeyValueIterableView<L> links{l1};
+
+ while (state.KeepRunning())
+ {
+ auto invalid_ctx = SpanContext::GetInvalid();
+ benchmark::DoNotOptimize(
+ sampler.ShouldSample(invalid_ctx, trace_id, "", span_kind, view, links));
+ }
+}
+
+// Sampler used as a baseline to compare with other samplers
+void BM_AlwaysOffSamplerShouldSample(benchmark::State &state)
+{
+ AlwaysOffSampler sampler;
+
+ BenchmarkShouldSampler(sampler, state);
+}
+BENCHMARK(BM_AlwaysOffSamplerShouldSample);
+
+// Sampler used as a baseline to compare with other samplers
+void BM_AlwaysOnSamplerShouldSample(benchmark::State &state)
+{
+ AlwaysOnSampler sampler;
+
+ BenchmarkShouldSampler(sampler, state);
+}
+BENCHMARK(BM_AlwaysOnSamplerShouldSample);
+
+void BM_ParentBasedSamplerShouldSample(benchmark::State &state)
+{
+ ParentBasedSampler sampler(std::make_shared<AlwaysOnSampler>());
+
+ BenchmarkShouldSampler(sampler, state);
+}
+BENCHMARK(BM_ParentBasedSamplerShouldSample);
+
+void BM_TraceIdRatioBasedSamplerShouldSample(benchmark::State &state)
+{
+ TraceIdRatioBasedSampler sampler(0.01);
+
+ BenchmarkShouldSampler(sampler, state);
+}
+BENCHMARK(BM_TraceIdRatioBasedSamplerShouldSample);
+
+// Sampler Helper Function
+void BenchmarkSpanCreation(std::shared_ptr<Sampler> sampler, benchmark::State &state)
+{
+ std::unique_ptr<SpanExporter> exporter(new InMemorySpanExporter());
+ std::unique_ptr<SpanProcessor> processor(new SimpleSpanProcessor(std::move(exporter)));
+ std::vector<std::unique_ptr<SpanProcessor>> processors;
+ processors.push_back(std::move(processor));
+ auto context = std::make_shared<TracerContext>(std::move(processors));
+ auto resource = opentelemetry::sdk::resource::Resource::Create({});
+ auto tracer = std::shared_ptr<opentelemetry::trace::Tracer>(new Tracer(context));
+
+ while (state.KeepRunning())
+ {
+ auto span = tracer->StartSpan("span");
+
+ span->SetAttribute("attr1", 3.1);
+
+ span->End();
+ }
+}
+
+// Test to measure performance for span creation
+void BM_SpanCreation(benchmark::State &state)
+{
+ BenchmarkSpanCreation(std::move(std::make_shared<AlwaysOnSampler>()), state);
+}
+BENCHMARK(BM_SpanCreation);
+
+// Test to measure performance overhead for no-op span creation
+void BM_NoopSpanCreation(benchmark::State &state)
+{
+ BenchmarkSpanCreation(std::move(std::make_shared<AlwaysOffSampler>()), state);
+}
+BENCHMARK(BM_NoopSpanCreation);
+
+} // namespace
+BENCHMARK_MAIN();
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/simple_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/simple_processor_test.cc
new file mode 100644
index 000000000..9398b922a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/simple_processor_test.cc
@@ -0,0 +1,75 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/trace/simple_processor.h"
+#include "opentelemetry/exporters/memory/in_memory_span_exporter.h"
+#include "opentelemetry/nostd/span.h"
+#include "opentelemetry/sdk/trace/exporter.h"
+#include "opentelemetry/sdk/trace/span_data.h"
+
+#include <gtest/gtest.h>
+
+using namespace opentelemetry::sdk::trace;
+using namespace opentelemetry::sdk::common;
+using opentelemetry::exporter::memory::InMemorySpanData;
+using opentelemetry::exporter::memory::InMemorySpanExporter;
+using opentelemetry::trace::SpanContext;
+
+TEST(SimpleProcessor, ToInMemorySpanExporter)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ SimpleSpanProcessor processor(std::move(exporter));
+
+ auto recordable = processor.MakeRecordable();
+
+ processor.OnStart(*recordable, SpanContext::GetInvalid());
+
+ ASSERT_EQ(0, span_data->GetSpans().size());
+
+ processor.OnEnd(std::move(recordable));
+
+ ASSERT_EQ(1, span_data->GetSpans().size());
+
+ EXPECT_TRUE(processor.Shutdown());
+}
+
+// An exporter that does nothing but record (and give back ) the # of times Shutdown was called.
+class RecordShutdownExporter final : public SpanExporter
+{
+public:
+ RecordShutdownExporter(int *shutdown_counter) : shutdown_counter_(shutdown_counter) {}
+
+ std::unique_ptr<Recordable> MakeRecordable() noexcept override
+ {
+ return std::unique_ptr<Recordable>(new SpanData());
+ }
+
+ ExportResult Export(
+ const opentelemetry::nostd::span<std::unique_ptr<Recordable>> &recordables) noexcept override
+ {
+ return ExportResult::kSuccess;
+ }
+
+ bool Shutdown(
+ std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override
+ {
+ *shutdown_counter_ += 1;
+ return true;
+ }
+
+private:
+ int *shutdown_counter_;
+};
+
+TEST(SimpleSpanProcessor, ShutdownCalledOnce)
+{
+ int shutdowns = 0;
+ std::unique_ptr<RecordShutdownExporter> exporter(new RecordShutdownExporter(&shutdowns));
+ SimpleSpanProcessor processor(std::move(exporter));
+ EXPECT_EQ(0, shutdowns);
+ processor.Shutdown();
+ EXPECT_EQ(1, shutdowns);
+ processor.Shutdown();
+ EXPECT_EQ(1, shutdowns);
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/span_data_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/span_data_test.cc
new file mode 100644
index 000000000..7a6c66c91
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/span_data_test.cc
@@ -0,0 +1,135 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/trace/span_data.h"
+#include "opentelemetry/nostd/variant.h"
+#include "opentelemetry/trace/span.h"
+#include "opentelemetry/trace/span_id.h"
+#include "opentelemetry/trace/trace_id.h"
+
+#include <gtest/gtest.h>
+
+using opentelemetry::sdk::trace::SpanData;
+namespace trace_api = opentelemetry::trace;
+namespace common = opentelemetry::common;
+namespace nostd = opentelemetry::nostd;
+
+TEST(SpanData, DefaultValues)
+{
+ trace_api::SpanContext empty_span_context{false, false};
+ trace_api::SpanId zero_span_id;
+ SpanData data;
+
+ ASSERT_EQ(data.GetTraceId(), empty_span_context.trace_id());
+ ASSERT_EQ(data.GetSpanId(), empty_span_context.span_id());
+ ASSERT_EQ(data.GetSpanContext(), empty_span_context);
+ ASSERT_EQ(data.GetParentSpanId(), zero_span_id);
+ ASSERT_EQ(data.GetName(), "");
+ ASSERT_EQ(data.GetStatus(), trace_api::StatusCode::kUnset);
+ ASSERT_EQ(data.GetDescription(), "");
+ ASSERT_EQ(data.GetStartTime().time_since_epoch(), std::chrono::nanoseconds(0));
+ ASSERT_EQ(data.GetDuration(), std::chrono::nanoseconds(0));
+ ASSERT_EQ(data.GetAttributes().size(), 0);
+ ASSERT_EQ(data.GetEvents().size(), 0);
+}
+
+TEST(SpanData, Set)
+{
+ constexpr uint8_t trace_id_buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8};
+ constexpr uint8_t span_id_buf[] = {1, 2, 3, 4, 5, 6, 7, 8};
+ constexpr uint8_t parent_span_id_buf[] = {8, 7, 6, 5, 4, 3, 2, 1};
+ trace_api::TraceId trace_id{trace_id_buf};
+ trace_api::SpanId span_id{span_id_buf};
+ trace_api::SpanId parent_span_id{parent_span_id_buf};
+ const auto trace_state = trace_api::TraceState::GetDefault()->Set("key1", "value");
+ const trace_api::SpanContext span_context{
+ trace_id, span_id, trace_api::TraceFlags{trace_api::TraceFlags::kIsSampled}, true,
+ trace_state};
+ common::SystemTimestamp now(std::chrono::system_clock::now());
+
+ SpanData data;
+ data.SetIdentity(span_context, parent_span_id);
+ data.SetName("span name");
+ data.SetSpanKind(trace_api::SpanKind::kServer);
+ data.SetStatus(trace_api::StatusCode::kOk, "description");
+ data.SetStartTime(now);
+ data.SetDuration(std::chrono::nanoseconds(1000000));
+ data.SetAttribute("attr1", (int64_t)314159);
+ data.AddEvent("event1", now);
+
+ ASSERT_EQ(data.GetTraceId(), trace_id);
+ ASSERT_EQ(data.GetSpanId(), span_id);
+ ASSERT_EQ(data.GetSpanContext(), span_context);
+ std::string trace_state_key1_value;
+ ASSERT_EQ(data.GetSpanContext().trace_state()->Get("key1", trace_state_key1_value), true);
+ ASSERT_EQ(trace_state_key1_value, "value");
+ ASSERT_EQ(data.GetParentSpanId(), parent_span_id);
+ ASSERT_EQ(data.GetName(), "span name");
+ ASSERT_EQ(data.GetSpanKind(), trace_api::SpanKind::kServer);
+ ASSERT_EQ(data.GetStatus(), trace_api::StatusCode::kOk);
+ ASSERT_EQ(data.GetDescription(), "description");
+ ASSERT_EQ(data.GetStartTime().time_since_epoch(), now.time_since_epoch());
+ ASSERT_EQ(data.GetDuration(), std::chrono::nanoseconds(1000000));
+ ASSERT_EQ(nostd::get<int64_t>(data.GetAttributes().at("attr1")), 314159);
+ ASSERT_EQ(data.GetEvents().at(0).GetName(), "event1");
+ ASSERT_EQ(data.GetEvents().at(0).GetTimestamp(), now);
+}
+
+TEST(SpanData, EventAttributes)
+{
+ SpanData data;
+ const int kNumAttributes = 3;
+ std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3"};
+ int64_t values[kNumAttributes] = {3, 5, 20};
+ std::map<std::string, int64_t> attributes = {
+ {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}};
+
+ data.AddEvent("Test Event", std::chrono::system_clock::now(),
+ common::KeyValueIterableView<std::map<std::string, int64_t>>(attributes));
+
+ for (int i = 0; i < kNumAttributes; i++)
+ {
+ EXPECT_EQ(nostd::get<int64_t>(data.GetEvents().at(0).GetAttributes().at(keys[i])), values[i]);
+ }
+}
+
+TEST(SpanData, Resources)
+{
+ SpanData data;
+ auto resource = opentelemetry::sdk::resource::Resource::Create({});
+ auto input_attr = resource.GetAttributes();
+ data.SetResource(resource);
+ auto output_attr = data.GetResource().GetAttributes();
+ EXPECT_EQ(input_attr, output_attr);
+}
+
+TEST(SpanData, Links)
+{
+ SpanData data;
+ const int kNumAttributes = 3;
+ std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3"};
+ int64_t values[kNumAttributes] = {4, 12, 33};
+ std::map<std::string, int64_t> attributes = {
+ {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}};
+
+ // produce valid SpanContext with pseudo span and trace Id.
+ uint8_t span_id_buf[trace_api::SpanId::kSize] = {
+ 1,
+ };
+ trace_api::SpanId span_id{span_id_buf};
+ uint8_t trace_id_buf[trace_api::TraceId::kSize] = {
+ 2,
+ };
+ trace_api::TraceId trace_id{trace_id_buf};
+ const auto span_context = trace_api::SpanContext(
+ trace_id, span_id, trace_api::TraceFlags{trace_api::TraceFlags::kIsSampled}, true);
+
+ data.AddLink(span_context,
+ common::KeyValueIterableView<std::map<std::string, int64_t>>(attributes));
+
+ EXPECT_EQ(data.GetLinks().at(0).GetSpanContext(), span_context);
+ for (int i = 0; i < kNumAttributes; i++)
+ {
+ EXPECT_EQ(nostd::get<int64_t>(data.GetLinks().at(0).GetAttributes().at(keys[i])), values[i]);
+ }
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/trace_id_ratio_sampler_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/trace_id_ratio_sampler_test.cc
new file mode 100644
index 000000000..1b0088f13
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/trace_id_ratio_sampler_test.cc
@@ -0,0 +1,258 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/trace/samplers/trace_id_ratio.h"
+#include "opentelemetry/trace/span_context_kv_iterable_view.h"
+#include "src/common/random.h"
+
+#include <gtest/gtest.h>
+#include <cstdlib>
+#include <ctime>
+
+using opentelemetry::sdk::common::Random;
+using opentelemetry::sdk::trace::Decision;
+using opentelemetry::sdk::trace::TraceIdRatioBasedSampler;
+namespace trace_api = opentelemetry::trace;
+namespace common = opentelemetry::common;
+
+namespace
+{
+/*
+ * Helper function for running TraceIdBased sampler tests.
+ * Given a span context, sampler, and number of iterations this function
+ * will return the number of RECORD_AND_SAMPLE decision based on randomly
+ * generated traces.
+ *
+ * @param context a required valid span context
+ * @param sampler a required valid sampler
+ * @param iterations a required number specifying the number of times to
+ * generate a random trace_id and check if it should sample using the provided
+ * provider and context
+ */
+int RunShouldSampleCountDecision(trace_api::SpanContext &context,
+ TraceIdRatioBasedSampler &sampler,
+ int iterations)
+{
+ int actual_count = 0;
+
+ trace_api::SpanKind span_kind = trace_api::SpanKind::kInternal;
+
+ using M = std::map<std::string, int>;
+ M m1 = {{}};
+
+ using L = std::vector<std::pair<trace_api::SpanContext, std::map<std::string, std::string>>>;
+ L l1 = {{trace_api::SpanContext(false, false), {}}, {trace_api::SpanContext(false, false), {}}};
+
+ common::KeyValueIterableView<M> view{m1};
+ trace_api::SpanContextKeyValueIterableView<L> links{l1};
+
+ for (int i = 0; i < iterations; ++i)
+ {
+ uint8_t buf[16] = {0};
+ Random::GenerateRandomBuffer(buf);
+
+ trace_api::TraceId trace_id(buf);
+
+ auto result = sampler.ShouldSample(context, trace_id, "", span_kind, view, links);
+ if (result.decision == Decision::RECORD_AND_SAMPLE)
+ {
+ ++actual_count;
+ }
+ }
+
+ return actual_count;
+}
+} // namespace
+
+TEST(TraceIdRatioBasedSampler, ShouldSampleWithoutContext)
+{
+ trace_api::TraceId invalid_trace_id;
+
+ trace_api::SpanKind span_kind = trace_api::SpanKind::kInternal;
+
+ using M = std::map<std::string, int>;
+ M m1 = {{}};
+
+ using L = std::vector<std::pair<trace_api::SpanContext, std::map<std::string, std::string>>>;
+ L l1 = {{trace_api::SpanContext(false, false), {}}, {trace_api::SpanContext(false, false), {}}};
+
+ common::KeyValueIterableView<M> view{m1};
+ trace_api::SpanContextKeyValueIterableView<L> links{l1};
+
+ TraceIdRatioBasedSampler s1(0.01);
+
+ auto sampling_result = s1.ShouldSample(trace_api::SpanContext::GetInvalid(), invalid_trace_id, "",
+ span_kind, view, links);
+
+ ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision);
+ ASSERT_EQ(nullptr, sampling_result.attributes);
+
+ constexpr uint8_t buf[] = {0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0};
+ trace_api::TraceId valid_trace_id(buf);
+
+ sampling_result = s1.ShouldSample(trace_api::SpanContext::GetInvalid(), valid_trace_id, "",
+ span_kind, view, links);
+
+ ASSERT_EQ(Decision::DROP, sampling_result.decision);
+ ASSERT_EQ(nullptr, sampling_result.attributes);
+
+ TraceIdRatioBasedSampler s2(0.50000001);
+
+ sampling_result = s2.ShouldSample(trace_api::SpanContext::GetInvalid(), valid_trace_id, "",
+ span_kind, view, links);
+
+ ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision);
+ ASSERT_EQ(nullptr, sampling_result.attributes);
+
+ TraceIdRatioBasedSampler s3(0.49999999);
+
+ sampling_result = s3.ShouldSample(trace_api::SpanContext::GetInvalid(), valid_trace_id, "",
+ span_kind, view, links);
+
+ ASSERT_EQ(Decision::DROP, sampling_result.decision);
+ ASSERT_EQ(nullptr, sampling_result.attributes);
+
+ TraceIdRatioBasedSampler s4(0.50000000);
+
+ sampling_result = s4.ShouldSample(trace_api::SpanContext::GetInvalid(), valid_trace_id, "",
+ span_kind, view, links);
+
+ ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision);
+ ASSERT_EQ(nullptr, sampling_result.attributes);
+}
+
+TEST(TraceIdRatioBasedSampler, ShouldSampleWithContext)
+{
+
+ uint8_t trace_id_buffer[trace_api::TraceId::kSize] = {1};
+ trace_api::TraceId trace_id{trace_id_buffer};
+ uint8_t span_id_buffer[trace_api::SpanId::kSize] = {1};
+ trace_api::SpanId span_id{span_id_buffer};
+
+ trace_api::SpanKind span_kind = trace_api::SpanKind::kInternal;
+ trace_api::SpanContext c1(trace_id, span_id, trace_api::TraceFlags{0}, false);
+ trace_api::SpanContext c2(trace_id, span_id, trace_api::TraceFlags{1}, false);
+ trace_api::SpanContext c3(trace_id, span_id, trace_api::TraceFlags{0}, true);
+ trace_api::SpanContext c4(trace_id, span_id, trace_api::TraceFlags{1}, true);
+
+ using M = std::map<std::string, int>;
+ M m1 = {{}};
+
+ using L = std::vector<std::pair<trace_api::SpanContext, std::map<std::string, std::string>>>;
+ L l1 = {{trace_api::SpanContext(false, false), {}}, {trace_api::SpanContext(false, false), {}}};
+
+ common::KeyValueIterableView<M> view{m1};
+ trace_api::SpanContextKeyValueIterableView<L> links{l1};
+
+ TraceIdRatioBasedSampler s1(0.01);
+
+ auto sampling_result = s1.ShouldSample(c1, trace_id, "", span_kind, view, links);
+
+ ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision);
+ ASSERT_EQ(nullptr, sampling_result.attributes);
+
+ sampling_result = s1.ShouldSample(c2, trace_id, "", span_kind, view, links);
+
+ ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision);
+ ASSERT_EQ(nullptr, sampling_result.attributes);
+
+ sampling_result = s1.ShouldSample(c3, trace_id, "", span_kind, view, links);
+
+ ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision);
+ ASSERT_EQ(nullptr, sampling_result.attributes);
+
+ sampling_result = s1.ShouldSample(c4, trace_id, "", span_kind, view, links);
+
+ ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision);
+ ASSERT_EQ(nullptr, sampling_result.attributes);
+}
+
+TEST(TraceIdRatioBasedSampler, TraceIdRatioBasedSamplerHalf)
+{
+ double ratio = 0.5;
+ int iterations = 100000;
+ int expected_count = static_cast<int>(iterations * ratio);
+ int variance = static_cast<int>(iterations * 0.01);
+
+ trace_api::SpanContext c(true, true);
+ TraceIdRatioBasedSampler s(ratio);
+
+ int actual_count = RunShouldSampleCountDecision(c, s, iterations);
+
+ ASSERT_TRUE(actual_count < (expected_count + variance));
+ ASSERT_TRUE(actual_count > (expected_count - variance));
+}
+
+TEST(TraceIdRatioBasedSampler, TraceIdRatioBasedSamplerOnePercent)
+{
+ double ratio = 0.01;
+ int iterations = 100000;
+ int expected_count = static_cast<int>(iterations * ratio);
+ int variance = static_cast<int>(iterations * 0.01);
+
+ trace_api::SpanContext c(true, true);
+ TraceIdRatioBasedSampler s(ratio);
+
+ int actual_count = RunShouldSampleCountDecision(c, s, iterations);
+
+ ASSERT_TRUE(actual_count < (expected_count + variance));
+ ASSERT_TRUE(actual_count > (expected_count - variance));
+}
+
+TEST(TraceIdRatioBasedSampler, TraceIdRatioBasedSamplerAll)
+{
+ double ratio = 1.0;
+ int iterations = 100000;
+ int expected_count = static_cast<int>(iterations * ratio);
+
+ trace_api::SpanContext c(true, true);
+ TraceIdRatioBasedSampler s(ratio);
+
+ int actual_count = RunShouldSampleCountDecision(c, s, iterations);
+
+ ASSERT_EQ(actual_count, expected_count);
+}
+
+TEST(TraceIdRatioBasedSampler, TraceIdRatioBasedSamplerNone)
+{
+ double ratio = 0.0;
+ int iterations = 100000;
+ int expected_count = static_cast<int>(iterations * ratio);
+
+ trace_api::SpanContext c(true, true);
+ TraceIdRatioBasedSampler s(ratio);
+
+ int actual_count = RunShouldSampleCountDecision(c, s, iterations);
+
+ ASSERT_EQ(actual_count, expected_count);
+}
+
+TEST(TraceIdRatioBasedSampler, GetDescription)
+{
+ TraceIdRatioBasedSampler s1(0.01);
+ ASSERT_EQ("TraceIdRatioBasedSampler{0.010000}", s1.GetDescription());
+
+ TraceIdRatioBasedSampler s2(0.00);
+ ASSERT_EQ("TraceIdRatioBasedSampler{0.000000}", s2.GetDescription());
+
+ TraceIdRatioBasedSampler s3(1.00);
+ ASSERT_EQ("TraceIdRatioBasedSampler{1.000000}", s3.GetDescription());
+
+ TraceIdRatioBasedSampler s4(0.102030405);
+ ASSERT_EQ("TraceIdRatioBasedSampler{0.102030}", s4.GetDescription());
+
+ TraceIdRatioBasedSampler s5(3.00);
+ ASSERT_EQ("TraceIdRatioBasedSampler{1.000000}", s5.GetDescription());
+
+ TraceIdRatioBasedSampler s6(-3.00);
+ ASSERT_EQ("TraceIdRatioBasedSampler{0.000000}", s6.GetDescription());
+
+ TraceIdRatioBasedSampler s7(1.00000000001);
+ ASSERT_EQ("TraceIdRatioBasedSampler{1.000000}", s7.GetDescription());
+
+ TraceIdRatioBasedSampler s8(-1.00000000001);
+ ASSERT_EQ("TraceIdRatioBasedSampler{0.000000}", s8.GetDescription());
+
+ TraceIdRatioBasedSampler s9(0.50);
+ ASSERT_EQ("TraceIdRatioBasedSampler{0.500000}", s9.GetDescription());
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_provider_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_provider_test.cc
new file mode 100644
index 000000000..498f66127
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_provider_test.cc
@@ -0,0 +1,99 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/trace/tracer_provider.h"
+#include "opentelemetry/sdk/resource/resource.h"
+#include "opentelemetry/sdk/trace/samplers/always_off.h"
+#include "opentelemetry/sdk/trace/samplers/always_on.h"
+#include "opentelemetry/sdk/trace/simple_processor.h"
+#include "opentelemetry/sdk/trace/tracer.h"
+
+#include <gtest/gtest.h>
+
+using namespace opentelemetry::sdk::trace;
+using namespace opentelemetry::sdk::resource;
+
+#include <iostream>
+
+TEST(TracerProvider, GetTracer)
+{
+ std::unique_ptr<SpanProcessor> processor(new SimpleSpanProcessor(nullptr));
+ std::vector<std::unique_ptr<SpanProcessor>> processors;
+ processors.push_back(std::move(processor));
+ TracerProvider tp1(std::make_shared<TracerContext>(std::move(processors), Resource::Create({})));
+ auto t1 = tp1.GetTracer("test");
+ auto t2 = tp1.GetTracer("test");
+ auto t3 = tp1.GetTracer("different", "1.0.0");
+ auto t4 = tp1.GetTracer("");
+ auto t5 = tp1.GetTracer(opentelemetry::nostd::string_view{});
+ auto t6 = tp1.GetTracer("different", "1.0.0", "https://opentelemetry.io/schemas/1.2.0");
+ ASSERT_NE(nullptr, t1);
+ ASSERT_NE(nullptr, t2);
+ ASSERT_NE(nullptr, t3);
+ ASSERT_NE(nullptr, t6);
+
+ // Should return the same instance each time.
+ ASSERT_EQ(t1, t2);
+ ASSERT_NE(t1, t3);
+ ASSERT_EQ(t4, t5);
+ ASSERT_NE(t3, t6);
+
+ // Should be an sdk::trace::Tracer with the processor attached.
+#ifdef OPENTELEMETRY_RTTI_ENABLED
+ auto sdkTracer1 = dynamic_cast<Tracer *>(t1.get());
+#else
+ auto sdkTracer1 = static_cast<Tracer *>(t1.get());
+#endif
+ ASSERT_NE(nullptr, sdkTracer1);
+ ASSERT_EQ("AlwaysOnSampler", sdkTracer1->GetSampler().GetDescription());
+ std::unique_ptr<SpanProcessor> processor2(new SimpleSpanProcessor(nullptr));
+ std::vector<std::unique_ptr<SpanProcessor>> processors2;
+ processors2.push_back(std::move(processor2));
+ TracerProvider tp2(
+ std::make_shared<TracerContext>(std::move(processors2), Resource::Create({}),
+ std::unique_ptr<Sampler>(new AlwaysOffSampler()),
+ std::unique_ptr<IdGenerator>(new RandomIdGenerator)));
+#ifdef OPENTELEMETRY_RTTI_ENABLED
+ auto sdkTracer2 = dynamic_cast<Tracer *>(tp2.GetTracer("test").get());
+#else
+ auto sdkTracer2 = static_cast<Tracer *>(tp2.GetTracer("test").get());
+#endif
+ ASSERT_EQ("AlwaysOffSampler", sdkTracer2->GetSampler().GetDescription());
+
+ auto instrumentation_library1 = sdkTracer1->GetInstrumentationLibrary();
+ ASSERT_EQ(instrumentation_library1.GetName(), "test");
+ ASSERT_EQ(instrumentation_library1.GetVersion(), "");
+
+ // Should be an sdk::trace::Tracer with the processor attached.
+#ifdef OPENTELEMETRY_RTTI_ENABLED
+ auto sdkTracer3 = dynamic_cast<Tracer *>(t3.get());
+#else
+ auto sdkTracer3 = static_cast<Tracer *>(t3.get());
+#endif
+ auto instrumentation_library3 = sdkTracer3->GetInstrumentationLibrary();
+ ASSERT_EQ(instrumentation_library3.GetName(), "different");
+ ASSERT_EQ(instrumentation_library3.GetVersion(), "1.0.0");
+}
+
+TEST(TracerProvider, Shutdown)
+{
+ std::unique_ptr<SpanProcessor> processor(new SimpleSpanProcessor(nullptr));
+ std::vector<std::unique_ptr<SpanProcessor>> processors;
+ processors.push_back(std::move(processor));
+
+ TracerProvider tp1(std::make_shared<TracerContext>(std::move(processors)));
+
+ EXPECT_TRUE(tp1.Shutdown());
+
+ // It's safe to shutdown again
+ EXPECT_TRUE(tp1.Shutdown());
+}
+
+TEST(TracerProvider, ForceFlush)
+{
+ std::unique_ptr<SpanProcessor> processor1(new SimpleSpanProcessor(nullptr));
+
+ TracerProvider tp1(std::move(processor1));
+
+ EXPECT_TRUE(tp1.ForceFlush());
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_test.cc
new file mode 100644
index 000000000..15a7566b9
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_test.cc
@@ -0,0 +1,731 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/trace/tracer.h"
+#include "opentelemetry/exporters/memory/in_memory_span_exporter.h"
+#include "opentelemetry/sdk/resource/resource.h"
+#include "opentelemetry/sdk/trace/samplers/always_off.h"
+#include "opentelemetry/sdk/trace/samplers/always_on.h"
+#include "opentelemetry/sdk/trace/samplers/parent.h"
+#include "opentelemetry/sdk/trace/simple_processor.h"
+#include "opentelemetry/sdk/trace/span_data.h"
+#include "opentelemetry/trace/context.h"
+
+#include <gtest/gtest.h>
+
+using namespace opentelemetry::sdk::trace;
+using namespace opentelemetry::sdk::resource;
+using opentelemetry::common::SteadyTimestamp;
+using opentelemetry::common::SystemTimestamp;
+namespace nostd = opentelemetry::nostd;
+namespace common = opentelemetry::common;
+using opentelemetry::common::KeyValueIterableView;
+using opentelemetry::exporter::memory::InMemorySpanData;
+using opentelemetry::exporter::memory::InMemorySpanExporter;
+using opentelemetry::trace::SpanContext;
+
+/**
+ * A mock sampler with ShouldSample returning:
+ * Decision::RECORD_AND_SAMPLE if trace_id is valid
+ * Decision::DROP otherwise.
+ */
+class MockSampler final : public Sampler
+{
+public:
+ SamplingResult ShouldSample(
+ const SpanContext & /*parent_context*/,
+ trace_api::TraceId trace_id,
+ nostd::string_view /*name*/,
+ trace_api::SpanKind /*span_kind*/,
+ const opentelemetry::common::KeyValueIterable & /*attributes*/,
+ const opentelemetry::trace::SpanContextKeyValueIterable & /*links*/) noexcept override
+ {
+ // Sample only if valid trace_id ( This is to test Sampler get's valid trace id)
+ if (trace_id.IsValid())
+ {
+ // Return two pairs of attributes. These attributes should be added to the
+ // span attributes
+ return {Decision::RECORD_AND_SAMPLE,
+ nostd::unique_ptr<const std::map<std::string, opentelemetry::common::AttributeValue>>(
+ new const std::map<std::string, opentelemetry::common::AttributeValue>(
+ {{"sampling_attr1", 123}, {"sampling_attr2", "string"}}))};
+ }
+ else
+ {
+ // we should never reach here
+ assert(false);
+ return {Decision::DROP};
+ }
+ }
+
+ nostd::string_view GetDescription() const noexcept override { return "MockSampler"; }
+};
+
+/**
+ * A Mock Custom Id Generator
+ */
+class MockIdGenerator : public IdGenerator
+{
+ opentelemetry::trace::SpanId GenerateSpanId() noexcept override
+ {
+ return opentelemetry::trace::SpanId(buf_span);
+ }
+
+ opentelemetry::trace::TraceId GenerateTraceId() noexcept override
+ {
+ return opentelemetry::trace::TraceId(buf_trace);
+ }
+ uint8_t buf_span[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+ uint8_t buf_trace[16] = {1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 1};
+};
+
+namespace
+{
+std::shared_ptr<opentelemetry::trace::Tracer> initTracer(std::unique_ptr<SpanExporter> &&exporter)
+{
+ auto processor = std::unique_ptr<SpanProcessor>(new SimpleSpanProcessor(std::move(exporter)));
+ std::vector<std::unique_ptr<SpanProcessor>> processors;
+ processors.push_back(std::move(processor));
+ auto context = std::make_shared<TracerContext>(std::move(processors));
+ return std::shared_ptr<opentelemetry::trace::Tracer>(new Tracer(context));
+}
+
+std::shared_ptr<opentelemetry::trace::Tracer> initTracer(
+ std::unique_ptr<SpanExporter> &&exporter,
+ // For testing, just shove a pointer over, we'll take it over.
+ Sampler *sampler,
+ IdGenerator *id_generator = new RandomIdGenerator)
+{
+ auto processor = std::unique_ptr<SpanProcessor>(new SimpleSpanProcessor(std::move(exporter)));
+ std::vector<std::unique_ptr<SpanProcessor>> processors;
+ processors.push_back(std::move(processor));
+ auto resource = Resource::Create({});
+ auto context = std::make_shared<TracerContext>(std::move(processors), resource,
+ std::unique_ptr<Sampler>(sampler),
+ std::unique_ptr<IdGenerator>(id_generator));
+ return std::shared_ptr<opentelemetry::trace::Tracer>(new Tracer(context));
+}
+
+} // namespace
+
+TEST(Tracer, ToInMemorySpanExporter)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter));
+
+ auto span_first = tracer->StartSpan("span 1");
+ auto scope_first = tracer->WithActiveSpan(span_first);
+ auto span_second = tracer->StartSpan("span 2");
+
+ ASSERT_EQ(0, span_data->GetSpans().size());
+
+ span_second->End();
+
+ auto span2 = span_data->GetSpans();
+ ASSERT_EQ(1, span2.size());
+ ASSERT_EQ("span 2", span2.at(0)->GetName());
+ EXPECT_TRUE(span2.at(0)->GetTraceId().IsValid());
+ EXPECT_TRUE(span2.at(0)->GetSpanId().IsValid());
+ EXPECT_TRUE(span2.at(0)->GetParentSpanId().IsValid());
+
+ span_first->End();
+
+ auto span1 = span_data->GetSpans();
+ ASSERT_EQ(1, span1.size());
+ ASSERT_EQ("span 1", span1.at(0)->GetName());
+ EXPECT_TRUE(span1.at(0)->GetTraceId().IsValid());
+ EXPECT_TRUE(span1.at(0)->GetSpanId().IsValid());
+ EXPECT_FALSE(span1.at(0)->GetParentSpanId().IsValid());
+
+ // Verify trace and parent span id propagation
+ EXPECT_EQ(span1.at(0)->GetTraceId(), span2.at(0)->GetTraceId());
+ EXPECT_EQ(span2.at(0)->GetParentSpanId(), span1.at(0)->GetSpanId());
+}
+
+TEST(Tracer, StartSpanSampleOn)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer_on = initTracer(std::move(exporter));
+
+ tracer_on->StartSpan("span 1")->End();
+
+ auto spans = span_data->GetSpans();
+ ASSERT_EQ(1, spans.size());
+
+ auto &cur_span_data = spans.at(0);
+ ASSERT_LT(std::chrono::nanoseconds(0), cur_span_data->GetStartTime().time_since_epoch());
+ ASSERT_LT(std::chrono::nanoseconds(0), cur_span_data->GetDuration());
+}
+
+TEST(Tracer, StartSpanSampleOff)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer_off = initTracer(std::move(exporter), new AlwaysOffSampler());
+
+ // This span will not be recorded.
+ auto span = tracer_off->StartSpan("span 2");
+
+ // Always generate a valid span-context (span-id)
+ auto context = span->GetContext();
+ EXPECT_TRUE(context.IsValid());
+ EXPECT_FALSE(context.IsSampled());
+
+ span->End();
+ // The span doesn't write any span data because the sampling decision is alway
+ // DROP.
+ ASSERT_EQ(0, span_data->GetSpans().size());
+}
+
+TEST(Tracer, StartSpanCustomIdGenerator)
+{
+ IdGenerator *id_generator = new MockIdGenerator();
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter), new AlwaysOnSampler(), id_generator);
+
+ tracer->StartSpan("span 1")->End();
+ auto spans = span_data->GetSpans();
+ auto &cur_span_data = spans.at(0);
+
+ EXPECT_EQ(cur_span_data->GetTraceId(), id_generator->GenerateTraceId());
+ EXPECT_EQ(cur_span_data->GetSpanId(), id_generator->GenerateSpanId());
+}
+
+TEST(Tracer, StartSpanWithOptionsTime)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter));
+
+ opentelemetry::trace::StartSpanOptions start;
+ start.start_system_time = SystemTimestamp(std::chrono::nanoseconds(300));
+ start.start_steady_time = SteadyTimestamp(std::chrono::nanoseconds(10));
+
+ opentelemetry::trace::EndSpanOptions end;
+ end.end_steady_time = SteadyTimestamp(std::chrono::nanoseconds(40));
+
+ tracer->StartSpan("span 1", start)->End(end);
+
+ auto spans = span_data->GetSpans();
+ ASSERT_EQ(1, spans.size());
+
+ auto &cur_span_data = spans.at(0);
+ ASSERT_EQ(std::chrono::nanoseconds(300), cur_span_data->GetStartTime().time_since_epoch());
+ ASSERT_EQ(std::chrono::nanoseconds(30), cur_span_data->GetDuration());
+}
+
+TEST(Tracer, StartSpanWithAttributes)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter));
+
+ // Start a span with all supported scalar attribute types.
+ tracer
+ ->StartSpan("span 1", {{"attr1", "string"},
+ {"attr2", false},
+ {"attr1", 314159},
+ {"attr3", (unsigned int)314159},
+ {"attr4", (int32_t)-20},
+ {"attr5", (uint32_t)20},
+ {"attr6", (int64_t)-20},
+ {"attr7", (uint64_t)20},
+ {"attr8", 3.1},
+ {"attr9", "string"}})
+ ->End();
+
+ // Start a span with all supported array attribute types.
+ int listInt[] = {1, 2, 3};
+ unsigned int listUInt[] = {1, 2, 3};
+ int32_t listInt32[] = {1, -2, 3};
+ uint32_t listUInt32[] = {1, 2, 3};
+ int64_t listInt64[] = {1, -2, 3};
+ uint64_t listUInt64[] = {1, 2, 3};
+ double listDouble[] = {1.1, 2.1, 3.1};
+ bool listBool[] = {true, false};
+ nostd::string_view listStringView[] = {"a", "b"};
+ std::map<std::string, common::AttributeValue> m;
+ m["attr1"] = nostd::span<int>(listInt);
+ m["attr2"] = nostd::span<unsigned int>(listUInt);
+ m["attr3"] = nostd::span<int32_t>(listInt32);
+ m["attr4"] = nostd::span<uint32_t>(listUInt32);
+ m["attr5"] = nostd::span<int64_t>(listInt64);
+ m["attr6"] = nostd::span<uint64_t>(listUInt64);
+ m["attr7"] = nostd::span<double>(listDouble);
+ m["attr8"] = nostd::span<bool>(listBool);
+ m["attr9"] = nostd::span<nostd::string_view>(listStringView);
+
+ tracer->StartSpan("span 2", m)->End();
+
+ auto spans = span_data->GetSpans();
+ ASSERT_EQ(2, spans.size());
+
+ auto &cur_span_data = spans.at(0);
+ ASSERT_EQ(9, cur_span_data->GetAttributes().size());
+ ASSERT_EQ(314159, nostd::get<int32_t>(cur_span_data->GetAttributes().at("attr1")));
+ ASSERT_EQ(false, nostd::get<bool>(cur_span_data->GetAttributes().at("attr2")));
+ ASSERT_EQ(314159, nostd::get<uint32_t>(cur_span_data->GetAttributes().at("attr3")));
+ ASSERT_EQ(-20, nostd::get<int32_t>(cur_span_data->GetAttributes().at("attr4")));
+ ASSERT_EQ(20, nostd::get<uint32_t>(cur_span_data->GetAttributes().at("attr5")));
+ ASSERT_EQ(-20, nostd::get<int64_t>(cur_span_data->GetAttributes().at("attr6")));
+ ASSERT_EQ(20, nostd::get<uint64_t>(cur_span_data->GetAttributes().at("attr7")));
+ ASSERT_EQ(3.1, nostd::get<double>(cur_span_data->GetAttributes().at("attr8")));
+ ASSERT_EQ("string", nostd::get<std::string>(cur_span_data->GetAttributes().at("attr9")));
+
+ auto &cur_span_data2 = spans.at(1);
+ ASSERT_EQ(9, cur_span_data2->GetAttributes().size());
+ ASSERT_EQ(std::vector<int32_t>({1, 2, 3}),
+ nostd::get<std::vector<int32_t>>(cur_span_data2->GetAttributes().at("attr1")));
+ ASSERT_EQ(std::vector<uint32_t>({1, 2, 3}),
+ nostd::get<std::vector<uint32_t>>(cur_span_data2->GetAttributes().at("attr2")));
+ ASSERT_EQ(std::vector<int32_t>({1, -2, 3}),
+ nostd::get<std::vector<int32_t>>(cur_span_data2->GetAttributes().at("attr3")));
+ ASSERT_EQ(std::vector<uint32_t>({1, 2, 3}),
+ nostd::get<std::vector<uint32_t>>(cur_span_data2->GetAttributes().at("attr4")));
+ ASSERT_EQ(std::vector<int64_t>({1, -2, 3}),
+ nostd::get<std::vector<int64_t>>(cur_span_data2->GetAttributes().at("attr5")));
+ ASSERT_EQ(std::vector<uint64_t>({1, 2, 3}),
+ nostd::get<std::vector<uint64_t>>(cur_span_data2->GetAttributes().at("attr6")));
+ ASSERT_EQ(std::vector<double>({1.1, 2.1, 3.1}),
+ nostd::get<std::vector<double>>(cur_span_data2->GetAttributes().at("attr7")));
+ ASSERT_EQ(std::vector<bool>({true, false}),
+ nostd::get<std::vector<bool>>(cur_span_data2->GetAttributes().at("attr8")));
+ ASSERT_EQ(std::vector<std::string>({"a", "b"}),
+ nostd::get<std::vector<std::string>>(cur_span_data2->GetAttributes().at("attr9")));
+}
+
+TEST(Tracer, StartSpanWithAttributesCopy)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter));
+
+ {
+ std::unique_ptr<std::vector<int64_t>> numbers(new std::vector<int64_t>);
+ numbers->push_back(1);
+ numbers->push_back(2);
+ numbers->push_back(3);
+
+ std::unique_ptr<std::vector<nostd::string_view>> strings(new std::vector<nostd::string_view>);
+ std::string s1("a");
+ std::string s2("b");
+ std::string s3("c");
+ strings->push_back(s1);
+ strings->push_back(s2);
+ strings->push_back(s3);
+ tracer
+ ->StartSpan("span 1",
+ {{"attr1", *numbers}, {"attr2", nostd::span<nostd::string_view>(*strings)}})
+ ->End();
+ }
+
+ auto spans = span_data->GetSpans();
+ ASSERT_EQ(1, spans.size());
+
+ auto &cur_span_data = spans.at(0);
+ ASSERT_EQ(2, cur_span_data->GetAttributes().size());
+
+ auto numbers = nostd::get<std::vector<int64_t>>(cur_span_data->GetAttributes().at("attr1"));
+ ASSERT_EQ(3, numbers.size());
+ ASSERT_EQ(1, numbers[0]);
+ ASSERT_EQ(2, numbers[1]);
+ ASSERT_EQ(3, numbers[2]);
+
+ auto strings = nostd::get<std::vector<std::string>>(cur_span_data->GetAttributes().at("attr2"));
+ ASSERT_EQ(3, strings.size());
+ ASSERT_EQ("a", strings[0]);
+ ASSERT_EQ("b", strings[1]);
+ ASSERT_EQ("c", strings[2]);
+}
+
+TEST(Tracer, GetSampler)
+{
+ auto resource = Resource::Create({});
+ // Create a Tracer with a default AlwaysOnSampler
+ auto tracer_on = initTracer(nullptr);
+
+#ifdef OPENTELEMETRY_RTTI_ENABLED
+ auto &t1 = std::dynamic_pointer_cast<Tracer>(tracer_on)->GetSampler();
+#else
+ auto &t1 = std::static_pointer_cast<Tracer>(tracer_on)->GetSampler();
+#endif
+ ASSERT_EQ("AlwaysOnSampler", t1.GetDescription());
+
+ // Create a Tracer with a AlwaysOffSampler
+ auto tracer_off = initTracer(nullptr, new AlwaysOffSampler());
+
+#ifdef OPENTELEMETRY_RTTI_ENABLED
+ auto &t2 = std::dynamic_pointer_cast<Tracer>(tracer_off)->GetSampler();
+#else
+ auto &t2 = std::static_pointer_cast<Tracer>(tracer_off)->GetSampler();
+#endif
+ ASSERT_EQ("AlwaysOffSampler", t2.GetDescription());
+}
+
+TEST(Tracer, SpanSetAttribute)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter));
+
+ auto span = tracer->StartSpan("span 1");
+
+ span->SetAttribute("abc", 3.1);
+
+ span->End();
+
+ auto spans = span_data->GetSpans();
+ ASSERT_EQ(1, spans.size());
+ auto &cur_span_data = spans.at(0);
+ ASSERT_EQ(3.1, nostd::get<double>(cur_span_data->GetAttributes().at("abc")));
+}
+
+TEST(Tracer, TestAfterEnd)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter));
+ auto span = tracer->StartSpan("span 1");
+ span->SetAttribute("abc", 3.1);
+
+ span->End();
+
+ // test after end
+ span->SetAttribute("testing null recordable", 3.1);
+ span->AddEvent("event 1");
+ span->AddEvent("event 2", std::chrono::system_clock::now());
+ span->AddEvent("event 3", std::chrono::system_clock::now(), {{"attr1", 1}});
+ std::string new_name{"new name"};
+ span->UpdateName(new_name);
+ span->SetAttribute("attr1", 3.1);
+ std::string description{"description"};
+ span->SetStatus(opentelemetry::trace::StatusCode::kError, description);
+ span->End();
+
+ auto spans = span_data->GetSpans();
+ ASSERT_EQ(1, spans.size());
+ auto &cur_span_data = spans.at(0);
+ ASSERT_EQ(3.1, nostd::get<double>(cur_span_data->GetAttributes().at("abc")));
+}
+
+TEST(Tracer, SpanSetEvents)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter));
+
+ auto span = tracer->StartSpan("span 1");
+ span->AddEvent("event 1");
+ span->AddEvent("event 2", std::chrono::system_clock::now());
+ span->AddEvent("event 3", std::chrono::system_clock::now(), {{"attr1", 1}});
+ span->End();
+
+ auto spans = span_data->GetSpans();
+ ASSERT_EQ(1, spans.size());
+
+ auto &span_data_events = spans.at(0)->GetEvents();
+ ASSERT_EQ(3, span_data_events.size());
+ ASSERT_EQ("event 1", span_data_events[0].GetName());
+ ASSERT_EQ("event 2", span_data_events[1].GetName());
+ ASSERT_EQ("event 3", span_data_events[2].GetName());
+ ASSERT_EQ(0, span_data_events[0].GetAttributes().size());
+ ASSERT_EQ(0, span_data_events[1].GetAttributes().size());
+ ASSERT_EQ(1, span_data_events[2].GetAttributes().size());
+}
+
+TEST(Tracer, SpanSetLinks)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter));
+
+ {
+
+ // Single span link passed through Initialization list
+ tracer->StartSpan("efg", {{"attr1", 1}}, {{SpanContext(false, false), {{"attr2", 2}}}})->End();
+ auto spans = span_data->GetSpans();
+ ASSERT_EQ(1, spans.size());
+
+ auto &span_data_links = spans.at(0)->GetLinks();
+ ASSERT_EQ(1, span_data_links.size());
+ auto link = span_data_links.at(0);
+ ASSERT_EQ(nostd::get<int>(link.GetAttributes().at("attr2")), 2);
+ }
+ {
+
+ // Multiple span links passed through Initialization list
+ tracer
+ ->StartSpan("efg", {{"attr1", 1}},
+ {{SpanContext(false, false), {{"attr2", 2}}},
+ {SpanContext(false, false), {{"attr3", 3}}}})
+ ->End();
+ auto spans = span_data->GetSpans();
+ ASSERT_EQ(1, spans.size());
+
+ auto &span_data_links = spans.at(0)->GetLinks();
+ ASSERT_EQ(2, span_data_links.size());
+ auto link1 = span_data_links.at(0);
+ ASSERT_EQ(nostd::get<int>(link1.GetAttributes().at("attr2")), 2);
+ auto link2 = span_data_links.at(1);
+ ASSERT_EQ(nostd::get<int>(link2.GetAttributes().at("attr3")), 3);
+ }
+
+ {
+
+ // Multiple links, each with multiple attributes passed through Initialization list
+ tracer
+ ->StartSpan("efg", {{"attr1", 1}},
+ {{SpanContext(false, false), {{"attr2", 2}, {"attr3", 3}}},
+ {SpanContext(false, false), {{"attr4", 4}}}})
+ ->End();
+ auto spans = span_data->GetSpans();
+ ASSERT_EQ(1, spans.size());
+
+ auto &span_data_links = spans.at(0)->GetLinks();
+ ASSERT_EQ(2, span_data_links.size());
+ auto link1 = span_data_links.at(0);
+ ASSERT_EQ(nostd::get<int>(link1.GetAttributes().at("attr2")), 2);
+ ASSERT_EQ(nostd::get<int>(link1.GetAttributes().at("attr3")), 3);
+ auto link2 = span_data_links.at(1);
+ ASSERT_EQ(nostd::get<int>(link2.GetAttributes().at("attr4")), 4);
+ }
+
+ {
+ std::map<std::string, std::string> attrs1 = {{"attr1", "1"}, {"attr2", "2"}};
+ std::map<std::string, std::string> attrs2 = {{"attr3", "3"}, {"attr4", "4"}};
+
+ std::vector<std::pair<SpanContext, std::map<std::string, std::string>>> links = {
+ {SpanContext(false, false), attrs1}, {SpanContext(false, false), attrs2}};
+ tracer->StartSpan("efg", attrs1, links)->End();
+ auto spans = span_data->GetSpans();
+
+ auto &span_data_links = spans.at(0)->GetLinks();
+ ASSERT_EQ(2, span_data_links.size());
+ auto link1 = span_data_links.at(0);
+ ASSERT_EQ(nostd::get<std::string>(link1.GetAttributes().at("attr1")), "1");
+ ASSERT_EQ(nostd::get<std::string>(link1.GetAttributes().at("attr2")), "2");
+ auto link2 = span_data_links.at(1);
+ ASSERT_EQ(nostd::get<std::string>(link2.GetAttributes().at("attr3")), "3");
+ ASSERT_EQ(nostd::get<std::string>(link2.GetAttributes().at("attr4")), "4");
+ }
+}
+
+TEST(Tracer, TestAlwaysOnSampler)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer_on = initTracer(std::move(exporter));
+
+ // Testing AlwaysOn sampler.
+ // Create two spans for each tracer. Check the exported result.
+ auto span_on_1 = tracer_on->StartSpan("span 1");
+ auto span_on_2 = tracer_on->StartSpan("span 2");
+ span_on_2->End();
+ span_on_1->End();
+
+ auto spans = span_data->GetSpans();
+ ASSERT_EQ(2, spans.size());
+ ASSERT_EQ("span 2", spans.at(0)->GetName()); // span 2 ends first.
+ ASSERT_EQ("span 1", spans.at(1)->GetName());
+}
+
+TEST(Tracer, TestAlwaysOffSampler)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer_off = initTracer(std::move(exporter), new AlwaysOffSampler());
+ auto span_off_1 = tracer_off->StartSpan("span 1");
+ auto span_off_2 = tracer_off->StartSpan("span 2");
+
+ span_off_1->SetAttribute("attr1", 3.1); // Not recorded.
+
+ span_off_2->End();
+ span_off_1->End();
+
+ // The tracer export nothing with an AlwaysOff sampler
+ ASSERT_EQ(0, span_data->GetSpans().size());
+}
+
+TEST(Tracer, TestParentBasedSampler)
+{
+ // Current ShouldSample always pass an empty ParentContext,
+ // so this sampler will work as an AlwaysOnSampler.
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data_parent_on = exporter->GetData();
+ auto tracer_parent_on =
+ initTracer(std::move(exporter), new ParentBasedSampler(std::make_shared<AlwaysOnSampler>()));
+
+ auto span_parent_on_1 = tracer_parent_on->StartSpan("span 1");
+ auto span_parent_on_2 = tracer_parent_on->StartSpan("span 2");
+
+ span_parent_on_1->SetAttribute("attr1", 3.1);
+
+ span_parent_on_2->End();
+ span_parent_on_1->End();
+
+ auto spans = span_data_parent_on->GetSpans();
+ ASSERT_EQ(2, spans.size());
+ ASSERT_EQ("span 2", spans.at(0)->GetName());
+ ASSERT_EQ("span 1", spans.at(1)->GetName());
+
+ // Current ShouldSample always pass an empty ParentContext,
+ // so this sampler will work as an AlwaysOnSampler.
+ std::unique_ptr<InMemorySpanExporter> exporter2(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data_parent_off = exporter2->GetData();
+ auto tracer_parent_off =
+ initTracer(std::move(exporter2),
+ // Add this to avoid different results for old and new version of clang-format
+ new ParentBasedSampler(std::make_shared<AlwaysOffSampler>()));
+
+ auto span_parent_off_1 = tracer_parent_off->StartSpan("span 1");
+ auto span_parent_off_2 = tracer_parent_off->StartSpan("span 2");
+
+ span_parent_off_1->SetAttribute("attr1", 3.1);
+
+ span_parent_off_1->End();
+ span_parent_off_2->End();
+ ASSERT_EQ(0, span_data_parent_off->GetSpans().size());
+}
+
+TEST(Tracer, WithActiveSpan)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter));
+ auto spans = span_data.get()->GetSpans();
+
+ ASSERT_EQ(0, spans.size());
+
+ {
+ auto span_first = tracer->StartSpan("span 1");
+ auto scope_first = tracer->WithActiveSpan(span_first);
+
+ {
+ auto span_second = tracer->StartSpan("span 2");
+ auto scope_second = tracer->WithActiveSpan(span_second);
+
+ spans = span_data->GetSpans();
+ ASSERT_EQ(0, spans.size());
+
+ span_second->End();
+ }
+
+ spans = span_data->GetSpans();
+ ASSERT_EQ(1, spans.size());
+ EXPECT_EQ("span 2", spans.at(0)->GetName());
+
+ span_first->End();
+ }
+
+ spans = span_data->GetSpans();
+ ASSERT_EQ(1, spans.size());
+ EXPECT_EQ("span 1", spans.at(0)->GetName());
+}
+
+TEST(Tracer, ExpectParent)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter));
+ auto spans = span_data.get()->GetSpans();
+
+ ASSERT_EQ(0, spans.size());
+
+ auto span_first = tracer->StartSpan("span 1");
+
+ trace_api::StartSpanOptions options;
+ options.parent = span_first->GetContext();
+ auto span_second = tracer->StartSpan("span 2", options);
+
+ options.parent = span_second->GetContext();
+ auto span_third = tracer->StartSpan("span 3", options);
+
+ span_third->End();
+ span_second->End();
+ span_first->End();
+
+ spans = span_data->GetSpans();
+ ASSERT_EQ(3, spans.size());
+ auto spandata_first = std::move(spans.at(2));
+ auto spandata_second = std::move(spans.at(1));
+ auto spandata_third = std::move(spans.at(0));
+ EXPECT_EQ("span 1", spandata_first->GetName());
+ EXPECT_EQ("span 2", spandata_second->GetName());
+ EXPECT_EQ("span 3", spandata_third->GetName());
+
+ EXPECT_EQ(spandata_first->GetSpanId(), spandata_second->GetParentSpanId());
+ EXPECT_EQ(spandata_second->GetSpanId(), spandata_third->GetParentSpanId());
+}
+
+TEST(Tracer, ExpectParentAsContext)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter));
+ auto spans = span_data.get()->GetSpans();
+
+ ASSERT_EQ(0, spans.size());
+
+ auto span_first = tracer->StartSpan("span 1");
+
+ opentelemetry::context::Context c1;
+ auto c2 = trace_api::SetSpan(c1, span_first);
+ trace_api::StartSpanOptions options;
+ options.parent = c2;
+ auto span_second = tracer->StartSpan("span 2", options);
+
+ auto c3 = trace_api::SetSpan(c2, span_second);
+ options.parent = c3;
+ auto span_third = tracer->StartSpan("span 3", options);
+
+ span_third->End();
+ span_second->End();
+ span_first->End();
+
+ spans = span_data->GetSpans();
+ ASSERT_EQ(3, spans.size());
+ auto spandata_first = std::move(spans.at(2));
+ auto spandata_second = std::move(spans.at(1));
+ auto spandata_third = std::move(spans.at(0));
+ EXPECT_EQ("span 1", spandata_first->GetName());
+ EXPECT_EQ("span 2", spandata_second->GetName());
+ EXPECT_EQ("span 3", spandata_third->GetName());
+
+ EXPECT_EQ(spandata_first->GetSpanId(), spandata_second->GetParentSpanId());
+ EXPECT_EQ(spandata_second->GetSpanId(), spandata_third->GetParentSpanId());
+}
+
+TEST(Tracer, ValidTraceIdToSampler)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter), new MockSampler());
+
+ auto span = tracer->StartSpan("span 1");
+ // sampler was fed with valid trace_id, so span shouldn't be NoOp Span.
+ EXPECT_TRUE(span->IsRecording());
+ EXPECT_TRUE(span->GetContext().IsValid());
+}
+
+TEST(Tracer, SpanCleanupWithScope)
+{
+ std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
+ std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
+ auto tracer = initTracer(std::move(exporter));
+ {
+ auto span0 = tracer->StartSpan("Span0");
+ auto span1 = tracer->StartSpan("span1");
+ {
+ trace_api::Scope scope(span1);
+ auto span2 = tracer->StartSpan("span2");
+ {
+ trace_api::Scope scope(span2);
+ auto span3 = tracer->StartSpan("span3");
+ }
+ }
+ }
+ EXPECT_EQ(4, span_data->GetSpans().size());
+}