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

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: Simplified and polished design, still ironing out tests 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 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/location.h"
14 #include "base/macros.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/run_loop.h"
18 #include "base/test/simple_test_tick_clock.h"
19 #include "base/time/tick_clock.h"
20 #include "base/time/time.h"
21 #include "base/timer/mock_timer.h"
22 #include "base/timer/timer.h"
23 #include "components/data_usage/core/data_use.h"
24 #include "components/data_usage/core/data_use_amortizer.h"
25 #include "net/base/network_change_notifier.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 #include "url/gurl.h"
28
29 namespace data_usage {
30 namespace android {
31
32 namespace {
33
34 // The delay between receiving DataUse and querying TrafficStats byte counts for
35 // amortization.
36 const base::TimeDelta kTrafficStatsQueryDelay =
37 base::TimeDelta::FromMilliseconds(50);
38
39 // The longest amount of time that an amortization run can be delayed for.
40 const base::TimeDelta kMaxAmortizationDelay =
41 base::TimeDelta::FromMilliseconds(200);
42
43 // The maximum allowed size of the DataUse buffer.
44 const size_t kMaxDataUseBufferSize = 8;
45
46 // Synthesizes a fake scoped_ptr<DataUse> with the given |tx_bytes| and
47 // |rx_bytes|, using arbitrary values for all other fields.
48 scoped_ptr<DataUse> CreateDataUse(int64_t tx_bytes, int64_t rx_bytes) {
49 return scoped_ptr<DataUse>(new DataUse(
50 GURL("http://example.com"), base::TimeTicks() /* request_start */,
51 GURL("http://examplefirstparty.com"), 10 /* tab_id */,
52 net::NetworkChangeNotifier::CONNECTION_2G, "example_mcc_mnc", tx_bytes,
53 rx_bytes));
54 }
55
56 class MockTimerWithTickClock : public base::MockTimer {
57 public:
58 MockTimerWithTickClock(bool retain_user_task,
59 bool is_repeating,
60 base::TickClock* tick_clock)
61 : base::MockTimer(retain_user_task, is_repeating),
62 tick_clock_(tick_clock) {}
63
64 ~MockTimerWithTickClock() override {}
65
66 void Reset() override {
67 start_time_ = tick_clock_->NowTicks();
68 base::MockTimer::Reset();
69 }
70
71 const base::TimeTicks& start_time() const { return start_time_; }
72
73 private:
74 base::TickClock* tick_clock_;
75 base::TimeTicks start_time_;
76
77 DISALLOW_COPY_AND_ASSIGN(MockTimerWithTickClock);
78 };
79
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 // Simulate the passage of time, firing timers if appropriate.
135 void AdvanceTime(const base::TimeDelta& delta) {
136 run_loop_.RunUntilIdle();
137 test_tick_clock_->Advance(delta);
138
139 // Fire the |mock_timer_| if necessary.
140 if (mock_timer_->IsRunning() &&
141 mock_timer_->start_time() + mock_timer_->GetCurrentDelay() >=
142 test_tick_clock_->NowTicks()) {
143 mock_timer_->Fire();
144 }
145 run_loop_.RunUntilIdle();
146 }
147
148 // Expects that |expected| and |actual| are equivalent.
149 void ExpectDataUse(scoped_ptr<DataUse> expected, scoped_ptr<DataUse> actual) {
150 ++data_use_callback_call_count_;
151
152 // Have separate checks for the |tx_bytes| and |rx_bytes| for better error
153 // messages.
154 EXPECT_EQ(expected->tx_bytes, actual->tx_bytes);
155 EXPECT_EQ(expected->rx_bytes, actual->rx_bytes);
156 EXPECT_EQ(*expected, *actual);
157 }
158
159 // Convenience function for creating an ExpectDataUse callback.
bengr 2015/11/10 18:12:50 // Creates an ExpectDataUse callback, as a conveni
sclittle 2015/11/11 02:10:08 Done.
160 DataUseAmortizer::DataUseConsumerCallback ExpectDataUseCallback(
161 scoped_ptr<DataUse> expected) {
162 return base::Bind(&TrafficStatsAmortizerTest::ExpectDataUse,
163 base::Unretained(this), base::Passed(&expected));
164 }
165
166 base::TimeTicks NowTicks() const { return test_tick_clock_->NowTicks(); }
167
168 TestTrafficStatsAmortizer* amortizer() { return &amortizer_; }
169
170 int data_use_callback_call_count() const {
171 return data_use_callback_call_count_;
172 }
173
174 private:
175 base::MessageLoop message_loop_;
176 base::RunLoop run_loop_;
177 // Weak, owned by |amortizer_|.
bengr 2015/11/10 18:12:50 Add blank line above.
sclittle 2015/11/11 02:10:08 Done.
178 base::SimpleTestTickClock* test_tick_clock_;
179 // Weak, owned by |amortizer_|.
bengr 2015/11/10 18:12:50 Add blank line above.
sclittle 2015/11/11 02:10:08 Done.
180 MockTimerWithTickClock* mock_timer_;
bengr 2015/11/10 18:12:50 Add blank line below.
sclittle 2015/11/11 02:10:08 Done.
181 TestTrafficStatsAmortizer amortizer_;
182
183 // The number of times ExpectDataUse has been called.
184 int data_use_callback_call_count_;
185
186 DISALLOW_COPY_AND_ASSIGN(TrafficStatsAmortizerTest);
187 };
188
189 TEST_F(TrafficStatsAmortizerTest, AmortizeWithTrafficStatsAlwaysUnavailable) {
190 amortizer()->SetNextTrafficStats(false, -1, -1);
191 // Do it three times for good measure.
192 for (int i = 0; i < 3; ++i) {
193 // Extra bytes should be ignored since TrafficStats are unavailable.
194 amortizer()->OnExtraBytes(1337, 9001);
195 // The original DataUse should be unchanged.
196 amortizer()->AmortizeDataUse(
197 CreateDataUse(100, 1000),
198 ExpectDataUseCallback(CreateDataUse(100, 1000)));
199
200 AdvanceTime(kTrafficStatsQueryDelay);
201 EXPECT_EQ(i + 1, data_use_callback_call_count());
202 }
203 }
204
205 TEST_F(TrafficStatsAmortizerTest, AmortizeDataUse) {
206 // The initial values of TrafficStats shouldn't matter.
207 amortizer()->SetNextTrafficStats(true, 1337, 9001);
208
209 // The first amortization run should not change any byte counts because
210 // there's no TrafficStats delta to work with.
211 amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
212 ExpectDataUseCallback(CreateDataUse(50, 500)));
213 amortizer()->AmortizeDataUse(CreateDataUse(100, 1000),
214 ExpectDataUseCallback(CreateDataUse(100, 1000)));
215 AdvanceTime(kTrafficStatsQueryDelay);
216 EXPECT_EQ(2, data_use_callback_call_count());
217
218 // This amortization run, tx_bytes and rx_bytes should be doubled.
219 amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
220 ExpectDataUseCallback(CreateDataUse(100, 1000)));
221 AdvanceTime(kTrafficStatsQueryDelay / 2);
222
223 // Another DataUse is reported before the amortizer queries TrafficStats.
224 amortizer()->AmortizeDataUse(CreateDataUse(100, 1000),
225 ExpectDataUseCallback(CreateDataUse(200, 2000)));
226 AdvanceTime(kTrafficStatsQueryDelay / 2);
227
228 // Then, the TrafficStats values update with the new bytes. The second run
229 // callbacks should not have been called yet.
230 amortizer()->AddTrafficStats(300, 3000);
231 EXPECT_EQ(2, data_use_callback_call_count());
232
233 // The callbacks should fire once kTrafficStatsQueryDelay has passed since the
234 // DataUse was passed to the amortizer.
235 AdvanceTime(kTrafficStatsQueryDelay / 2);
236 EXPECT_EQ(4, data_use_callback_call_count());
237 }
238
239 TEST_F(TrafficStatsAmortizerTest, AmortizeWithExtraBytes) {
240 // The initial values of TrafficStats shouldn't matter.
241 amortizer()->SetNextTrafficStats(true, 1337, 9001);
242
243 // Do the first amortization run with TrafficStats unavailable.
244 amortizer()->OnExtraBytes(100, 1000);
245 AdvanceTime(kTrafficStatsQueryDelay);
246 EXPECT_EQ(0, data_use_callback_call_count());
247
248 // On the second amortization run, byte counts should double.
249 amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
250 ExpectDataUseCallback(CreateDataUse(100, 1000)));
251 amortizer()->OnExtraBytes(500, 5000);
252 amortizer()->AddTrafficStats(1100, 11000);
253 AdvanceTime(kTrafficStatsQueryDelay);
254 EXPECT_EQ(1, data_use_callback_call_count());
255 }
256
257 TEST_F(TrafficStatsAmortizerTest, AmortizeWithNegativeOverhead) {
258 // The initial values of TrafficStats shouldn't matter.
259 amortizer()->SetNextTrafficStats(true, 1337, 9001);
260
261 // Do the first amortization run with TrafficStats unavailable.
262 amortizer()->OnExtraBytes(100, 1000);
263 AdvanceTime(kTrafficStatsQueryDelay);
264 EXPECT_EQ(0, data_use_callback_call_count());
265
266 // On the second amortization run, byte counts should halve.
267 amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
bengr 2015/11/10 18:12:50 Also add cases where sent or received bytes is at
sclittle 2015/11/11 02:10:08 Done. Note that these are weird since floating poi
268 ExpectDataUseCallback(CreateDataUse(25, 250)));
269 amortizer()->AddTrafficStats(25, 250);
270 AdvanceTime(kTrafficStatsQueryDelay);
271 EXPECT_EQ(1, data_use_callback_call_count());
272 }
273
274 TEST_F(TrafficStatsAmortizerTest, AmortizeWithZeroPreAmortizationBytes) {
275 // The initial values of TrafficStats shouldn't matter.
276 amortizer()->SetNextTrafficStats(true, 1337, 9001);
277
278 // Do the first amortization run with TrafficStats unavailable.
279 amortizer()->OnExtraBytes(100, 1000);
280 AdvanceTime(kTrafficStatsQueryDelay);
281 EXPECT_EQ(0, data_use_callback_call_count());
282
283 // On the second amortization run, byte counts should stay 0.
284 amortizer()->AmortizeDataUse(CreateDataUse(0, 0),
bengr 2015/11/10 18:12:50 Add cases where sent or received bytes is 0.
sclittle 2015/11/11 02:10:08 Done.
285 ExpectDataUseCallback(CreateDataUse(0, 0)));
286 amortizer()->AddTrafficStats(100, 1000);
287 AdvanceTime(kTrafficStatsQueryDelay);
288 EXPECT_EQ(1, data_use_callback_call_count());
289 }
290
291 TEST_F(TrafficStatsAmortizerTest, AmortizeAtMaxDelay) {
292 // The initial values of TrafficStats shouldn't matter.
293 amortizer()->SetNextTrafficStats(true, 1337, 9001);
294
295 // Do the first amortization run with TrafficStats unavailable.
296 amortizer()->OnExtraBytes(100, 1000);
297 AdvanceTime(kTrafficStatsQueryDelay);
298 EXPECT_EQ(0, data_use_callback_call_count());
299
300 // On the second amortization run, byte counts should double.
301 amortizer()->AddTrafficStats(1000, 10000);
302 amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
303 ExpectDataUseCallback(CreateDataUse(100, 1000)));
304
305 // Simulate 9 cases of extra bytes being reported, each before TrafficStats
bengr 2015/11/10 18:12:50 Why 9? Do you mean kMaxBufferSize + 1?
sclittle 2015/11/11 02:10:08 Reworded comment. I meant that we'll keep reportin
306 // would be queried.
307 const base::TimeDelta kSmallDelay = kMaxAmortizationDelay / 10;
bengr 2015/11/10 18:12:50 Why / 10?
sclittle 2015/11/11 02:10:08 Added comments.
308 EXPECT_LT(kSmallDelay, kMaxAmortizationDelay);
309
310 AdvanceTime(kSmallDelay);
311 for (int i = 0; i < 9; ++i) {
312 EXPECT_EQ(0, data_use_callback_call_count());
313 amortizer()->OnExtraBytes(50, 500);
314 AdvanceTime(kSmallDelay);
315 }
316
317 // The final time, the amortizer should have given up on waiting to query
318 // TrafficStats and just have amortized as soon as it hit the deadline of
319 // 200ms.
320 EXPECT_EQ(1, data_use_callback_call_count());
321 }
322
323 TEST_F(TrafficStatsAmortizerTest, AmortizeAtMaxBufferSize) {
324 // The initial values of TrafficStats shouldn't matter.
325 amortizer()->SetNextTrafficStats(true, 1337, 9001);
326
327 // Do the first amortization run with TrafficStats unavailable.
328 amortizer()->OnExtraBytes(100, 1000);
329 AdvanceTime(kTrafficStatsQueryDelay);
330 EXPECT_EQ(0, data_use_callback_call_count());
331
332 // The second run will fill up with 9 consecutive DataUse objects, which will
333 // be amortized immediately once the buffer exceeds maximum size.
334 amortizer()->AddTrafficStats(900, 9000);
335 for (int i = 0; i < 9; ++i) {
336 EXPECT_EQ(0, data_use_callback_call_count());
337 amortizer()->AmortizeDataUse(
338 CreateDataUse(50, 500),
339 ExpectDataUseCallback(CreateDataUse(100, 1000)));
340 }
341
342 EXPECT_EQ(9, data_use_callback_call_count());
343 }
344
345 } // namespace
346
347 } // namespace android
348 } // namespace data_usage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698