1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "net/nqe/observation_buffer.h"
11
12 #include <stddef.h>
13
14 #include <map>
15 #include <string>
16 #include <utility>
17 #include <vector>
18
19 #include "base/test/simple_test_tick_clock.h"
20 #include "base/time/time.h"
21 #include "net/nqe/network_quality_estimator_params.h"
22 #include "net/nqe/network_quality_observation.h"
23 #include "net/nqe/network_quality_observation_source.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 namespace net::nqe::internal {
27
28 namespace {
29
30 // Verify that the buffer size is never exceeded.
TEST(NetworkQualityObservationBufferTest,BoundedBuffer)31 TEST(NetworkQualityObservationBufferTest, BoundedBuffer) {
32 std::map<std::string, std::string> variation_params;
33 NetworkQualityEstimatorParams params(variation_params);
34 base::SimpleTestTickClock tick_clock;
35 tick_clock.Advance(base::Minutes(1));
36 ObservationBuffer observation_buffer(¶ms, &tick_clock, 1.0, 1.0);
37 const base::TimeTicks now = base::TimeTicks() + base::Seconds(1);
38 for (int i = 1; i <= 1000; ++i) {
39 observation_buffer.AddObservation(
40 Observation(i, now, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP));
41 // The number of entries should be at most the maximum buffer size.
42 EXPECT_GE(300u, observation_buffer.Size());
43 }
44 }
45
46 // Verify that the percentiles are monotonically non-decreasing when a weight is
47 // applied.
TEST(NetworkQualityObservationBufferTest,GetPercentileWithWeights)48 TEST(NetworkQualityObservationBufferTest, GetPercentileWithWeights) {
49 std::map<std::string, std::string> variation_params;
50 NetworkQualityEstimatorParams params(variation_params);
51 base::SimpleTestTickClock tick_clock;
52 tick_clock.Advance(base::Minutes(1));
53
54 ObservationBuffer observation_buffer(¶ms, &tick_clock, 0.98, 1.0);
55 const base::TimeTicks now = tick_clock.NowTicks();
56 for (int i = 1; i <= 100; ++i) {
57 tick_clock.Advance(base::Seconds(1));
58 observation_buffer.AddObservation(
59 Observation(i, tick_clock.NowTicks(), INT32_MIN,
60 NETWORK_QUALITY_OBSERVATION_SOURCE_TCP));
61 }
62 EXPECT_EQ(100U, observation_buffer.Size());
63
64 int32_t result_lowest = INT32_MAX;
65 int32_t result_highest = INT32_MIN;
66
67 for (int i = 1; i <= 100; ++i) {
68 size_t observations_count = 0;
69 // Verify that i'th percentile is more than i-1'th percentile.
70 std::optional<int32_t> result_i = observation_buffer.GetPercentile(
71 now, INT32_MIN, i, &observations_count);
72 EXPECT_EQ(100u, observations_count);
73 ASSERT_TRUE(result_i.has_value());
74 result_lowest = std::min(result_lowest, result_i.value());
75
76 result_highest = std::max(result_highest, result_i.value());
77
78 std::optional<int32_t> result_i_1 = observation_buffer.GetPercentile(
79 now, INT32_MIN, i - 1, &observations_count);
80 EXPECT_EQ(100u, observations_count);
81 ASSERT_TRUE(result_i_1.has_value());
82
83 EXPECT_LE(result_i_1.value(), result_i.value());
84 }
85 EXPECT_LT(result_lowest, result_highest);
86 }
87
88 // Verifies that the percentiles are correctly computed. All observations have
89 // the same timestamp.
TEST(NetworkQualityObservationBufferTest,PercentileSameTimestamps)90 TEST(NetworkQualityObservationBufferTest, PercentileSameTimestamps) {
91 std::map<std::string, std::string> variation_params;
92 NetworkQualityEstimatorParams params(variation_params);
93 base::SimpleTestTickClock tick_clock;
94 tick_clock.Advance(base::Minutes(1));
95 ObservationBuffer buffer(¶ms, &tick_clock, 0.5, 1.0);
96 ASSERT_EQ(0u, buffer.Size());
97 ASSERT_LT(0u, buffer.Capacity());
98
99 const base::TimeTicks now = tick_clock.NowTicks();
100
101 size_t observations_count = 0;
102 // Percentiles should be unavailable when no observations are available.
103 EXPECT_FALSE(
104 buffer
105 .GetPercentile(base::TimeTicks(), INT32_MIN, 50,
106 &observations_count)
107 .has_value());
108 EXPECT_EQ(0u, observations_count);
109
110 // Insert samples from {1,2,3,..., 100}. First insert odd samples, then even
111 // samples. This helps in verifying that the order of samples does not matter.
112 for (int i = 1; i <= 99; i += 2) {
113 buffer.AddObservation(Observation(i, now, INT32_MIN,
114 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP));
115 EXPECT_TRUE(buffer.GetPercentile(base::TimeTicks(), INT32_MIN, 50, nullptr)
116 .has_value());
117 ASSERT_EQ(static_cast<size_t>(i / 2 + 1), buffer.Size());
118 }
119
120 for (int i = 2; i <= 100; i += 2) {
121 buffer.AddObservation(Observation(i, now, INT32_MIN,
122 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP));
123 EXPECT_TRUE(buffer.GetPercentile(base::TimeTicks(), INT32_MIN, 50, nullptr)
124 .has_value());
125 ASSERT_EQ(static_cast<size_t>(i / 2 + 50), buffer.Size());
126 }
127
128 ASSERT_EQ(100u, buffer.Size());
129
130 for (int i = 0; i <= 100; ++i) {
131 // Checks if the difference between actual result and the computed result is
132 // less than 1. This is required because computed percentiles may be
133 // slightly different from what is expected due to floating point
134 // computation errors and integer rounding off errors.
135 std::optional<int32_t> result = buffer.GetPercentile(
136 base::TimeTicks(), INT32_MIN, i, &observations_count);
137 EXPECT_EQ(100u, observations_count);
138 EXPECT_TRUE(result.has_value());
139 EXPECT_NEAR(result.value(), i, 1.0);
140 }
141
142 EXPECT_FALSE(buffer
143 .GetPercentile(now + base::Seconds(1), INT32_MIN, 50,
144 &observations_count)
145 .has_value());
146 EXPECT_EQ(0u, observations_count);
147
148 // Percentiles should be unavailable when no observations are available.
149 buffer.Clear();
150 EXPECT_FALSE(
151 buffer
152 .GetPercentile(base::TimeTicks(), INT32_MIN, 50,
153 &observations_count)
154 .has_value());
155 EXPECT_EQ(0u, observations_count);
156 }
157
158 // Verifies that the percentiles are correctly computed. Observations have
159 // different timestamps with half the observations being very old and the rest
160 // of them being very recent. Percentiles should factor in recent observations
161 // much more heavily than older samples.
TEST(NetworkQualityObservationBufferTest,PercentileDifferentTimestamps)162 TEST(NetworkQualityObservationBufferTest, PercentileDifferentTimestamps) {
163 std::map<std::string, std::string> variation_params;
164 NetworkQualityEstimatorParams params(variation_params);
165 base::SimpleTestTickClock tick_clock;
166 tick_clock.Advance(base::Minutes(1));
167 ObservationBuffer buffer(¶ms, &tick_clock, 0.5, 1.0);
168 const base::TimeTicks now = tick_clock.NowTicks();
169 const base::TimeTicks very_old = now - base::Days(7);
170
171 size_t observations_count;
172
173 // Network quality should be unavailable when no observations are available.
174 EXPECT_FALSE(
175 buffer
176 .GetPercentile(base::TimeTicks(), INT32_MIN, 50,
177 &observations_count)
178 .has_value());
179 EXPECT_EQ(0u, observations_count);
180
181 // First 50 samples have very old timestamps.
182 for (int i = 1; i <= 50; ++i) {
183 buffer.AddObservation(Observation(i, very_old, INT32_MIN,
184 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP));
185 }
186
187 // Next 50 (i.e., from 51 to 100) have recent timestamps.
188 for (int i = 51; i <= 100; ++i) {
189 buffer.AddObservation(Observation(i, now, INT32_MIN,
190 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP));
191 }
192
193 // Older samples have very little weight. So, all percentiles are >= 51
194 // (lowest value among recent observations).
195 for (int i = 1; i < 100; ++i) {
196 // Checks if the difference between the two integers is less than 1. This is
197 // required because computed percentiles may be slightly different from
198 // what is expected due to floating point computation errors and integer
199 // rounding off errors.
200 std::optional<int32_t> result =
201 buffer.GetPercentile(very_old, INT32_MIN, i, &observations_count);
202 EXPECT_TRUE(result.has_value());
203 EXPECT_NEAR(result.value(), 51 + 0.49 * i, 1);
204 EXPECT_EQ(100u, observations_count);
205 }
206
207 EXPECT_FALSE(buffer.GetPercentile(now + base::Seconds(1), INT32_MIN, 50,
208 &observations_count));
209 EXPECT_EQ(0u, observations_count);
210 }
211
212 // Verifies that the percentiles are correctly computed. All observations have
213 // same timestamp with half the observations taken at low RSSI, and half the
214 // observations with high RSSI. Percentiles should be computed based on the
215 // current RSSI and the RSSI of the observations.
TEST(NetworkQualityObservationBufferTest,PercentileDifferentRSSI)216 TEST(NetworkQualityObservationBufferTest, PercentileDifferentRSSI) {
217 std::map<std::string, std::string> variation_params;
218 NetworkQualityEstimatorParams params(variation_params);
219 base::SimpleTestTickClock tick_clock;
220 tick_clock.Advance(base::Minutes(1));
221 ObservationBuffer buffer(¶ms, &tick_clock, 1.0, 0.25);
222 const base::TimeTicks now = tick_clock.NowTicks();
223 int32_t high_rssi = 4;
224 int32_t low_rssi = 0;
225
226 // Network quality should be unavailable when no observations are available.
227 EXPECT_FALSE(buffer.GetPercentile(base::TimeTicks(), INT32_MIN, 50, nullptr)
228 .has_value());
229
230 // First 50 samples have very low RSSI.
231 for (int i = 1; i <= 50; ++i) {
232 buffer.AddObservation(
233 Observation(i, now, low_rssi, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP));
234 }
235
236 // Next 50 (i.e., from 51 to 100) have high RSSI.
237 for (int i = 51; i <= 100; ++i) {
238 buffer.AddObservation(Observation(i, now, high_rssi,
239 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP));
240 }
241
242 // When the current RSSI is |high_rssi|, higher weight should be assigned
243 // to observations that were taken at |high_rssi|.
244 for (int i = 1; i < 100; ++i) {
245 std::optional<int32_t> result =
246 buffer.GetPercentile(now, high_rssi, i, nullptr);
247 EXPECT_TRUE(result.has_value());
248 EXPECT_NEAR(result.value(), 51 + 0.49 * i, 2);
249 }
250
251 // When the current RSSI is |low_rssi|, higher weight should be assigned
252 // to observations that were taken at |low_rssi|.
253 for (int i = 1; i < 100; ++i) {
254 std::optional<int32_t> result =
255 buffer.GetPercentile(now, low_rssi, i, nullptr);
256 EXPECT_TRUE(result.has_value());
257 EXPECT_NEAR(result.value(), i / 2, 2);
258 }
259 }
260
261 // Verifies that the percentiles are correctly computed when some of the
262 // observation sources are disallowed. All observations have the same timestamp.
TEST(NetworkQualityObservationBufferTest,RemoveObservations)263 TEST(NetworkQualityObservationBufferTest, RemoveObservations) {
264 std::map<std::string, std::string> variation_params;
265 NetworkQualityEstimatorParams params(variation_params);
266 base::SimpleTestTickClock tick_clock;
267 tick_clock.Advance(base::Minutes(1));
268
269 ObservationBuffer buffer(¶ms, &tick_clock, 0.5, 1.0);
270 const base::TimeTicks now = tick_clock.NowTicks();
271
272 // Insert samples from {1,2,3,..., 100}. First insert odd samples, then even
273 // samples. This helps in verifying that the order of samples does not matter.
274 for (int i = 1; i <= 99; i += 2) {
275 buffer.AddObservation(Observation(i, now, INT32_MIN,
276 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP));
277 }
278 EXPECT_EQ(50u, buffer.Size());
279
280 // Add samples for TCP and QUIC observations which should not be taken into
281 // account when computing the percentile.
282 for (int i = 1; i <= 99; i += 2) {
283 buffer.AddObservation(Observation(10000, now, INT32_MIN,
284 NETWORK_QUALITY_OBSERVATION_SOURCE_TCP));
285 buffer.AddObservation(Observation(10000, now, INT32_MIN,
286 NETWORK_QUALITY_OBSERVATION_SOURCE_QUIC));
287 }
288 EXPECT_EQ(150u, buffer.Size());
289
290 for (int i = 2; i <= 100; i += 2) {
291 buffer.AddObservation(Observation(i, now, INT32_MIN,
292 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP));
293 }
294 EXPECT_EQ(200u, buffer.Size());
295
296 bool deleted_observation_sources[NETWORK_QUALITY_OBSERVATION_SOURCE_MAX] = {
297 false};
298
299 // Since all entries in |deleted_observation_sources| are set to false, no
300 // observations should be deleted.
301 buffer.RemoveObservationsWithSource(deleted_observation_sources);
302 EXPECT_EQ(200u, buffer.Size());
303
304 // 50 TCP and 50 QUIC observations should be deleted.
305 deleted_observation_sources[NETWORK_QUALITY_OBSERVATION_SOURCE_TCP] = true;
306 deleted_observation_sources[NETWORK_QUALITY_OBSERVATION_SOURCE_QUIC] = true;
307 buffer.RemoveObservationsWithSource(deleted_observation_sources);
308 EXPECT_EQ(100u, buffer.Size());
309
310 for (int i = 0; i <= 100; ++i) {
311 // Checks if the difference between the two integers is less than 1. This is
312 // required because computed percentiles may be slightly different from
313 // what is expected due to floating point computation errors and integer
314 // rounding off errors.
315 std::optional<int32_t> result =
316 buffer.GetPercentile(base::TimeTicks(), INT32_MIN, i, nullptr);
317 EXPECT_TRUE(result.has_value());
318 EXPECT_NEAR(result.value(), i, 1);
319 }
320
321 deleted_observation_sources[NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP] = true;
322 buffer.RemoveObservationsWithSource(deleted_observation_sources);
323 EXPECT_EQ(0u, buffer.Size());
324 }
325
TEST(NetworkQualityObservationBufferTest,TestGetMedianRTTSince)326 TEST(NetworkQualityObservationBufferTest, TestGetMedianRTTSince) {
327 std::map<std::string, std::string> variation_params;
328 NetworkQualityEstimatorParams params(variation_params);
329 base::SimpleTestTickClock tick_clock;
330 tick_clock.Advance(base::Minutes(1));
331 ObservationBuffer buffer(¶ms, &tick_clock, 0.5, 1.0);
332 base::TimeTicks now = tick_clock.NowTicks();
333 base::TimeTicks old = now - base::Milliseconds(1);
334 ASSERT_NE(old, now);
335
336 // First sample has very old timestamp.
337 buffer.AddObservation(
338 Observation(1, old, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP));
339
340 buffer.AddObservation(Observation(100, now, INT32_MIN,
341 NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP));
342
343 const struct {
344 base::TimeTicks start_timestamp;
345 bool expect_network_quality_available;
346 base::TimeDelta expected_url_request_rtt;
347 } tests[] = {
348 {now + base::Seconds(10), false, base::Milliseconds(0)},
349 {now, true, base::Milliseconds(100)},
350 {now - base::Microseconds(500), true, base::Milliseconds(100)},
351
352 };
353
354 for (const auto& test : tests) {
355 std::optional<int32_t> url_request_rtt =
356 buffer.GetPercentile(test.start_timestamp, INT32_MIN, 50, nullptr);
357 EXPECT_EQ(test.expect_network_quality_available,
358 url_request_rtt.has_value());
359
360 if (test.expect_network_quality_available) {
361 EXPECT_EQ(test.expected_url_request_rtt.InMillisecondsF(),
362 url_request_rtt.value());
363 }
364 }
365 }
366
367
368 } // namespace
369
370 } // namespace net::nqe::internal
371