blob: 76abbcfebdbfbff981ed42b9f2049870fba1c7e6 [file] [log] [blame]
Tommi822a8742020-05-10 22:42:301/*
2 * Copyright (c) 2020 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 "video/call_stats2.h"
12
13#include <memory>
14
Niels Möller8d6cd552021-12-22 14:44:5415#include "api/task_queue/default_task_queue_factory.h"
Danil Chapovalov03f8b8a2022-07-18 11:11:4216#include "api/task_queue/task_queue_base.h"
Tommi822a8742020-05-10 22:42:3017#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
Tommi822a8742020-05-10 22:42:3018#include "rtc_base/thread.h"
19#include "system_wrappers/include/metrics.h"
20#include "test/gmock.h"
21#include "test/gtest.h"
22#include "test/run_loop.h"
23
24using ::testing::AnyNumber;
25using ::testing::InvokeWithoutArgs;
26using ::testing::Return;
27
28namespace webrtc {
29namespace internal {
30
31class MockStatsObserver : public CallStatsObserver {
32 public:
33 MockStatsObserver() {}
34 virtual ~MockStatsObserver() {}
35
Danil Chapovalov91fdc602020-05-14 17:17:5136 MOCK_METHOD(void, OnRttUpdate, (int64_t, int64_t), (override));
Tommi822a8742020-05-10 22:42:3037};
38
39class CallStats2Test : public ::testing::Test {
40 public:
Niels Möller8d6cd552021-12-22 14:44:5441 CallStats2Test() { call_stats_.EnsureStarted(); }
Tommi822a8742020-05-10 22:42:3042
43 // Queues an rtt update call on the process thread.
44 void AsyncSimulateRttUpdate(int64_t rtt) {
45 RtcpRttStats* rtcp_rtt_stats = call_stats_.AsRtcpRttStats();
Danil Chapovalov03f8b8a2022-07-18 11:11:4246 task_queue_->PostTask(
Danil Chapovalov95eeaa72022-07-06 08:14:2947 [rtcp_rtt_stats, rtt] { rtcp_rtt_stats->OnRttUpdate(rtt); });
Tommi822a8742020-05-10 22:42:3048 }
49
50 protected:
51 void FlushProcessAndWorker() {
Danil Chapovalov03f8b8a2022-07-18 11:11:4252 task_queue_->PostTask([this] { loop_.PostTask([this] { loop_.Quit(); }); });
Tommi822a8742020-05-10 22:42:3053 loop_.Run();
54 }
55
56 test::RunLoop loop_;
Danil Chapovalov03f8b8a2022-07-18 11:11:4257 std::unique_ptr<TaskQueueBase, TaskQueueDeleter> task_queue_ =
58 CreateDefaultTaskQueueFactory()->CreateTaskQueue(
59 "CallStats",
60 TaskQueueFactory::Priority::NORMAL);
Niels Möller8d6cd552021-12-22 14:44:5461
Tommi822a8742020-05-10 22:42:3062 // Note: Since rtc::Thread doesn't support injecting a Clock, we're going
63 // to be using a mix of the fake clock (used by CallStats) as well as the
64 // system clock (used by rtc::Thread). This isn't ideal and will result in
65 // the tests taking longer to execute in some cases than they need to.
66 SimulatedClock fake_clock_{12345};
67 CallStats call_stats_{&fake_clock_, loop_.task_queue()};
68};
69
70TEST_F(CallStats2Test, AddAndTriggerCallback) {
71 static constexpr const int64_t kRtt = 25;
72
73 MockStatsObserver stats_observer;
74 EXPECT_CALL(stats_observer, OnRttUpdate(kRtt, kRtt))
75 .Times(1)
76 .WillOnce(InvokeWithoutArgs([this] { loop_.Quit(); }));
77
78 call_stats_.RegisterStatsObserver(&stats_observer);
79 EXPECT_EQ(-1, call_stats_.LastProcessedRtt());
80
81 AsyncSimulateRttUpdate(kRtt);
82 loop_.Run();
83
84 EXPECT_EQ(kRtt, call_stats_.LastProcessedRtt());
85
86 call_stats_.DeregisterStatsObserver(&stats_observer);
87}
88
89TEST_F(CallStats2Test, ProcessTime) {
90 static constexpr const int64_t kRtt = 100;
91 static constexpr const int64_t kRtt2 = 80;
92
93 MockStatsObserver stats_observer;
94
95 EXPECT_CALL(stats_observer, OnRttUpdate(kRtt, kRtt))
96 .Times(2)
97 .WillOnce(InvokeWithoutArgs([this] {
98 // Advance clock and verify we get an update.
Tommia0a44802020-05-13 16:27:2699 fake_clock_.AdvanceTimeMilliseconds(CallStats::kUpdateInterval.ms());
Tommi822a8742020-05-10 22:42:30100 }))
101 .WillRepeatedly(InvokeWithoutArgs([this] {
102 AsyncSimulateRttUpdate(kRtt2);
103 // Advance clock just too little to get an update.
Tommia0a44802020-05-13 16:27:26104 fake_clock_.AdvanceTimeMilliseconds(CallStats::kUpdateInterval.ms() -
105 1);
Tommi822a8742020-05-10 22:42:30106 }));
107
108 // In case you're reading this and wondering how this number is arrived at,
109 // please see comments in the ChangeRtt test that go into some detail.
110 static constexpr const int64_t kLastAvg = 94;
111 EXPECT_CALL(stats_observer, OnRttUpdate(kLastAvg, kRtt2))
112 .Times(1)
113 .WillOnce(InvokeWithoutArgs([this] { loop_.Quit(); }));
114
115 call_stats_.RegisterStatsObserver(&stats_observer);
116
117 AsyncSimulateRttUpdate(kRtt);
118 loop_.Run();
119
120 call_stats_.DeregisterStatsObserver(&stats_observer);
121}
122
123// Verify all observers get correct estimates and observers can be added and
124// removed.
125TEST_F(CallStats2Test, MultipleObservers) {
126 MockStatsObserver stats_observer_1;
127 call_stats_.RegisterStatsObserver(&stats_observer_1);
128 // Add the second observer twice, there should still be only one report to the
129 // observer.
130 MockStatsObserver stats_observer_2;
131 call_stats_.RegisterStatsObserver(&stats_observer_2);
132 call_stats_.RegisterStatsObserver(&stats_observer_2);
133
134 static constexpr const int64_t kRtt = 100;
135
136 // Verify both observers are updated.
137 EXPECT_CALL(stats_observer_1, OnRttUpdate(kRtt, kRtt))
138 .Times(AnyNumber())
139 .WillRepeatedly(Return());
140 EXPECT_CALL(stats_observer_2, OnRttUpdate(kRtt, kRtt))
141 .Times(AnyNumber())
142 .WillOnce(InvokeWithoutArgs([this] { loop_.Quit(); }))
143 .WillRepeatedly(Return());
144 AsyncSimulateRttUpdate(kRtt);
145 loop_.Run();
146
147 // Deregister the second observer and verify update is only sent to the first
148 // observer.
149 call_stats_.DeregisterStatsObserver(&stats_observer_2);
150
151 EXPECT_CALL(stats_observer_1, OnRttUpdate(kRtt, kRtt))
152 .Times(AnyNumber())
153 .WillOnce(InvokeWithoutArgs([this] { loop_.Quit(); }))
154 .WillRepeatedly(Return());
155 EXPECT_CALL(stats_observer_2, OnRttUpdate(kRtt, kRtt)).Times(0);
156 AsyncSimulateRttUpdate(kRtt);
157 loop_.Run();
158
159 // Deregister the first observer.
160 call_stats_.DeregisterStatsObserver(&stats_observer_1);
161
162 // Now make sure we don't get any callbacks.
163 EXPECT_CALL(stats_observer_1, OnRttUpdate(kRtt, kRtt)).Times(0);
164 EXPECT_CALL(stats_observer_2, OnRttUpdate(kRtt, kRtt)).Times(0);
165 AsyncSimulateRttUpdate(kRtt);
166
167 // Flush the queue on the process thread to make sure we return after
168 // Process() has been called.
169 FlushProcessAndWorker();
170}
171
172// Verify increasing and decreasing rtt triggers callbacks with correct values.
173TEST_F(CallStats2Test, ChangeRtt) {
174 // NOTE: This test assumes things about how old reports are removed
175 // inside of call_stats.cc. The threshold ms value is 1500ms, but it's not
176 // clear here that how the clock is advanced, affects that algorithm and
177 // subsequently the average reported rtt.
178
179 MockStatsObserver stats_observer;
180 call_stats_.RegisterStatsObserver(&stats_observer);
181
182 static constexpr const int64_t kFirstRtt = 100;
183 static constexpr const int64_t kLowRtt = kFirstRtt - 20;
184 static constexpr const int64_t kHighRtt = kFirstRtt + 20;
185
186 EXPECT_CALL(stats_observer, OnRttUpdate(kFirstRtt, kFirstRtt))
187 .Times(1)
188 .WillOnce(InvokeWithoutArgs([this] {
189 fake_clock_.AdvanceTimeMilliseconds(1000);
190 AsyncSimulateRttUpdate(kHighRtt); // Reported at T1 (1000ms).
191 }));
192
193 // NOTE: This relies on the internal algorithms of call_stats.cc.
194 // There's a weight factor there (0.3), that weighs the previous average to
195 // the new one by 70%, so the number 103 in this case is arrived at like so:
196 // (100) / 1 * 0.7 + (100+120)/2 * 0.3 = 103
197 static constexpr const int64_t kAvgRtt1 = 103;
198 EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt1, kHighRtt))
199 .Times(1)
200 .WillOnce(InvokeWithoutArgs([this] {
201 // This interacts with an internal implementation detail in call_stats
202 // that decays the oldest rtt value. See more below.
203 fake_clock_.AdvanceTimeMilliseconds(1000);
204 AsyncSimulateRttUpdate(kLowRtt); // Reported at T2 (2000ms).
205 }));
206
207 // Increase time enough for a new update, but not too much to make the
208 // rtt invalid. Report a lower rtt and verify the old/high value still is sent
209 // in the callback.
210
211 // Here, enough time must have passed in order to remove exactly the first
212 // report and nothing else (>1500ms has passed since the first rtt).
213 // So, this value is arrived by doing:
214 // (kAvgRtt1)/1 * 0.7 + (kHighRtt+kLowRtt)/2 * 0.3 = 102.1
215 static constexpr const int64_t kAvgRtt2 = 102;
216 EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt2, kHighRtt))
217 .Times(1)
218 .WillOnce(InvokeWithoutArgs([this] {
219 // Advance time to make the high report invalid, the lower rtt should
220 // now be in the callback.
221 fake_clock_.AdvanceTimeMilliseconds(1000);
222 }));
223
224 static constexpr const int64_t kAvgRtt3 = 95;
225 EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt3, kLowRtt))
226 .Times(1)
227 .WillOnce(InvokeWithoutArgs([this] { loop_.Quit(); }));
228
229 // Trigger the first rtt value and set off the chain of callbacks.
230 AsyncSimulateRttUpdate(kFirstRtt); // Reported at T0 (0ms).
231 loop_.Run();
232
233 call_stats_.DeregisterStatsObserver(&stats_observer);
234}
235
236TEST_F(CallStats2Test, LastProcessedRtt) {
237 MockStatsObserver stats_observer;
238 call_stats_.RegisterStatsObserver(&stats_observer);
239
240 static constexpr const int64_t kRttLow = 10;
241 static constexpr const int64_t kRttHigh = 30;
242 // The following two average numbers dependend on average + weight
243 // calculations in call_stats.cc.
244 static constexpr const int64_t kAvgRtt1 = 13;
245 static constexpr const int64_t kAvgRtt2 = 15;
246
247 EXPECT_CALL(stats_observer, OnRttUpdate(kRttLow, kRttLow))
248 .Times(1)
249 .WillOnce(InvokeWithoutArgs([this] {
250 EXPECT_EQ(kRttLow, call_stats_.LastProcessedRtt());
251 // Don't advance the clock to make sure that low and high rtt values
252 // are associated with the same time stamp.
253 AsyncSimulateRttUpdate(kRttHigh);
254 }));
255
256 EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt1, kRttHigh))
257 .Times(AnyNumber())
258 .WillOnce(InvokeWithoutArgs([this] {
259 EXPECT_EQ(kAvgRtt1, call_stats_.LastProcessedRtt());
Tommia0a44802020-05-13 16:27:26260 fake_clock_.AdvanceTimeMilliseconds(CallStats::kUpdateInterval.ms());
Tommi822a8742020-05-10 22:42:30261 AsyncSimulateRttUpdate(kRttLow);
262 AsyncSimulateRttUpdate(kRttHigh);
263 }))
264 .WillRepeatedly(Return());
265
266 EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt2, kRttHigh))
267 .Times(AnyNumber())
268 .WillOnce(InvokeWithoutArgs([this] {
269 EXPECT_EQ(kAvgRtt2, call_stats_.LastProcessedRtt());
270 loop_.Quit();
271 }))
272 .WillRepeatedly(Return());
273
274 // Set a first values and verify that LastProcessedRtt initially returns the
275 // average rtt.
Tommia0a44802020-05-13 16:27:26276 fake_clock_.AdvanceTimeMilliseconds(CallStats::kUpdateInterval.ms());
Tommi822a8742020-05-10 22:42:30277 AsyncSimulateRttUpdate(kRttLow);
278 loop_.Run();
279 EXPECT_EQ(kAvgRtt2, call_stats_.LastProcessedRtt());
280
281 call_stats_.DeregisterStatsObserver(&stats_observer);
282}
283
284TEST_F(CallStats2Test, ProducesHistogramMetrics) {
285 metrics::Reset();
286 static constexpr const int64_t kRtt = 123;
287 MockStatsObserver stats_observer;
288 call_stats_.RegisterStatsObserver(&stats_observer);
289 EXPECT_CALL(stats_observer, OnRttUpdate(kRtt, kRtt))
290 .Times(AnyNumber())
291 .WillRepeatedly(InvokeWithoutArgs([this] { loop_.Quit(); }));
292
293 AsyncSimulateRttUpdate(kRtt);
294 loop_.Run();
295 fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds *
Tommia0a44802020-05-13 16:27:26296 CallStats::kUpdateInterval.ms());
Tommi822a8742020-05-10 22:42:30297 AsyncSimulateRttUpdate(kRtt);
298 loop_.Run();
299
300 call_stats_.DeregisterStatsObserver(&stats_observer);
301
302 call_stats_.UpdateHistogramsForTest();
303
304 EXPECT_METRIC_EQ(1, metrics::NumSamples(
305 "WebRTC.Video.AverageRoundTripTimeInMilliseconds"));
306 EXPECT_METRIC_EQ(
307 1, metrics::NumEvents("WebRTC.Video.AverageRoundTripTimeInMilliseconds",
308 kRtt));
309}
310
311} // namespace internal
312} // namespace webrtc