Index: webrtc/video/stats_counter_unittest.cc |
diff --git a/webrtc/video/stats_counter_unittest.cc b/webrtc/video/stats_counter_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..13df2baad528519a9d4661be8a2bc6b11c887982 |
--- /dev/null |
+++ b/webrtc/video/stats_counter_unittest.cc |
@@ -0,0 +1,390 @@ |
+/* |
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. |
+ * |
+ * Use of this source code is governed by a BSD-style license |
+ * that can be found in the LICENSE file in the root of the source |
+ * tree. An additional intellectual property rights grant can be found |
+ * in the file PATENTS. All contributing project authors may |
+ * be found in the AUTHORS file in the root of the source tree. |
+ */ |
+ |
+#include "webrtc/video/stats_counter.h" |
+ |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+#include "webrtc/system_wrappers/include/clock.h" |
+ |
+namespace webrtc { |
+namespace { |
+const int kProcessIntervalMs = 2000; |
+ |
+class AvgEncodeTimeMsObserver : public StatsCounterObserver { |
+ public: |
+ AvgEncodeTimeMsObserver() : num_calls_(0), last_sample_(-1) {} |
+ void OnMetricUpdated(int sample) override { |
+ // RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.AvgEncodeTimeMs", sample); |
+ ++num_calls_; |
+ last_sample_ = sample; |
+ } |
+ int num_calls_; |
+ int last_sample_; |
+}; |
+} // namespace |
+ |
+class StatsCounterTest : public ::testing::Test { |
+ protected: |
+ StatsCounterTest() : clock_(1234) {} |
+ |
+ void AddSampleAndAdvance(int sample, int interval_ms, AvgCounter* counter) { |
+ counter->Add(sample); |
+ clock_.AdvanceTimeMilliseconds(interval_ms); |
+ } |
+ |
+ void SetSampleAndAdvance(int sample, |
+ int interval_ms, |
+ RateAccCounter* counter) { |
+ counter->Set(sample); |
+ clock_.AdvanceTimeMilliseconds(interval_ms); |
+ } |
+ |
+ void VerifyStatsIsNotSet(const StatsCounter::Histogram::Stats& stats) { |
+ EXPECT_EQ(0, stats.num_samples); |
+ EXPECT_EQ(-1, stats.min); |
+ EXPECT_EQ(-1, stats.max); |
+ EXPECT_EQ(-1, stats.average); |
+ EXPECT_EQ(-1, stats.percentile10); |
+ EXPECT_EQ(-1, stats.percentile50); |
+ EXPECT_EQ(-1, stats.percentile90); |
+ } |
+ |
+ SimulatedClock clock_; |
+}; |
+ |
+TEST_F(StatsCounterTest, NoSamples) { |
+ AvgCounter counter(&clock_, 10, 10, nullptr); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ VerifyStatsIsNotSet(stats); |
+} |
+ |
+TEST_F(StatsCounterTest, TestMetric_AvgCounter) { |
+ AvgCounter counter(&clock_, 50, 50, nullptr); |
+ counter.Add(4); |
+ counter.Add(8); |
+ counter.Add(9); |
+ clock_.AdvanceTimeMilliseconds(kProcessIntervalMs); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(111); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [7:1] metric: average per interval |
+ EXPECT_EQ(1, stats.num_samples); |
+ EXPECT_EQ(7, stats.min); |
+ EXPECT_EQ(7, stats.max); |
+ EXPECT_EQ(7, stats.average); |
+} |
+ |
+TEST_F(StatsCounterTest, TestMetric_PercentCounter) { |
+ PercentCounter counter(&clock_, nullptr); |
+ counter.Add(true); |
+ counter.Add(false); |
+ clock_.AdvanceTimeMilliseconds(kProcessIntervalMs); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(false); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [50:1] metric: percentage per interval |
+ EXPECT_EQ(1, stats.num_samples); |
+ EXPECT_EQ(50, stats.min); |
+ EXPECT_EQ(50, stats.max); |
+} |
+ |
+TEST_F(StatsCounterTest, TestMetric_PermilleCounter) { |
+ PermilleCounter counter(&clock_, 1001, nullptr); |
+ counter.Add(true); |
+ counter.Add(false); |
+ clock_.AdvanceTimeMilliseconds(kProcessIntervalMs); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(false); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [500:1] metric: permille per interval |
+ EXPECT_EQ(1, stats.num_samples); |
+ EXPECT_EQ(500, stats.min); |
+ EXPECT_EQ(500, stats.max); |
+} |
+ |
+TEST_F(StatsCounterTest, TestMetric_RateCounter) { |
+ RateCounter counter(&clock_, 1000, 1000, true, nullptr); |
+ counter.Add(186); |
+ counter.Add(350); |
+ counter.Add(22); |
+ clock_.AdvanceTimeMilliseconds(kProcessIntervalMs); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(111); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [279:1] metric: rate per interval, (186+350+22)/2sec = 279 samples/sec |
+ EXPECT_EQ(1, stats.num_samples); |
+ EXPECT_EQ(279, stats.min); |
+ EXPECT_EQ(279, stats.max); |
+} |
+ |
+TEST_F(StatsCounterTest, TestMetric_RateAccCounter) { |
+ RateAccCounter counter(&clock_, 1000, 1000, true, nullptr); |
+ counter.Set(175); |
+ counter.Set(188); |
+ clock_.AdvanceTimeMilliseconds(kProcessIntervalMs); |
+ // Trigger process (sample included in next interval). |
+ counter.Set(192); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [94:1] metric: rate per interval, (188-0)/2sec = 94 samples/sec |
+ EXPECT_EQ(1, stats.num_samples); |
+ EXPECT_EQ(94, stats.min); |
+ EXPECT_EQ(94, stats.max); |
+} |
+ |
+TEST_F(StatsCounterTest, TestRegisterObserver) { |
+ AvgEncodeTimeMsObserver* observer = new AvgEncodeTimeMsObserver(); |
+ const int kSample = 22; |
+ AvgCounter counter(&clock_, 50, 50, observer); |
+ AddSampleAndAdvance(kSample, kProcessIntervalMs, &counter); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(111); |
+ EXPECT_EQ(1, observer->num_calls_); |
+ EXPECT_EQ(kSample, observer->last_sample_); |
+} |
+ |
+TEST_F(StatsCounterTest, VerifyProcessInterval) { |
+ AvgCounter counter(&clock_, 10, 10, nullptr); |
+ counter.Add(4); |
+ clock_.AdvanceTimeMilliseconds(kProcessIntervalMs - 1); |
+ // Try trigger process (interval has not passed). |
+ counter.Add(8); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ VerifyStatsIsNotSet(stats); |
+ // Make process interval pass. |
+ clock_.AdvanceTimeMilliseconds(1); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(111); |
+ stats = counter.GetStats(); |
+ // [6:1] |
+ EXPECT_EQ(1, stats.num_samples); |
+ EXPECT_EQ(6, stats.min); |
+ EXPECT_EQ(6, stats.max); |
+} |
+ |
+TEST_F(StatsCounterTest, TestHistogramUnderflow) { |
+ const int kMin = 0; |
+ const int kMax = 100; |
+ const int kSample = -1; |
+ // 8 buckets: [0-1),[1-2),[2-4),[4-9),[9-20),[20-45),[45-100),[100-inf) |
+ AvgCounter counter(&clock_, 8, kMax, nullptr); |
+ AddSampleAndAdvance(kSample, kProcessIntervalMs, &counter); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(111); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [0:1],[1:0],[2:0],[4:0],[9:0],[20:0],[45:0],[100:0] |
+ EXPECT_EQ(1, stats.num_samples); |
+ EXPECT_EQ(kSample, stats.min); |
+ EXPECT_EQ(kSample, stats.max); |
+ EXPECT_EQ(kSample, stats.average); |
+ EXPECT_EQ(kMin, stats.percentile10); |
+ EXPECT_EQ(kMin, stats.percentile50); |
+ EXPECT_EQ(kMin, stats.percentile90); |
+} |
+ |
+TEST_F(StatsCounterTest, TestHistogramOverflow) { |
+ const int kMax = 100; |
+ const int kSample = kMax + 1; |
+ // 8 buckets: [0-1),[1-2),[2-4),[4-9),[9-20),[20-45),[45-100),[100-inf) |
+ AvgCounter counter(&clock_, 8, kMax, nullptr); |
+ AddSampleAndAdvance(kSample, kProcessIntervalMs, &counter); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(111); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [0:0],[1:0],[2:0],[4:0],[9:0],[20:0],[45:0],[100:1] |
+ EXPECT_EQ(1, stats.num_samples); |
+ EXPECT_EQ(kSample, stats.min); |
+ EXPECT_EQ(kSample, stats.max); |
+ EXPECT_EQ(kSample, stats.average); |
+ EXPECT_EQ(kMax, stats.percentile10); |
+ EXPECT_EQ(kMax, stats.percentile50); |
+ EXPECT_EQ(kMax, stats.percentile90); |
+} |
+ |
+TEST_F(StatsCounterTest, TestStats_LinearBuckets) { |
+ AvgCounter counter(&clock_, 100, 100, nullptr); |
+ const int kSample1 = 1; |
+ const int kSample2 = 4; |
+ const int kSample3 = 8; |
+ const int kSample4 = kSample3; |
+ const int kSample5 = 50; |
+ AddSampleAndAdvance(kSample1, kProcessIntervalMs, &counter); |
+ AddSampleAndAdvance(kSample2, kProcessIntervalMs, &counter); |
+ AddSampleAndAdvance(kSample3, kProcessIntervalMs, &counter); |
+ AddSampleAndAdvance(kSample4, kProcessIntervalMs, &counter); |
+ AddSampleAndAdvance(kSample5, kProcessIntervalMs, &counter); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(111); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [1:1],[4:1],[8:2],[50:1] |
+ EXPECT_EQ(5, stats.num_samples); |
+ EXPECT_EQ(kSample1, stats.min); |
+ EXPECT_EQ(kSample5, stats.max); |
+ EXPECT_EQ(14, stats.average); |
+ EXPECT_EQ(kSample1, stats.percentile10); |
+ EXPECT_EQ(kSample3, stats.percentile50); |
+ EXPECT_EQ(kSample5, stats.percentile90); |
+} |
+ |
+TEST_F(StatsCounterTest, TestStats_ExponentialBuckets) { |
+ AvgCounter counter(&clock_, 10, 50, nullptr); |
+ const int kSample1 = 1; |
+ const int kSample2 = 4; |
+ const int kSample3 = 9; |
+ const int kSample4 = kSample3; |
+ const int kSample5 = 50; |
+ AddSampleAndAdvance(kSample1, kProcessIntervalMs, &counter); |
+ AddSampleAndAdvance(kSample2, kProcessIntervalMs, &counter); |
+ AddSampleAndAdvance(kSample3, kProcessIntervalMs, &counter); |
+ AddSampleAndAdvance(kSample4, kProcessIntervalMs, &counter); |
+ AddSampleAndAdvance(kSample5, kProcessIntervalMs, &counter); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(111); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [0:0],[1:1],[2:0],[3:1],[5:0],[8:2],[13:0],[20:0],[32:0],[50:1] |
+ EXPECT_EQ(5, stats.num_samples); |
+ EXPECT_EQ(kSample1, stats.min); |
+ EXPECT_EQ(kSample5, stats.max); |
+ EXPECT_EQ(15, stats.average); |
+ EXPECT_EQ(kSample1, stats.percentile10); |
+ EXPECT_EQ(kSample3 + 1, stats.percentile50); // Mid bin size used. |
+ EXPECT_EQ(kSample5, stats.percentile90); |
+} |
+ |
+TEST_F(StatsCounterTest, TestGetStatsTwice) { |
+ const int kSample1 = 4; |
+ const int kSample2 = 7; |
+ AvgCounter counter(&clock_, 20, 50, nullptr); |
+ AddSampleAndAdvance(kSample1, kProcessIntervalMs, &counter); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(kSample2); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [0:0],[1:0],[2:0],[3:0],[4:1],[5:0],[6:0],[7:0],[8:0],[9:0],... |
+ EXPECT_EQ(1, stats.num_samples); |
+ EXPECT_EQ(kSample1, stats.min); |
+ EXPECT_EQ(kSample1, stats.max); |
+ // Trigger process (sample included in next interval). |
+ clock_.AdvanceTimeMilliseconds(kProcessIntervalMs); |
+ counter.Add(111); |
+ stats = counter.GetStats(); |
+ // [0:0],[1:0],[2:0],[3:0],[4:1],[5:0],[6:0],[7:1],[8:0],[9:0],... |
+ EXPECT_EQ(2, stats.num_samples); |
+ EXPECT_EQ(kSample1, stats.min); |
+ EXPECT_EQ(kSample2, stats.max); |
+ EXPECT_EQ(6, stats.average); |
+ EXPECT_EQ(kSample1, stats.percentile10); |
+ EXPECT_EQ(kSample1, stats.percentile50); |
+ EXPECT_EQ(kSample2, stats.percentile90); |
+} |
+ |
+TEST_F(StatsCounterTest, TestRateAccCounter) { |
+ const int kSample1 = 200; // 200 / 2 sec |
+ const int kSample2 = 800; // 600 / 2 sec |
+ const int kSample3 = 1800; // 1000 / 2 sec |
+ RateAccCounter counter(&clock_, 1000, 1000, true, nullptr); |
+ SetSampleAndAdvance(kSample1, kProcessIntervalMs, &counter); |
+ SetSampleAndAdvance(kSample2, kProcessIntervalMs, &counter); |
+ SetSampleAndAdvance(kSample3, kProcessIntervalMs, &counter); |
+ // Trigger process (sample included in next interval). |
+ counter.Set(2000); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [100:1],[300:1],[500:1] |
+ EXPECT_EQ(3, stats.num_samples); |
+ EXPECT_EQ(100, stats.min); |
+ EXPECT_EQ(500, stats.max); |
+ EXPECT_EQ(300, stats.average); |
+} |
+ |
+TEST_F(StatsCounterTest, TestRateAccCounter_NegativeRateIgnored) { |
+ const int kSample1 = 200; // 200 / 2 sec |
+ const int kSample2 = 100; // -100 / 2 sec - negative ignored |
+ const int kSample3 = 700; // 600 / 2 sec |
+ RateAccCounter counter(&clock_, 1000, 1000, true, nullptr); |
+ SetSampleAndAdvance(kSample1, kProcessIntervalMs, &counter); |
+ SetSampleAndAdvance(kSample2, kProcessIntervalMs, &counter); |
+ SetSampleAndAdvance(kSample3, kProcessIntervalMs, &counter); |
+ // Trigger process (sample included in next interval). |
+ counter.Set(2000); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [100:1],[300:1] |
+ EXPECT_EQ(2, stats.num_samples); |
+ EXPECT_EQ(100, stats.min); |
+ EXPECT_EQ(300, stats.max); |
+ EXPECT_EQ(200, stats.average); |
+} |
+ |
+TEST_F(StatsCounterTest, IntervalsWithoutSamplesIgnored_AvgCounter) { |
+ AvgCounter counter(&clock_, 10, 10, nullptr); |
+ AddSampleAndAdvance(6, kProcessIntervalMs * 4 - 1, &counter); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(8); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [6:1], two intervals without samples passed. |
+ EXPECT_EQ(1, stats.num_samples); |
+ EXPECT_EQ(6, stats.min); |
+ EXPECT_EQ(6, stats.max); |
+ // Make last interval pass. |
+ clock_.AdvanceTimeMilliseconds(1); |
+ counter.Add(111); // Trigger process (sample included in next interval). |
+ stats = counter.GetStats(); |
+ // [6:1],[8:1] |
+ EXPECT_EQ(2, stats.num_samples); |
+ EXPECT_EQ(6, stats.min); |
+ EXPECT_EQ(8, stats.max); |
+} |
+ |
+TEST_F(StatsCounterTest, IntervalsWithoutSamplesIgnored_RateCounter) { |
+ const int kSample1 = 50; // 50 / 2 sec |
+ const int kSample2 = 20; // 20 / 2 sec |
+ RateCounter counter(&clock_, 1000, 1000, false, nullptr); |
+ counter.Add(kSample1); |
+ clock_.AdvanceTimeMilliseconds(kProcessIntervalMs * 4 - 1); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(kSample2); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [25:1], two intervals without samples passed. |
+ EXPECT_EQ(1, stats.num_samples); |
+ EXPECT_EQ(25, stats.min); |
+ EXPECT_EQ(25, stats.max); |
+ // Make last interval pass. |
+ clock_.AdvanceTimeMilliseconds(1); |
+ counter.Add(111); // Trigger process (sample included in next interval). |
+ stats = counter.GetStats(); |
+ // [10:1],[25:1] |
+ EXPECT_EQ(2, stats.num_samples); |
+ EXPECT_EQ(10, stats.min); |
+ EXPECT_EQ(25, stats.max); |
+} |
+ |
+TEST_F(StatsCounterTest, RateCounter_IntervalsWithoutSamplesIncluded) { |
+ const int kSample1 = 50; // 50 / 2 sec |
+ const int kSample2 = 20; // 20 / 2 sec |
+ RateCounter counter(&clock_, 1000, 1000, true, nullptr); |
+ counter.Add(kSample1); |
+ clock_.AdvanceTimeMilliseconds(kProcessIntervalMs * 3 - 1); |
+ // Trigger process (sample included in next interval). |
+ counter.Add(kSample2); |
+ StatsCounter::Histogram::Stats stats = counter.GetStats(); |
+ // [0:1],[25:1], one interval without samples passed. |
+ EXPECT_EQ(2, stats.num_samples); |
+ EXPECT_EQ(0, stats.min); |
+ EXPECT_EQ(25, stats.max); |
+ // Make last interval pass. |
+ clock_.AdvanceTimeMilliseconds(1); |
+ counter.Add(111); // Trigger process (sample included in next interval). |
+ stats = counter.GetStats(); |
+ // [0:1],[10:1],[25:1] |
+ EXPECT_EQ(3, stats.num_samples); |
+ EXPECT_EQ(0, stats.min); |
+ EXPECT_EQ(25, stats.max); |
+ EXPECT_EQ(10, stats.percentile50); |
+} |
+ |
+} // namespace webrtc |