diff options
Diffstat (limited to 'src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics')
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 |