summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/video/call_stats2_unittest.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/libwebrtc/video/call_stats2_unittest.cc
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/video/call_stats2_unittest.cc')
-rw-r--r--third_party/libwebrtc/video/call_stats2_unittest.cc312
1 files changed, 312 insertions, 0 deletions
diff --git a/third_party/libwebrtc/video/call_stats2_unittest.cc b/third_party/libwebrtc/video/call_stats2_unittest.cc
new file mode 100644
index 0000000000..76abbcfebd
--- /dev/null
+++ b/third_party/libwebrtc/video/call_stats2_unittest.cc
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2020 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 "video/call_stats2.h"
+
+#include <memory>
+
+#include "api/task_queue/default_task_queue_factory.h"
+#include "api/task_queue/task_queue_base.h"
+#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "rtc_base/thread.h"
+#include "system_wrappers/include/metrics.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/run_loop.h"
+
+using ::testing::AnyNumber;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+namespace webrtc {
+namespace internal {
+
+class MockStatsObserver : public CallStatsObserver {
+ public:
+ MockStatsObserver() {}
+ virtual ~MockStatsObserver() {}
+
+ MOCK_METHOD(void, OnRttUpdate, (int64_t, int64_t), (override));
+};
+
+class CallStats2Test : public ::testing::Test {
+ public:
+ CallStats2Test() { call_stats_.EnsureStarted(); }
+
+ // Queues an rtt update call on the process thread.
+ void AsyncSimulateRttUpdate(int64_t rtt) {
+ RtcpRttStats* rtcp_rtt_stats = call_stats_.AsRtcpRttStats();
+ task_queue_->PostTask(
+ [rtcp_rtt_stats, rtt] { rtcp_rtt_stats->OnRttUpdate(rtt); });
+ }
+
+ protected:
+ void FlushProcessAndWorker() {
+ task_queue_->PostTask([this] { loop_.PostTask([this] { loop_.Quit(); }); });
+ loop_.Run();
+ }
+
+ test::RunLoop loop_;
+ std::unique_ptr<TaskQueueBase, TaskQueueDeleter> task_queue_ =
+ CreateDefaultTaskQueueFactory()->CreateTaskQueue(
+ "CallStats",
+ TaskQueueFactory::Priority::NORMAL);
+
+ // Note: Since rtc::Thread doesn't support injecting a Clock, we're going
+ // to be using a mix of the fake clock (used by CallStats) as well as the
+ // system clock (used by rtc::Thread). This isn't ideal and will result in
+ // the tests taking longer to execute in some cases than they need to.
+ SimulatedClock fake_clock_{12345};
+ CallStats call_stats_{&fake_clock_, loop_.task_queue()};
+};
+
+TEST_F(CallStats2Test, AddAndTriggerCallback) {
+ static constexpr const int64_t kRtt = 25;
+
+ MockStatsObserver stats_observer;
+ EXPECT_CALL(stats_observer, OnRttUpdate(kRtt, kRtt))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs([this] { loop_.Quit(); }));
+
+ call_stats_.RegisterStatsObserver(&stats_observer);
+ EXPECT_EQ(-1, call_stats_.LastProcessedRtt());
+
+ AsyncSimulateRttUpdate(kRtt);
+ loop_.Run();
+
+ EXPECT_EQ(kRtt, call_stats_.LastProcessedRtt());
+
+ call_stats_.DeregisterStatsObserver(&stats_observer);
+}
+
+TEST_F(CallStats2Test, ProcessTime) {
+ static constexpr const int64_t kRtt = 100;
+ static constexpr const int64_t kRtt2 = 80;
+
+ MockStatsObserver stats_observer;
+
+ EXPECT_CALL(stats_observer, OnRttUpdate(kRtt, kRtt))
+ .Times(2)
+ .WillOnce(InvokeWithoutArgs([this] {
+ // Advance clock and verify we get an update.
+ fake_clock_.AdvanceTimeMilliseconds(CallStats::kUpdateInterval.ms());
+ }))
+ .WillRepeatedly(InvokeWithoutArgs([this] {
+ AsyncSimulateRttUpdate(kRtt2);
+ // Advance clock just too little to get an update.
+ fake_clock_.AdvanceTimeMilliseconds(CallStats::kUpdateInterval.ms() -
+ 1);
+ }));
+
+ // In case you're reading this and wondering how this number is arrived at,
+ // please see comments in the ChangeRtt test that go into some detail.
+ static constexpr const int64_t kLastAvg = 94;
+ EXPECT_CALL(stats_observer, OnRttUpdate(kLastAvg, kRtt2))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs([this] { loop_.Quit(); }));
+
+ call_stats_.RegisterStatsObserver(&stats_observer);
+
+ AsyncSimulateRttUpdate(kRtt);
+ loop_.Run();
+
+ call_stats_.DeregisterStatsObserver(&stats_observer);
+}
+
+// Verify all observers get correct estimates and observers can be added and
+// removed.
+TEST_F(CallStats2Test, MultipleObservers) {
+ MockStatsObserver stats_observer_1;
+ call_stats_.RegisterStatsObserver(&stats_observer_1);
+ // Add the second observer twice, there should still be only one report to the
+ // observer.
+ MockStatsObserver stats_observer_2;
+ call_stats_.RegisterStatsObserver(&stats_observer_2);
+ call_stats_.RegisterStatsObserver(&stats_observer_2);
+
+ static constexpr const int64_t kRtt = 100;
+
+ // Verify both observers are updated.
+ EXPECT_CALL(stats_observer_1, OnRttUpdate(kRtt, kRtt))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return());
+ EXPECT_CALL(stats_observer_2, OnRttUpdate(kRtt, kRtt))
+ .Times(AnyNumber())
+ .WillOnce(InvokeWithoutArgs([this] { loop_.Quit(); }))
+ .WillRepeatedly(Return());
+ AsyncSimulateRttUpdate(kRtt);
+ loop_.Run();
+
+ // Deregister the second observer and verify update is only sent to the first
+ // observer.
+ call_stats_.DeregisterStatsObserver(&stats_observer_2);
+
+ EXPECT_CALL(stats_observer_1, OnRttUpdate(kRtt, kRtt))
+ .Times(AnyNumber())
+ .WillOnce(InvokeWithoutArgs([this] { loop_.Quit(); }))
+ .WillRepeatedly(Return());
+ EXPECT_CALL(stats_observer_2, OnRttUpdate(kRtt, kRtt)).Times(0);
+ AsyncSimulateRttUpdate(kRtt);
+ loop_.Run();
+
+ // Deregister the first observer.
+ call_stats_.DeregisterStatsObserver(&stats_observer_1);
+
+ // Now make sure we don't get any callbacks.
+ EXPECT_CALL(stats_observer_1, OnRttUpdate(kRtt, kRtt)).Times(0);
+ EXPECT_CALL(stats_observer_2, OnRttUpdate(kRtt, kRtt)).Times(0);
+ AsyncSimulateRttUpdate(kRtt);
+
+ // Flush the queue on the process thread to make sure we return after
+ // Process() has been called.
+ FlushProcessAndWorker();
+}
+
+// Verify increasing and decreasing rtt triggers callbacks with correct values.
+TEST_F(CallStats2Test, ChangeRtt) {
+ // NOTE: This test assumes things about how old reports are removed
+ // inside of call_stats.cc. The threshold ms value is 1500ms, but it's not
+ // clear here that how the clock is advanced, affects that algorithm and
+ // subsequently the average reported rtt.
+
+ MockStatsObserver stats_observer;
+ call_stats_.RegisterStatsObserver(&stats_observer);
+
+ static constexpr const int64_t kFirstRtt = 100;
+ static constexpr const int64_t kLowRtt = kFirstRtt - 20;
+ static constexpr const int64_t kHighRtt = kFirstRtt + 20;
+
+ EXPECT_CALL(stats_observer, OnRttUpdate(kFirstRtt, kFirstRtt))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs([this] {
+ fake_clock_.AdvanceTimeMilliseconds(1000);
+ AsyncSimulateRttUpdate(kHighRtt); // Reported at T1 (1000ms).
+ }));
+
+ // NOTE: This relies on the internal algorithms of call_stats.cc.
+ // There's a weight factor there (0.3), that weighs the previous average to
+ // the new one by 70%, so the number 103 in this case is arrived at like so:
+ // (100) / 1 * 0.7 + (100+120)/2 * 0.3 = 103
+ static constexpr const int64_t kAvgRtt1 = 103;
+ EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt1, kHighRtt))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs([this] {
+ // This interacts with an internal implementation detail in call_stats
+ // that decays the oldest rtt value. See more below.
+ fake_clock_.AdvanceTimeMilliseconds(1000);
+ AsyncSimulateRttUpdate(kLowRtt); // Reported at T2 (2000ms).
+ }));
+
+ // Increase time enough for a new update, but not too much to make the
+ // rtt invalid. Report a lower rtt and verify the old/high value still is sent
+ // in the callback.
+
+ // Here, enough time must have passed in order to remove exactly the first
+ // report and nothing else (>1500ms has passed since the first rtt).
+ // So, this value is arrived by doing:
+ // (kAvgRtt1)/1 * 0.7 + (kHighRtt+kLowRtt)/2 * 0.3 = 102.1
+ static constexpr const int64_t kAvgRtt2 = 102;
+ EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt2, kHighRtt))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs([this] {
+ // Advance time to make the high report invalid, the lower rtt should
+ // now be in the callback.
+ fake_clock_.AdvanceTimeMilliseconds(1000);
+ }));
+
+ static constexpr const int64_t kAvgRtt3 = 95;
+ EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt3, kLowRtt))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs([this] { loop_.Quit(); }));
+
+ // Trigger the first rtt value and set off the chain of callbacks.
+ AsyncSimulateRttUpdate(kFirstRtt); // Reported at T0 (0ms).
+ loop_.Run();
+
+ call_stats_.DeregisterStatsObserver(&stats_observer);
+}
+
+TEST_F(CallStats2Test, LastProcessedRtt) {
+ MockStatsObserver stats_observer;
+ call_stats_.RegisterStatsObserver(&stats_observer);
+
+ static constexpr const int64_t kRttLow = 10;
+ static constexpr const int64_t kRttHigh = 30;
+ // The following two average numbers dependend on average + weight
+ // calculations in call_stats.cc.
+ static constexpr const int64_t kAvgRtt1 = 13;
+ static constexpr const int64_t kAvgRtt2 = 15;
+
+ EXPECT_CALL(stats_observer, OnRttUpdate(kRttLow, kRttLow))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs([this] {
+ EXPECT_EQ(kRttLow, call_stats_.LastProcessedRtt());
+ // Don't advance the clock to make sure that low and high rtt values
+ // are associated with the same time stamp.
+ AsyncSimulateRttUpdate(kRttHigh);
+ }));
+
+ EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt1, kRttHigh))
+ .Times(AnyNumber())
+ .WillOnce(InvokeWithoutArgs([this] {
+ EXPECT_EQ(kAvgRtt1, call_stats_.LastProcessedRtt());
+ fake_clock_.AdvanceTimeMilliseconds(CallStats::kUpdateInterval.ms());
+ AsyncSimulateRttUpdate(kRttLow);
+ AsyncSimulateRttUpdate(kRttHigh);
+ }))
+ .WillRepeatedly(Return());
+
+ EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt2, kRttHigh))
+ .Times(AnyNumber())
+ .WillOnce(InvokeWithoutArgs([this] {
+ EXPECT_EQ(kAvgRtt2, call_stats_.LastProcessedRtt());
+ loop_.Quit();
+ }))
+ .WillRepeatedly(Return());
+
+ // Set a first values and verify that LastProcessedRtt initially returns the
+ // average rtt.
+ fake_clock_.AdvanceTimeMilliseconds(CallStats::kUpdateInterval.ms());
+ AsyncSimulateRttUpdate(kRttLow);
+ loop_.Run();
+ EXPECT_EQ(kAvgRtt2, call_stats_.LastProcessedRtt());
+
+ call_stats_.DeregisterStatsObserver(&stats_observer);
+}
+
+TEST_F(CallStats2Test, ProducesHistogramMetrics) {
+ metrics::Reset();
+ static constexpr const int64_t kRtt = 123;
+ MockStatsObserver stats_observer;
+ call_stats_.RegisterStatsObserver(&stats_observer);
+ EXPECT_CALL(stats_observer, OnRttUpdate(kRtt, kRtt))
+ .Times(AnyNumber())
+ .WillRepeatedly(InvokeWithoutArgs([this] { loop_.Quit(); }));
+
+ AsyncSimulateRttUpdate(kRtt);
+ loop_.Run();
+ fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds *
+ CallStats::kUpdateInterval.ms());
+ AsyncSimulateRttUpdate(kRtt);
+ loop_.Run();
+
+ call_stats_.DeregisterStatsObserver(&stats_observer);
+
+ call_stats_.UpdateHistogramsForTest();
+
+ EXPECT_METRIC_EQ(1, metrics::NumSamples(
+ "WebRTC.Video.AverageRoundTripTimeInMilliseconds"));
+ EXPECT_METRIC_EQ(
+ 1, metrics::NumEvents("WebRTC.Video.AverageRoundTripTimeInMilliseconds",
+ kRtt));
+}
+
+} // namespace internal
+} // namespace webrtc