summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics
diff options
context:
space:
mode:
Diffstat (limited to 'src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics')
-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
13 files changed, 2771 insertions, 0 deletions
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