Chromium Code Reviews| 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 |