OLD | NEW |
---|---|
(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 | |
OLD | NEW |