samples_stats_counter_unittest.cc (7714B)
1 /* 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "api/numerics/samples_stats_counter.h" 12 13 #include <cmath> 14 #include <random> 15 #include <vector> 16 17 #include "absl/algorithm/container.h" 18 #include "api/units/timestamp.h" 19 #include "test/gtest.h" 20 21 namespace webrtc { 22 namespace { 23 24 constexpr Timestamp kTimestamp = Timestamp::Seconds(1); 25 26 SamplesStatsCounter CreateStatsFilledWithIntsFrom1ToN(int n) { 27 std::vector<double> data; 28 for (int i = 1; i <= n; i++) { 29 data.push_back(i); 30 } 31 absl::c_shuffle(data, std::mt19937(std::random_device()())); 32 33 SamplesStatsCounter stats; 34 for (double v : data) { 35 stats.AddSample({.value = v, .time = kTimestamp}); 36 } 37 return stats; 38 } 39 40 // Add n samples drawn from uniform distribution in [a;b]. 41 SamplesStatsCounter CreateStatsFromUniformDistribution(int n, 42 double a, 43 double b) { 44 std::mt19937 gen{std::random_device()()}; 45 std::uniform_real_distribution<> dis(a, b); 46 47 SamplesStatsCounter stats; 48 for (int i = 1; i <= n; i++) { 49 stats.AddSample({.value = dis(gen), .time = kTimestamp}); 50 } 51 return stats; 52 } 53 54 class SamplesStatsCounterTest : public ::testing::TestWithParam<int> {}; 55 56 constexpr int SIZE_FOR_MERGE = 10; 57 58 } // namespace 59 60 TEST(SamplesStatsCounterTest, FullSimpleTest) { 61 SamplesStatsCounter stats = CreateStatsFilledWithIntsFrom1ToN(100); 62 63 EXPECT_TRUE(!stats.IsEmpty()); 64 EXPECT_DOUBLE_EQ(stats.GetMin(), 1.0); 65 EXPECT_DOUBLE_EQ(stats.GetMax(), 100.0); 66 EXPECT_DOUBLE_EQ(stats.GetSum(), 5050.0); 67 EXPECT_NEAR(stats.GetAverage(), 50.5, 1e-6); 68 for (int i = 1; i <= 100; i++) { 69 double p = i / 100.0; 70 EXPECT_GE(stats.GetPercentile(p), i); 71 EXPECT_LT(stats.GetPercentile(p), i + 1); 72 } 73 } 74 75 TEST(SamplesStatsCounterTest, VarianceAndDeviation) { 76 SamplesStatsCounter stats; 77 stats.AddSample({.value = 2, .time = kTimestamp}); 78 stats.AddSample({.value = 2, .time = kTimestamp}); 79 stats.AddSample({.value = -1, .time = kTimestamp}); 80 stats.AddSample({.value = 5, .time = kTimestamp}); 81 82 EXPECT_DOUBLE_EQ(stats.GetAverage(), 2.0); 83 EXPECT_DOUBLE_EQ(stats.GetVariance(), 4.5); 84 EXPECT_DOUBLE_EQ(stats.GetStandardDeviation(), sqrt(4.5)); 85 } 86 87 TEST(SamplesStatsCounterTest, FractionPercentile) { 88 SamplesStatsCounter stats = CreateStatsFilledWithIntsFrom1ToN(5); 89 90 EXPECT_DOUBLE_EQ(stats.GetPercentile(0.5), 3); 91 } 92 93 TEST(SamplesStatsCounterTest, TestBorderValues) { 94 SamplesStatsCounter stats = CreateStatsFilledWithIntsFrom1ToN(5); 95 96 EXPECT_GE(stats.GetPercentile(0.01), 1); 97 EXPECT_LT(stats.GetPercentile(0.01), 2); 98 EXPECT_DOUBLE_EQ(stats.GetPercentile(1.0), 5); 99 } 100 101 TEST(SamplesStatsCounterTest, VarianceFromUniformDistribution) { 102 // Check variance converge to 1/12 for [0;1) uniform distribution. 103 // Acts as a sanity check for NumericStabilityForVariance test. 104 SamplesStatsCounter stats = CreateStatsFromUniformDistribution(1e6, 0, 1); 105 106 EXPECT_NEAR(stats.GetVariance(), 1. / 12, 1e-3); 107 } 108 109 TEST(SamplesStatsCounterTest, NumericStabilityForVariance) { 110 // Same test as VarianceFromUniformDistribution, 111 // except the range is shifted to [1e9;1e9+1). 112 // Variance should also converge to 1/12. 113 // NB: Although we lose precision for the samples themselves, the fractional 114 // part still enjoys 22 bits of mantissa and errors should even out, 115 // so that couldn't explain a mismatch. 116 SamplesStatsCounter stats = 117 CreateStatsFromUniformDistribution(1e6, 1e9, 1e9 + 1); 118 119 EXPECT_NEAR(stats.GetVariance(), 1. / 12, 1e-3); 120 } 121 122 TEST_P(SamplesStatsCounterTest, AddSamples) { 123 double data[SIZE_FOR_MERGE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 124 // Split the data in different partitions. 125 // We have 11 distinct tests: 126 // * Empty merged with full sequence. 127 // * 1 sample merged with 9 last. 128 // * 2 samples merged with 8 last. 129 // [...] 130 // * Full merged with empty sequence. 131 // All must lead to the same result. 132 SamplesStatsCounter stats0, stats1; 133 for (int i = 0; i < GetParam(); ++i) { 134 stats0.AddSample({.value = data[i], .time = kTimestamp}); 135 } 136 for (int i = GetParam(); i < SIZE_FOR_MERGE; ++i) { 137 stats1.AddSample({.value = data[i], .time = kTimestamp}); 138 } 139 stats0.AddSamples(stats1); 140 141 EXPECT_EQ(stats0.GetMin(), 0); 142 EXPECT_EQ(stats0.GetMax(), 9); 143 EXPECT_DOUBLE_EQ(stats0.GetAverage(), 4.5); 144 EXPECT_DOUBLE_EQ(stats0.GetVariance(), 8.25); 145 EXPECT_DOUBLE_EQ(stats0.GetStandardDeviation(), sqrt(8.25)); 146 EXPECT_DOUBLE_EQ(stats0.GetPercentile(0.1), 0.9); 147 EXPECT_DOUBLE_EQ(stats0.GetPercentile(0.5), 4.5); 148 EXPECT_DOUBLE_EQ(stats0.GetPercentile(0.9), 8.1); 149 } 150 151 TEST(SamplesStatsCounterTest, MultiplyRight) { 152 SamplesStatsCounter stats = CreateStatsFilledWithIntsFrom1ToN(10); 153 154 EXPECT_TRUE(!stats.IsEmpty()); 155 EXPECT_DOUBLE_EQ(stats.GetMin(), 1.0); 156 EXPECT_DOUBLE_EQ(stats.GetMax(), 10.0); 157 EXPECT_DOUBLE_EQ(stats.GetAverage(), 5.5); 158 159 SamplesStatsCounter multiplied_stats = stats * 10; 160 EXPECT_TRUE(!multiplied_stats.IsEmpty()); 161 EXPECT_DOUBLE_EQ(multiplied_stats.GetMin(), 10.0); 162 EXPECT_DOUBLE_EQ(multiplied_stats.GetMax(), 100.0); 163 EXPECT_DOUBLE_EQ(multiplied_stats.GetAverage(), 55.0); 164 EXPECT_EQ(multiplied_stats.GetSamples().size(), stats.GetSamples().size()); 165 166 // Check that origin stats were not modified. 167 EXPECT_TRUE(!stats.IsEmpty()); 168 EXPECT_DOUBLE_EQ(stats.GetMin(), 1.0); 169 EXPECT_DOUBLE_EQ(stats.GetMax(), 10.0); 170 EXPECT_DOUBLE_EQ(stats.GetAverage(), 5.5); 171 } 172 173 TEST(SamplesStatsCounterTest, MultiplyLeft) { 174 SamplesStatsCounter stats = CreateStatsFilledWithIntsFrom1ToN(10); 175 176 EXPECT_TRUE(!stats.IsEmpty()); 177 EXPECT_DOUBLE_EQ(stats.GetMin(), 1.0); 178 EXPECT_DOUBLE_EQ(stats.GetMax(), 10.0); 179 EXPECT_DOUBLE_EQ(stats.GetAverage(), 5.5); 180 181 SamplesStatsCounter multiplied_stats = 10 * stats; 182 EXPECT_TRUE(!multiplied_stats.IsEmpty()); 183 EXPECT_DOUBLE_EQ(multiplied_stats.GetMin(), 10.0); 184 EXPECT_DOUBLE_EQ(multiplied_stats.GetMax(), 100.0); 185 EXPECT_DOUBLE_EQ(multiplied_stats.GetAverage(), 55.0); 186 EXPECT_EQ(multiplied_stats.GetSamples().size(), stats.GetSamples().size()); 187 188 // Check that origin stats were not modified. 189 EXPECT_TRUE(!stats.IsEmpty()); 190 EXPECT_DOUBLE_EQ(stats.GetMin(), 1.0); 191 EXPECT_DOUBLE_EQ(stats.GetMax(), 10.0); 192 EXPECT_DOUBLE_EQ(stats.GetAverage(), 5.5); 193 } 194 195 TEST(SamplesStatsCounterTest, Divide) { 196 SamplesStatsCounter stats; 197 for (int i = 1; i <= 10; i++) { 198 stats.AddSample({.value = i * 10., .time = kTimestamp}); 199 } 200 201 EXPECT_TRUE(!stats.IsEmpty()); 202 EXPECT_DOUBLE_EQ(stats.GetMin(), 10.0); 203 EXPECT_DOUBLE_EQ(stats.GetMax(), 100.0); 204 EXPECT_DOUBLE_EQ(stats.GetAverage(), 55.0); 205 206 SamplesStatsCounter divided_stats = stats / 10; 207 EXPECT_TRUE(!divided_stats.IsEmpty()); 208 EXPECT_DOUBLE_EQ(divided_stats.GetMin(), 1.0); 209 EXPECT_DOUBLE_EQ(divided_stats.GetMax(), 10.0); 210 EXPECT_DOUBLE_EQ(divided_stats.GetAverage(), 5.5); 211 EXPECT_EQ(divided_stats.GetSamples().size(), stats.GetSamples().size()); 212 213 // Check that origin stats were not modified. 214 EXPECT_TRUE(!stats.IsEmpty()); 215 EXPECT_DOUBLE_EQ(stats.GetMin(), 10.0); 216 EXPECT_DOUBLE_EQ(stats.GetMax(), 100.0); 217 EXPECT_DOUBLE_EQ(stats.GetAverage(), 55.0); 218 } 219 220 INSTANTIATE_TEST_SUITE_P(SamplesStatsCounterTests, 221 SamplesStatsCounterTest, 222 ::testing::Range(0, SIZE_FOR_MERGE + 1)); 223 224 } // namespace webrtc