Deterministic render ordering

This commit is contained in:
2025-08-31 22:55:01 -04:00
parent 8b828be0a9
commit 8326c67b9c
3 changed files with 162 additions and 24 deletions

View File

@@ -7,7 +7,9 @@
#include <atomic>
#include <chrono>
#include <cmath>
#include <fstream>
#include <latch>
#include <sstream>
#include <thread>
#include <vector>
@@ -657,3 +659,98 @@ TEST_CASE("memory management") {
CHECK(final_output.size() > 0);
}
}
TEST_CASE("render output deterministic order golden test") {
// Clean slate - reset all metrics before this test
metric::reset_metrics_for_testing();
ArenaAllocator arena;
// Create a comprehensive set of metrics with deliberate ordering
// to test deterministic output
// Create counters with different family names and labels
auto z_counter_family =
metric::create_counter("z_last_counter", "Last counter alphabetically");
auto z_counter =
z_counter_family.create({{"method", "POST"}, {"handler", "api"}});
z_counter.inc(42.0);
auto a_counter_family =
metric::create_counter("a_first_counter", "First counter alphabetically");
auto a_counter1 = a_counter_family.create({{"status", "200"}});
auto a_counter2 = a_counter_family.create(
{{"method", "GET"}}); // Should come before status lexicographically
a_counter1.inc(100.0);
a_counter2.inc(200.0);
// Create gauges with different orderings
auto m_gauge_family = metric::create_gauge("m_middle_gauge", "Middle gauge");
auto m_gauge = m_gauge_family.create({{"type", "memory"}});
m_gauge.set(1024.0);
auto b_gauge_family = metric::create_gauge("b_second_gauge", "Second gauge");
auto b_gauge = b_gauge_family.create({{"region", "us-west"}});
b_gauge.set(256.0);
// Create histograms
auto x_hist_family = metric::create_histogram("x_histogram", "Test histogram",
{0.1, 0.5, 1.0});
auto x_hist = x_hist_family.create({{"endpoint", "/api/v1"}});
x_hist.observe(0.25);
x_hist.observe(0.75);
// Add some callbacks to test callback ordering
a_counter_family.register_callback({{"callback", "test"}},
[]() { return 123.0; });
m_gauge_family.register_callback({{"callback", "dynamic"}},
[]() { return 456.0; });
// Render the metrics
auto output = metric::render(arena);
// Concatenate all output into a single string
std::ostringstream oss;
for (const auto &line : output) {
oss << line;
}
std::string actual_output = oss.str();
// Define expected golden output - this represents the exact expected
// deterministic order
std::string expected_golden =
"# HELP a_first_counter First counter alphabetically\n"
"# TYPE a_first_counter counter\n"
"a_first_counter{callback=\"test\"} 123\n"
"a_first_counter{method=\"GET\"} 200\n"
"a_first_counter{status=\"200\"} 100\n"
"# HELP z_last_counter Last counter alphabetically\n"
"# TYPE z_last_counter counter\n"
"z_last_counter{handler=\"api\",method=\"POST\"} 42\n"
"# HELP b_second_gauge Second gauge\n"
"# TYPE b_second_gauge gauge\n"
"b_second_gauge{region=\"us-west\"} 256\n"
"# HELP m_middle_gauge Middle gauge\n"
"# TYPE m_middle_gauge gauge\n"
"m_middle_gauge{callback=\"dynamic\"} 456\n"
"m_middle_gauge{type=\"memory\"} 1024\n"
"# HELP x_histogram Test histogram\n"
"# TYPE x_histogram histogram\n"
"x_histogram_bucket{endpoint=\"/api/v1\",le=\"0.1\"} 0\n"
"x_histogram_bucket{endpoint=\"/api/v1\",le=\"0.5\"} 1\n"
"x_histogram_bucket{endpoint=\"/api/v1\",le=\"1.0\"} 2\n"
"x_histogram_bucket{endpoint=\"/api/v1\",le=\"+Inf\"} 2\n"
"x_histogram_sum{endpoint=\"/api/v1\"} 1\n"
"x_histogram_count{endpoint=\"/api/v1\"} 2\n";
// Check if output matches golden file
if (actual_output != expected_golden) {
MESSAGE("Render output does not match expected golden output.");
MESSAGE("This indicates the deterministic ordering has changed.");
MESSAGE("Expected output:\n" << expected_golden);
MESSAGE("Actual output:\n" << actual_output);
CHECK(false); // Force test failure
} else {
CHECK(true); // Test passes
}
}