diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /third_party/libwebrtc/video/call_stats2_unittest.cc | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
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.cc | 312 |
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 |