Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(326)

Side by Side Diff: components/data_usage/android/traffic_stats_amortizer_unittest.cc

Issue 1390993005: Amortize data usage using TrafficStats on Android. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@data_use_buffering
Patch Set: Fixed nit Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/data_usage/android/traffic_stats_amortizer.h"
6
7 #include <stdint.h>
8
9 #include <string>
10
11 #include "base/bind.h"
12 #include "base/macros.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/run_loop.h"
16 #include "base/test/simple_test_tick_clock.h"
17 #include "base/time/tick_clock.h"
18 #include "base/time/time.h"
19 #include "base/timer/mock_timer.h"
20 #include "base/timer/timer.h"
21 #include "components/data_usage/core/data_use.h"
22 #include "components/data_usage/core/data_use_amortizer.h"
23 #include "net/base/network_change_notifier.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "url/gurl.h"
26
27 namespace data_usage {
28 namespace android {
29
30 namespace {
31
32 // The delay between receiving DataUse and querying TrafficStats byte counts for
33 // amortization.
34 const base::TimeDelta kTrafficStatsQueryDelay =
35 base::TimeDelta::FromMilliseconds(50);
36
37 // The longest amount of time that an amortization run can be delayed for.
38 const base::TimeDelta kMaxAmortizationDelay =
39 base::TimeDelta::FromMilliseconds(200);
40
41 // The maximum allowed size of the DataUse buffer.
42 const size_t kMaxDataUseBufferSize = 8;
43
44 // Synthesizes a fake scoped_ptr<DataUse> with the given |tx_bytes| and
45 // |rx_bytes|, using arbitrary values for all other fields.
46 scoped_ptr<DataUse> CreateDataUse(int64_t tx_bytes, int64_t rx_bytes) {
47 return scoped_ptr<DataUse>(new DataUse(
48 GURL("http://example.com"), base::TimeTicks() /* request_start */,
49 GURL("http://examplefirstparty.com"), 10 /* tab_id */,
50 net::NetworkChangeNotifier::CONNECTION_2G, "example_mcc_mnc", tx_bytes,
51 rx_bytes));
52 }
53
54 // Class that represents a base::MockTimer with an attached base::TickClock, so
55 // that it can update its |desired_run_time()| according to the current time
56 // when the timer is reset.
57 class MockTimerWithTickClock : public base::MockTimer {
58 public:
59 MockTimerWithTickClock(bool retain_user_task,
60 bool is_repeating,
61 base::TickClock* tick_clock)
62 : base::MockTimer(retain_user_task, is_repeating),
63 tick_clock_(tick_clock) {}
64
65 ~MockTimerWithTickClock() override {}
66
67 void Reset() override {
68 base::MockTimer::Reset();
69 set_desired_run_time(tick_clock_->NowTicks() + GetCurrentDelay());
70 }
71
72 private:
73 base::TickClock* tick_clock_;
74
75 DISALLOW_COPY_AND_ASSIGN(MockTimerWithTickClock);
76 };
77
78 // A TrafficStatsAmortizer for testing that allows for tests to simulate the
79 // byte counts returned from TrafficStats.
80 class TestTrafficStatsAmortizer : public TrafficStatsAmortizer {
81 public:
82 TestTrafficStatsAmortizer(scoped_ptr<base::TickClock> tick_clock,
83 scoped_ptr<base::Timer> traffic_stats_query_timer)
84 : TrafficStatsAmortizer(tick_clock.Pass(),
85 traffic_stats_query_timer.Pass(),
86 kTrafficStatsQueryDelay,
87 kMaxAmortizationDelay,
88 kMaxDataUseBufferSize),
89 next_traffic_stats_available_(false),
90 next_traffic_stats_tx_bytes_(-1),
91 next_traffic_stats_rx_bytes_(-1) {}
92
93 ~TestTrafficStatsAmortizer() override {}
94
95 void SetNextTrafficStats(bool available, int64_t tx_bytes, int64_t rx_bytes) {
96 next_traffic_stats_available_ = available;
97 next_traffic_stats_tx_bytes_ = tx_bytes;
98 next_traffic_stats_rx_bytes_ = rx_bytes;
99 }
100
101 void AddTrafficStats(int64_t tx_bytes, int64_t rx_bytes) {
102 next_traffic_stats_tx_bytes_ += tx_bytes;
103 next_traffic_stats_rx_bytes_ += rx_bytes;
104 }
105
106 protected:
107 bool QueryTrafficStats(int64_t* tx_bytes, int64_t* rx_bytes) const override {
108 *tx_bytes = next_traffic_stats_tx_bytes_;
109 *rx_bytes = next_traffic_stats_rx_bytes_;
110 return next_traffic_stats_available_;
111 }
112
113 private:
114 bool next_traffic_stats_available_;
115 int64_t next_traffic_stats_tx_bytes_;
116 int64_t next_traffic_stats_rx_bytes_;
117
118 DISALLOW_COPY_AND_ASSIGN(TestTrafficStatsAmortizer);
119 };
120
121 class TrafficStatsAmortizerTest : public testing::Test {
122 public:
123 TrafficStatsAmortizerTest()
124 : test_tick_clock_(new base::SimpleTestTickClock()),
125 mock_timer_(new MockTimerWithTickClock(false, false, test_tick_clock_)),
126 amortizer_(scoped_ptr<base::TickClock>(test_tick_clock_),
127 scoped_ptr<base::Timer>(mock_timer_)),
128 data_use_callback_call_count_(0) {}
129
130 ~TrafficStatsAmortizerTest() override {
131 EXPECT_FALSE(mock_timer_->IsRunning());
132 }
133
134 // Simulates the passage of time by |delta|, firing timers when appropriate.
135 void AdvanceTime(const base::TimeDelta& delta) {
136 const base::TimeTicks end_time = test_tick_clock_->NowTicks() + delta;
137 base::RunLoop().RunUntilIdle();
138
139 while (test_tick_clock_->NowTicks() < end_time) {
140 PumpMockTimer();
141
142 // If |mock_timer_| is scheduled to fire in the future before |end_time|,
143 // advance to that time.
144 if (mock_timer_->IsRunning() &&
145 mock_timer_->desired_run_time() < end_time) {
146 test_tick_clock_->Advance(mock_timer_->desired_run_time() -
147 test_tick_clock_->NowTicks());
148 } else {
149 // Otherwise, advance to |end_time|.
150 test_tick_clock_->Advance(end_time - test_tick_clock_->NowTicks());
151 }
152 }
153 PumpMockTimer();
154 }
155
156 // Skip the first amortization run where TrafficStats byte count deltas are
157 // unavailable, for convenience.
158 void SkipFirstAmortizationRun() {
159 // The initial values of TrafficStats shouldn't matter.
160 amortizer()->SetNextTrafficStats(true, 0, 0);
161
162 // Do the first amortization run with TrafficStats unavailable.
163 amortizer()->OnExtraBytes(100, 1000);
164 AdvanceTime(kTrafficStatsQueryDelay);
165 EXPECT_EQ(0, data_use_callback_call_count());
166 }
167
168 // Expects that |expected| and |actual| are equivalent.
169 void ExpectDataUse(scoped_ptr<DataUse> expected, scoped_ptr<DataUse> actual) {
170 ++data_use_callback_call_count_;
171
172 // Have separate checks for the |tx_bytes| and |rx_bytes|, since those are
173 // calculated with floating point arithmetic.
174 EXPECT_DOUBLE_EQ(static_cast<double>(expected->tx_bytes),
175 static_cast<double>(actual->tx_bytes));
176 EXPECT_DOUBLE_EQ(static_cast<double>(expected->rx_bytes),
177 static_cast<double>(actual->rx_bytes));
178
179 // Copy the byte counts over from |expected| just in case they're only
180 // slightly different due to floating point error, so that this doesn't
181 // cause the equality comparison below to fail.
182 actual->tx_bytes = expected->tx_bytes;
183 actual->rx_bytes = expected->rx_bytes;
184 EXPECT_EQ(*expected, *actual);
185 }
186
187 // Creates an ExpectDataUse callback, as a convenience.
188 DataUseAmortizer::AmortizationCompleteCallback ExpectDataUseCallback(
189 scoped_ptr<DataUse> expected) {
190 return base::Bind(&TrafficStatsAmortizerTest::ExpectDataUse,
191 base::Unretained(this), base::Passed(&expected));
192 }
193
194 base::TimeTicks NowTicks() const { return test_tick_clock_->NowTicks(); }
195
196 TestTrafficStatsAmortizer* amortizer() { return &amortizer_; }
197
198 int data_use_callback_call_count() const {
199 return data_use_callback_call_count_;
200 }
201
202 private:
203 // Pumps |mock_timer_|, firing it while it's scheduled to run now or in the
204 // past. After calling this, |mock_timer_| is either not running or is
205 // scheduled to run in the future.
206 void PumpMockTimer() {
207 // Fire the |mock_timer_| if the time has come up. Use a while loop in case
208 // the fired task started the timer again to fire immediately.
209 while (mock_timer_->IsRunning() &&
210 mock_timer_->desired_run_time() <= test_tick_clock_->NowTicks()) {
211 mock_timer_->Fire();
212 base::RunLoop().RunUntilIdle();
213 }
214 }
215
216 base::MessageLoop message_loop_;
217
218 // Weak, owned by |amortizer_|.
219 base::SimpleTestTickClock* test_tick_clock_;
220
221 // Weak, owned by |amortizer_|.
222 MockTimerWithTickClock* mock_timer_;
223
224 TestTrafficStatsAmortizer amortizer_;
225
226 // The number of times ExpectDataUse has been called.
227 int data_use_callback_call_count_;
228
229 DISALLOW_COPY_AND_ASSIGN(TrafficStatsAmortizerTest);
230 };
231
232 TEST_F(TrafficStatsAmortizerTest, AmortizeWithTrafficStatsAlwaysUnavailable) {
233 amortizer()->SetNextTrafficStats(false, -1, -1);
234 // Do it three times for good measure.
235 for (int i = 0; i < 3; ++i) {
236 // Extra bytes should be ignored since TrafficStats are unavailable.
237 amortizer()->OnExtraBytes(1337, 9001);
238 // The original DataUse should be unchanged.
239 amortizer()->AmortizeDataUse(
240 CreateDataUse(100, 1000),
241 ExpectDataUseCallback(CreateDataUse(100, 1000)));
242
243 AdvanceTime(kTrafficStatsQueryDelay);
244 EXPECT_EQ(i + 1, data_use_callback_call_count());
245 }
246 }
247
248 TEST_F(TrafficStatsAmortizerTest, AmortizeDataUse) {
249 // The initial values of TrafficStats shouldn't matter.
250 amortizer()->SetNextTrafficStats(true, 1337, 9001);
251
252 // The first amortization run should not change any byte counts because
253 // there's no TrafficStats delta to work with.
254 amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
255 ExpectDataUseCallback(CreateDataUse(50, 500)));
256 amortizer()->AmortizeDataUse(CreateDataUse(100, 1000),
257 ExpectDataUseCallback(CreateDataUse(100, 1000)));
258 AdvanceTime(kTrafficStatsQueryDelay);
259 EXPECT_EQ(2, data_use_callback_call_count());
260
261 // This amortization run, tx_bytes and rx_bytes should be doubled.
262 amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
263 ExpectDataUseCallback(CreateDataUse(100, 1000)));
264 AdvanceTime(kTrafficStatsQueryDelay / 2);
265
266 // Another DataUse is reported before the amortizer queries TrafficStats.
267 amortizer()->AmortizeDataUse(CreateDataUse(100, 1000),
268 ExpectDataUseCallback(CreateDataUse(200, 2000)));
269 AdvanceTime(kTrafficStatsQueryDelay / 2);
270
271 // Then, the TrafficStats values update with the new bytes. The second run
272 // callbacks should not have been called yet.
273 amortizer()->AddTrafficStats(300, 3000);
274 EXPECT_EQ(2, data_use_callback_call_count());
275
276 // The callbacks should fire once kTrafficStatsQueryDelay has passed since the
277 // DataUse was passed to the amortizer.
278 AdvanceTime(kTrafficStatsQueryDelay / 2);
279 EXPECT_EQ(4, data_use_callback_call_count());
280 }
281
282 TEST_F(TrafficStatsAmortizerTest, AmortizeWithExtraBytes) {
283 SkipFirstAmortizationRun();
284
285 // Byte counts should double.
286 amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
287 ExpectDataUseCallback(CreateDataUse(100, 1000)));
288 amortizer()->OnExtraBytes(500, 5000);
289 amortizer()->AddTrafficStats(1100, 11000);
290 AdvanceTime(kTrafficStatsQueryDelay);
291 EXPECT_EQ(1, data_use_callback_call_count());
292 }
293
294 TEST_F(TrafficStatsAmortizerTest, AmortizeWithNegativeOverhead) {
295 SkipFirstAmortizationRun();
296
297 // Byte counts should halve.
298 amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
299 ExpectDataUseCallback(CreateDataUse(25, 250)));
300 amortizer()->AddTrafficStats(25, 250);
301 AdvanceTime(kTrafficStatsQueryDelay);
302 EXPECT_EQ(1, data_use_callback_call_count());
303 }
304
305 TEST_F(TrafficStatsAmortizerTest, AmortizeWithMaxIntByteCounts) {
306 SkipFirstAmortizationRun();
307
308 // Byte counts should be unchanged.
309 amortizer()->AmortizeDataUse(
310 CreateDataUse(INT64_MAX, INT64_MAX),
311 ExpectDataUseCallback(CreateDataUse(INT64_MAX, INT64_MAX)));
312 amortizer()->SetNextTrafficStats(true, INT64_MAX, INT64_MAX);
313 AdvanceTime(kTrafficStatsQueryDelay);
314 EXPECT_EQ(1, data_use_callback_call_count());
315 }
316
317 TEST_F(TrafficStatsAmortizerTest, AmortizeWithMaxIntScaleFactor) {
318 SkipFirstAmortizationRun();
319
320 // Byte counts should be scaled up to INT64_MAX.
321 amortizer()->AmortizeDataUse(
322 CreateDataUse(1, 1),
323 ExpectDataUseCallback(CreateDataUse(INT64_MAX, INT64_MAX)));
324 amortizer()->SetNextTrafficStats(true, INT64_MAX, INT64_MAX);
325 AdvanceTime(kTrafficStatsQueryDelay);
326 EXPECT_EQ(1, data_use_callback_call_count());
327 }
328
329 TEST_F(TrafficStatsAmortizerTest, AmortizeWithZeroScaleFactor) {
330 SkipFirstAmortizationRun();
331
332 // Byte counts should be scaled down to 0.
333 amortizer()->AmortizeDataUse(CreateDataUse(INT64_MAX, INT64_MAX),
334 ExpectDataUseCallback(CreateDataUse(0, 0)));
335 amortizer()->SetNextTrafficStats(true, 0, 0);
336 AdvanceTime(kTrafficStatsQueryDelay);
337 EXPECT_EQ(1, data_use_callback_call_count());
338 }
339
340 TEST_F(TrafficStatsAmortizerTest, AmortizeWithZeroPreAmortizationBytes) {
341 SkipFirstAmortizationRun();
342
343 // Both byte counts should stay 0, even though TrafficStats saw bytes.
344 amortizer()->AmortizeDataUse(CreateDataUse(0, 0),
345 ExpectDataUseCallback(CreateDataUse(0, 0)));
346 amortizer()->AddTrafficStats(100, 1000);
347 AdvanceTime(kTrafficStatsQueryDelay);
348 EXPECT_EQ(1, data_use_callback_call_count());
349
350 // This time, only TX bytes are 0, so RX bytes should double, but TX bytes
351 // should stay 0.
352 amortizer()->AmortizeDataUse(CreateDataUse(0, 500),
353 ExpectDataUseCallback(CreateDataUse(0, 1000)));
354 amortizer()->AddTrafficStats(100, 1000);
355 AdvanceTime(kTrafficStatsQueryDelay);
356 EXPECT_EQ(2, data_use_callback_call_count());
357
358 // This time, only RX bytes are 0, so TX bytes should double, but RX bytes
359 // should stay 0.
360 amortizer()->AmortizeDataUse(CreateDataUse(50, 0),
361 ExpectDataUseCallback(CreateDataUse(100, 0)));
362 amortizer()->AddTrafficStats(100, 1000);
363 AdvanceTime(kTrafficStatsQueryDelay);
364 EXPECT_EQ(3, data_use_callback_call_count());
365 }
366
367 TEST_F(TrafficStatsAmortizerTest, AmortizeAtMaxDelay) {
368 SkipFirstAmortizationRun();
369
370 // Byte counts should double.
371 amortizer()->AddTrafficStats(1000, 10000);
372 amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
373 ExpectDataUseCallback(CreateDataUse(100, 1000)));
374
375 // kSmallDelay is a delay that's shorter than the delay before TrafficStats
376 // would be queried, where kMaxAmortizationDelay is a multiple of kSmallDelay.
377 const base::TimeDelta kSmallDelay = kMaxAmortizationDelay / 10;
378 EXPECT_LT(kSmallDelay, kMaxAmortizationDelay);
379
380 // Simulate multiple cases of extra bytes being reported, each before
381 // TrafficStats would be queried, until kMaxAmortizationDelay has elapsed.
382 AdvanceTime(kSmallDelay);
383 for (int64_t i = 0; i < kMaxAmortizationDelay / kSmallDelay - 1; ++i) {
384 EXPECT_EQ(0, data_use_callback_call_count());
385 amortizer()->OnExtraBytes(50, 500);
386 AdvanceTime(kSmallDelay);
387 }
388
389 // The final time, the amortizer should have given up on waiting to query
390 // TrafficStats and just have amortized as soon as it hit the deadline of
391 // kMaxAmortizationDelay.
392 EXPECT_EQ(1, data_use_callback_call_count());
393 }
394
395 TEST_F(TrafficStatsAmortizerTest, AmortizeAtMaxBufferSize) {
396 SkipFirstAmortizationRun();
397
398 // Report (max buffer size + 1) consecutive DataUse objects, which will be
399 // amortized immediately once the buffer exceeds maximum size.
400 amortizer()->AddTrafficStats(100 * (kMaxDataUseBufferSize + 1),
401 1000 * (kMaxDataUseBufferSize + 1));
402 for (size_t i = 0; i < kMaxDataUseBufferSize + 1; ++i) {
403 EXPECT_EQ(0, data_use_callback_call_count());
404 amortizer()->AmortizeDataUse(
405 CreateDataUse(50, 500),
406 ExpectDataUseCallback(CreateDataUse(100, 1000)));
407 }
408
409 EXPECT_EQ(static_cast<int>(kMaxDataUseBufferSize + 1),
410 data_use_callback_call_count());
411 }
412
413 } // namespace
414
415 } // namespace android
416 } // namespace data_usage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698