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