1 /*
2 * Copyright (c) 2012 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 "rtc_base/rate_statistics.h"
12
13 #include <cstdlib>
14
15 #include "test/gtest.h"
16
17 namespace {
18
19 using webrtc::RateStatistics;
20
21 const int64_t kWindowMs = 500;
22
23 class RateStatisticsTest : public ::testing::Test {
24 protected:
RateStatisticsTest()25 RateStatisticsTest() : stats_(kWindowMs, 8000) {}
26 RateStatistics stats_;
27 };
28
TEST_F(RateStatisticsTest,TestStrictMode)29 TEST_F(RateStatisticsTest, TestStrictMode) {
30 int64_t now_ms = 0;
31 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
32
33 const uint32_t kPacketSize = 1500u;
34 const uint32_t kExpectedRateBps = kPacketSize * 1000 * 8;
35
36 // Single data point is not enough for valid estimate.
37 stats_.Update(kPacketSize, now_ms++);
38 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
39
40 // Expecting 1200 kbps since the window is initially kept small and grows as
41 // we have more data.
42 stats_.Update(kPacketSize, now_ms);
43 EXPECT_EQ(kExpectedRateBps, *stats_.Rate(now_ms));
44
45 stats_.Reset();
46 // Expecting 0 after init.
47 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
48
49 const int kInterval = 10;
50 for (int i = 0; i < 100000; ++i) {
51 if (i % kInterval == 0)
52 stats_.Update(kPacketSize, now_ms);
53
54 // Approximately 1200 kbps expected. Not exact since when packets
55 // are removed we will jump 10 ms to the next packet.
56 if (i > kInterval) {
57 absl::optional<uint32_t> rate = stats_.Rate(now_ms);
58 EXPECT_TRUE(static_cast<bool>(rate));
59 uint32_t samples = i / kInterval + 1;
60 uint64_t total_bits = samples * kPacketSize * 8;
61 uint32_t rate_bps = static_cast<uint32_t>((1000 * total_bits) / (i + 1));
62 EXPECT_NEAR(rate_bps, *rate, 22000u);
63 }
64 now_ms += 1;
65 }
66 now_ms += kWindowMs;
67 // The window is 2 seconds. If nothing has been received for that time
68 // the estimate should be 0.
69 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
70 }
71
TEST_F(RateStatisticsTest,IncreasingThenDecreasingBitrate)72 TEST_F(RateStatisticsTest, IncreasingThenDecreasingBitrate) {
73 int64_t now_ms = 0;
74 stats_.Reset();
75 // Expecting 0 after init.
76 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
77
78 stats_.Update(1000, ++now_ms);
79 const uint32_t kExpectedBitrate = 8000000;
80 // 1000 bytes per millisecond until plateau is reached.
81 int prev_error = kExpectedBitrate;
82 absl::optional<uint32_t> bitrate;
83 while (++now_ms < 10000) {
84 stats_.Update(1000, now_ms);
85 bitrate = stats_.Rate(now_ms);
86 EXPECT_TRUE(static_cast<bool>(bitrate));
87 int error = kExpectedBitrate - *bitrate;
88 error = std::abs(error);
89 // Expect the estimation error to decrease as the window is extended.
90 EXPECT_LE(error, prev_error + 1);
91 prev_error = error;
92 }
93 // Window filled, expect to be close to 8000000.
94 EXPECT_EQ(kExpectedBitrate, *bitrate);
95
96 // 1000 bytes per millisecond until 10-second mark, 8000 kbps expected.
97 while (++now_ms < 10000) {
98 stats_.Update(1000, now_ms);
99 bitrate = stats_.Rate(now_ms);
100 EXPECT_EQ(kExpectedBitrate, *bitrate);
101 }
102
103 // Zero bytes per millisecond until 0 is reached.
104 while (++now_ms < 20000) {
105 stats_.Update(0, now_ms);
106 absl::optional<uint32_t> new_bitrate = stats_.Rate(now_ms);
107 if (static_cast<bool>(new_bitrate) && *new_bitrate != *bitrate) {
108 // New bitrate must be lower than previous one.
109 EXPECT_LT(*new_bitrate, *bitrate);
110 } else {
111 // 0 kbps expected.
112 EXPECT_EQ(0u, *new_bitrate);
113 break;
114 }
115 bitrate = new_bitrate;
116 }
117
118 // Zero bytes per millisecond until 20-second mark, 0 kbps expected.
119 while (++now_ms < 20000) {
120 stats_.Update(0, now_ms);
121 EXPECT_EQ(0u, *stats_.Rate(now_ms));
122 }
123 }
124
TEST_F(RateStatisticsTest,ResetAfterSilence)125 TEST_F(RateStatisticsTest, ResetAfterSilence) {
126 int64_t now_ms = 0;
127 stats_.Reset();
128 // Expecting 0 after init.
129 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
130
131 const uint32_t kExpectedBitrate = 8000000;
132 // 1000 bytes per millisecond until the window has been filled.
133 int prev_error = kExpectedBitrate;
134 absl::optional<uint32_t> bitrate;
135 while (++now_ms < 10000) {
136 stats_.Update(1000, now_ms);
137 bitrate = stats_.Rate(now_ms);
138 if (bitrate) {
139 int error = kExpectedBitrate - *bitrate;
140 error = std::abs(error);
141 // Expect the estimation error to decrease as the window is extended.
142 EXPECT_LE(error, prev_error + 1);
143 prev_error = error;
144 }
145 }
146 // Window filled, expect to be close to 8000000.
147 EXPECT_EQ(kExpectedBitrate, *bitrate);
148
149 now_ms += kWindowMs + 1;
150 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
151 // Silence over window size should trigger auto reset for coming sample.
152 stats_.Update(1000, now_ms);
153 ++now_ms;
154 stats_.Update(1000, now_ms);
155 // We expect two samples of 1000 bytes, and that the bitrate is measured over
156 // active window instead of full window, which is now_ms - first_timestamp + 1
157 EXPECT_EQ(kExpectedBitrate, *stats_.Rate(now_ms));
158
159 // Manual reset, add the same samples again.
160 stats_.Reset();
161 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
162 stats_.Update(1000, now_ms);
163 ++now_ms;
164 stats_.Update(1000, now_ms);
165 // We expect two samples of 1000 bytes, and that the bitrate is measured over
166 // 2 ms (window size has been reset) i.e. 2 * 8 * 1000 / 0.002 = 8000000.
167 EXPECT_EQ(kExpectedBitrate, *stats_.Rate(now_ms));
168 }
169
TEST_F(RateStatisticsTest,HandlesChangingWindowSize)170 TEST_F(RateStatisticsTest, HandlesChangingWindowSize) {
171 int64_t now_ms = 0;
172 stats_.Reset();
173
174 // Sanity test window size.
175 EXPECT_TRUE(stats_.SetWindowSize(kWindowMs, now_ms));
176 EXPECT_FALSE(stats_.SetWindowSize(kWindowMs + 1, now_ms));
177 EXPECT_FALSE(stats_.SetWindowSize(0, now_ms));
178 EXPECT_TRUE(stats_.SetWindowSize(1, now_ms));
179 EXPECT_TRUE(stats_.SetWindowSize(kWindowMs, now_ms));
180
181 // Fill the buffer at a rate of 1 byte / millisecond (8 kbps).
182 const int kBatchSize = 10;
183 for (int i = 0; i <= kWindowMs; i += kBatchSize)
184 stats_.Update(kBatchSize, now_ms += kBatchSize);
185 EXPECT_EQ(static_cast<uint32_t>(8000), *stats_.Rate(now_ms));
186
187 // Halve the window size, rate should stay the same.
188 EXPECT_TRUE(stats_.SetWindowSize(kWindowMs / 2, now_ms));
189 EXPECT_EQ(static_cast<uint32_t>(8000), *stats_.Rate(now_ms));
190
191 // Double the window size again, rate should stay the same. (As the window
192 // won't actually expand until new bit and bobs fall into it.
193 EXPECT_TRUE(stats_.SetWindowSize(kWindowMs, now_ms));
194 EXPECT_EQ(static_cast<uint32_t>(8000), *stats_.Rate(now_ms));
195
196 // Fill the now empty half with bits it twice the rate.
197 for (int i = 0; i < kWindowMs / 2; i += kBatchSize)
198 stats_.Update(kBatchSize * 2, now_ms += kBatchSize);
199
200 // Rate should have increase be 50%.
201 EXPECT_EQ(static_cast<uint32_t>((8000 * 3) / 2), *stats_.Rate(now_ms));
202 }
203
TEST_F(RateStatisticsTest,RespectsWindowSizeEdges)204 TEST_F(RateStatisticsTest, RespectsWindowSizeEdges) {
205 int64_t now_ms = 0;
206 stats_.Reset();
207 // Expecting 0 after init.
208 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
209
210 // One byte per ms, using one big sample.
211 stats_.Update(kWindowMs, now_ms);
212 now_ms += kWindowMs - 2;
213 // Shouldn't work! (Only one sample, not full window size.)
214 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
215
216 // Window size should be full, and the single data point should be accepted.
217 ++now_ms;
218 absl::optional<uint32_t> bitrate = stats_.Rate(now_ms);
219 EXPECT_TRUE(static_cast<bool>(bitrate));
220 EXPECT_EQ(1000 * 8u, *bitrate);
221
222 // Add another, now we have twice the bitrate.
223 stats_.Update(kWindowMs, now_ms);
224 bitrate = stats_.Rate(now_ms);
225 EXPECT_TRUE(static_cast<bool>(bitrate));
226 EXPECT_EQ(2 * 1000 * 8u, *bitrate);
227
228 // Now that first sample should drop out...
229 now_ms += 1;
230 bitrate = stats_.Rate(now_ms);
231 EXPECT_TRUE(static_cast<bool>(bitrate));
232 EXPECT_EQ(1000 * 8u, *bitrate);
233 }
234
TEST_F(RateStatisticsTest,HandlesZeroCounts)235 TEST_F(RateStatisticsTest, HandlesZeroCounts) {
236 int64_t now_ms = 0;
237 stats_.Reset();
238 // Expecting 0 after init.
239 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
240
241 stats_.Update(kWindowMs, now_ms);
242 now_ms += kWindowMs - 1;
243 stats_.Update(0, now_ms);
244 absl::optional<uint32_t> bitrate = stats_.Rate(now_ms);
245 EXPECT_TRUE(static_cast<bool>(bitrate));
246 EXPECT_EQ(1000 * 8u, *bitrate);
247
248 // Move window along so first data point falls out.
249 ++now_ms;
250 bitrate = stats_.Rate(now_ms);
251 EXPECT_TRUE(static_cast<bool>(bitrate));
252 EXPECT_EQ(0u, *bitrate);
253
254 // Move window so last data point falls out.
255 now_ms += kWindowMs;
256 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
257 }
258
TEST_F(RateStatisticsTest,HandlesQuietPeriods)259 TEST_F(RateStatisticsTest, HandlesQuietPeriods) {
260 int64_t now_ms = 0;
261 stats_.Reset();
262 // Expecting 0 after init.
263 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
264
265 stats_.Update(0, now_ms);
266 now_ms += kWindowMs - 1;
267 absl::optional<uint32_t> bitrate = stats_.Rate(now_ms);
268 EXPECT_TRUE(static_cast<bool>(bitrate));
269 EXPECT_EQ(0u, *bitrate);
270
271 // Move window along so first data point falls out.
272 ++now_ms;
273 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
274
275 // Move window a long way out.
276 // This will cause an automatic reset of the window
277 // First data point won't give a valid result
278 now_ms += 2 * kWindowMs;
279 stats_.Update(0, now_ms);
280 bitrate = stats_.Rate(now_ms);
281 EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
282 // Second data point gives valid result
283 ++now_ms;
284 stats_.Update(0, now_ms);
285 bitrate = stats_.Rate(now_ms);
286 EXPECT_TRUE(static_cast<bool>(bitrate));
287 EXPECT_EQ(0u, *bitrate);
288 }
289
TEST_F(RateStatisticsTest,HandlesBigNumbers)290 TEST_F(RateStatisticsTest, HandlesBigNumbers) {
291 int64_t large_number = 0x100000000u;
292 int64_t now_ms = 0;
293 stats_.Update(large_number, now_ms++);
294 stats_.Update(large_number, now_ms);
295 EXPECT_TRUE(stats_.Rate(now_ms));
296 EXPECT_EQ(large_number * RateStatistics::kBpsScale, *stats_.Rate(now_ms));
297 }
298
TEST_F(RateStatisticsTest,HandlesTooLargeNumbers)299 TEST_F(RateStatisticsTest, HandlesTooLargeNumbers) {
300 int64_t very_large_number = std::numeric_limits<int64_t>::max();
301 int64_t now_ms = 0;
302 stats_.Update(very_large_number, now_ms++);
303 stats_.Update(very_large_number, now_ms);
304 // This should overflow the internal accumulator.
305 EXPECT_FALSE(stats_.Rate(now_ms));
306 }
307
TEST_F(RateStatisticsTest,HandlesSomewhatLargeNumbers)308 TEST_F(RateStatisticsTest, HandlesSomewhatLargeNumbers) {
309 int64_t very_large_number = std::numeric_limits<int64_t>::max();
310 int64_t now_ms = 0;
311 stats_.Update(very_large_number / 4, now_ms++);
312 stats_.Update(very_large_number / 4, now_ms);
313 // This should generate a rate of more than int64_t max, but still
314 // accumulate less than int64_t overflow.
315 EXPECT_FALSE(stats_.Rate(now_ms));
316 }
317
318 } // namespace
319