 Chromium Code Reviews
 Chromium Code Reviews Issue 2772313002:
  [Doodle] Don't refresh more than once per 15 mins  (Closed)
    
  
    Issue 2772313002:
  [Doodle] Don't refresh more than once per 15 mins  (Closed) 
  | OLD | NEW | 
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "components/doodle/doodle_service.h" | 5 #include "components/doodle/doodle_service.h" | 
| 6 | 6 | 
| 7 #include <memory> | 7 #include <memory> | 
| 8 #include <utility> | 8 #include <utility> | 
| 9 #include <vector> | 9 #include <vector> | 
| 10 | 10 | 
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 68 DoodleServiceTest() | 68 DoodleServiceTest() | 
| 69 : task_runner_(new base::TestMockTimeTaskRunner()), | 69 : task_runner_(new base::TestMockTimeTaskRunner()), | 
| 70 task_runner_handle_(task_runner_), | 70 task_runner_handle_(task_runner_), | 
| 71 tick_clock_(task_runner_->GetMockTickClock()), | 71 tick_clock_(task_runner_->GetMockTickClock()), | 
| 72 fetcher_(nullptr), | 72 fetcher_(nullptr), | 
| 73 expiry_timer_(nullptr) { | 73 expiry_timer_(nullptr) { | 
| 74 DoodleService::RegisterProfilePrefs(pref_service_.registry()); | 74 DoodleService::RegisterProfilePrefs(pref_service_.registry()); | 
| 75 | 75 | 
| 76 task_runner_->FastForwardBy(base::TimeDelta::FromHours(12345)); | 76 task_runner_->FastForwardBy(base::TimeDelta::FromHours(12345)); | 
| 77 | 77 | 
| 78 RecreateService(); | 78 // Set the minimum refresh interval to 0 by default, so tests don't have to | 
| 79 // worry about it. The tests that care set it explicitly. | |
| 80 RecreateServiceWithZeroRefreshInterval(); | |
| 79 } | 81 } | 
| 80 | 82 | 
| 81 void DestroyService() { service_ = nullptr; } | 83 void DestroyService() { service_ = nullptr; } | 
| 82 | 84 | 
| 83 void RecreateService() { | 85 void RecreateServiceWithZeroRefreshInterval() { | 
| 86 RecreateService(/*min_refresh_interval=*/base::TimeDelta()); | |
| 87 } | |
| 88 | |
| 89 void RecreateService(base::Optional<base::TimeDelta> refresh_interval) { | |
| 84 auto expiry_timer = base::MakeUnique<base::OneShotTimer>(tick_clock_.get()); | 90 auto expiry_timer = base::MakeUnique<base::OneShotTimer>(tick_clock_.get()); | 
| 85 expiry_timer->SetTaskRunner(task_runner_); | 91 expiry_timer->SetTaskRunner(task_runner_); | 
| 86 expiry_timer_ = expiry_timer.get(); | 92 expiry_timer_ = expiry_timer.get(); | 
| 87 | 93 | 
| 88 auto fetcher = base::MakeUnique<FakeDoodleFetcher>(); | 94 auto fetcher = base::MakeUnique<FakeDoodleFetcher>(); | 
| 89 fetcher_ = fetcher.get(); | 95 fetcher_ = fetcher.get(); | 
| 90 | 96 | 
| 91 service_ = base::MakeUnique<DoodleService>( | 97 service_ = base::MakeUnique<DoodleService>( | 
| 92 &pref_service_, std::move(fetcher), std::move(expiry_timer), | 98 &pref_service_, std::move(fetcher), std::move(expiry_timer), | 
| 93 task_runner_->GetMockClock(), task_runner_->GetMockTickClock()); | 99 task_runner_->GetMockClock(), task_runner_->GetMockTickClock(), | 
| 100 refresh_interval); | |
| 94 } | 101 } | 
| 95 | 102 | 
| 96 DoodleService* service() { return service_.get(); } | 103 DoodleService* service() { return service_.get(); } | 
| 97 FakeDoodleFetcher* fetcher() { return fetcher_; } | 104 FakeDoodleFetcher* fetcher() { return fetcher_; } | 
| 98 | 105 | 
| 99 base::TestMockTimeTaskRunner* task_runner() { return task_runner_.get(); } | 106 base::TestMockTimeTaskRunner* task_runner() { return task_runner_.get(); } | 
| 100 | 107 | 
| 101 private: | 108 private: | 
| 102 TestingPrefServiceSimple pref_service_; | 109 TestingPrefServiceSimple pref_service_; | 
| 103 | 110 | 
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 147 // Load some doodle config. | 154 // Load some doodle config. | 
| 148 service()->Refresh(); | 155 service()->Refresh(); | 
| 149 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | 156 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | 
| 150 config.large_image.url = GURL("https://doodle.com/doodle.jpg"); | 157 config.large_image.url = GURL("https://doodle.com/doodle.jpg"); | 
| 151 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | 158 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | 
| 152 base::TimeDelta::FromHours(1), config); | 159 base::TimeDelta::FromHours(1), config); | 
| 153 ASSERT_THAT(service()->config(), Eq(config)); | 160 ASSERT_THAT(service()->config(), Eq(config)); | 
| 154 | 161 | 
| 155 // Re-create the service. It should have persisted the config, and load it | 162 // Re-create the service. It should have persisted the config, and load it | 
| 156 // again automatically. | 163 // again automatically. | 
| 157 RecreateService(); | 164 RecreateServiceWithZeroRefreshInterval(); | 
| 158 EXPECT_THAT(service()->config(), Eq(config)); | 165 EXPECT_THAT(service()->config(), Eq(config)); | 
| 159 } | 166 } | 
| 160 | 167 | 
| 161 TEST_F(DoodleServiceTest, PersistsExpiryDate) { | 168 TEST_F(DoodleServiceTest, PersistsExpiryDate) { | 
| 162 // Load some doodle config. | 169 // Load some doodle config. | 
| 163 service()->Refresh(); | 170 service()->Refresh(); | 
| 164 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | 171 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | 
| 165 config.large_image.url = GURL("https://doodle.com/doodle.jpg"); | 172 config.large_image.url = GURL("https://doodle.com/doodle.jpg"); | 
| 166 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | 173 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | 
| 167 base::TimeDelta::FromHours(1), config); | 174 base::TimeDelta::FromHours(1), config); | 
| 168 ASSERT_THAT(service()->config(), Eq(config)); | 175 ASSERT_THAT(service()->config(), Eq(config)); | 
| 169 | 176 | 
| 170 // Destroy the service, and let some time pass. | 177 // Destroy the service, and let some time pass. | 
| 171 DestroyService(); | 178 DestroyService(); | 
| 172 task_runner()->FastForwardBy(base::TimeDelta::FromMinutes(15)); | 179 task_runner()->FastForwardBy(base::TimeDelta::FromMinutes(15)); | 
| 173 | 180 | 
| 174 // Remove the abandoned expiry task from the task runner. | 181 // Remove the abandoned expiry task from the task runner. | 
| 175 ASSERT_THAT(task_runner()->GetPendingTaskCount(), Eq(1u)); | 182 ASSERT_THAT(task_runner()->GetPendingTaskCount(), Eq(1u)); | 
| 176 task_runner()->ClearPendingTasks(); | 183 task_runner()->ClearPendingTasks(); | 
| 177 | 184 | 
| 178 // Re-create the service. The persisted config should have been loaded again. | 185 // Re-create the service. The persisted config should have been loaded again. | 
| 179 RecreateService(); | 186 RecreateServiceWithZeroRefreshInterval(); | 
| 180 EXPECT_THAT(service()->config(), Eq(config)); | 187 EXPECT_THAT(service()->config(), Eq(config)); | 
| 181 | 188 | 
| 182 // Its time-to-live should have been updated. | 189 // Its time-to-live should have been updated. | 
| 183 EXPECT_THAT(task_runner()->GetPendingTaskCount(), Eq(1u)); | 190 EXPECT_THAT(task_runner()->GetPendingTaskCount(), Eq(1u)); | 
| 184 EXPECT_THAT(task_runner()->NextPendingTaskDelay(), | 191 EXPECT_THAT(task_runner()->NextPendingTaskDelay(), | 
| 185 Eq(base::TimeDelta::FromMinutes(45))); | 192 Eq(base::TimeDelta::FromMinutes(45))); | 
| 186 } | 193 } | 
| 187 | 194 | 
| 188 TEST_F(DoodleServiceTest, PersistedConfigExpires) { | 195 TEST_F(DoodleServiceTest, PersistedConfigExpires) { | 
| 189 // Load some doodle config. | 196 // Load some doodle config. | 
| 190 service()->Refresh(); | 197 service()->Refresh(); | 
| 191 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | 198 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | 
| 192 config.large_image.url = GURL("https://doodle.com/doodle.jpg"); | 199 config.large_image.url = GURL("https://doodle.com/doodle.jpg"); | 
| 193 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | 200 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | 
| 194 base::TimeDelta::FromHours(1), config); | 201 base::TimeDelta::FromHours(1), config); | 
| 195 ASSERT_THAT(service()->config(), Eq(config)); | 202 ASSERT_THAT(service()->config(), Eq(config)); | 
| 196 | 203 | 
| 197 // Destroy the service, and let enough time pass so that the config expires. | 204 // Destroy the service, and let enough time pass so that the config expires. | 
| 198 DestroyService(); | 205 DestroyService(); | 
| 199 task_runner()->FastForwardBy(base::TimeDelta::FromHours(1)); | 206 task_runner()->FastForwardBy(base::TimeDelta::FromHours(1)); | 
| 200 | 207 | 
| 201 // Re-create the service. The persisted config should have been discarded | 208 // Re-create the service. The persisted config should have been discarded | 
| 202 // because it is expired. | 209 // because it is expired. | 
| 203 RecreateService(); | 210 RecreateServiceWithZeroRefreshInterval(); | 
| 204 EXPECT_THAT(service()->config(), Eq(base::nullopt)); | 211 EXPECT_THAT(service()->config(), Eq(base::nullopt)); | 
| 205 } | 212 } | 
| 206 | 213 | 
| 214 TEST_F(DoodleServiceTest, RespectsMinRefreshInterval) { | |
| 215 // Create a service with the default refresh interval. | |
| 216 RecreateService(/*min_refresh_interval=*/base::nullopt); | |
| 217 | |
| 218 // Load some doodle config. | |
| 219 service()->Refresh(); | |
| 220 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | |
| 221 config.large_image.url = GURL("https://doodle.com/doodle.jpg"); | |
| 222 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | |
| 223 base::TimeDelta::FromHours(1), config); | |
| 224 ASSERT_THAT(service()->config(), Eq(config)); | |
| 225 | |
| 226 // Let some time pass (less than the refresh interval). | |
| 227 task_runner()->FastForwardBy(base::TimeDelta::FromMinutes(10)); | |
| 228 | |
| 229 // Request a refresh. This should get ignored. | |
| 230 service()->Refresh(); | |
| 231 EXPECT_THAT(fetcher()->num_pending_callbacks(), Eq(0u)); | |
| 232 | |
| 233 // Let more time pass, so we get past the refresh interval. | |
| 234 task_runner()->FastForwardBy(base::TimeDelta::FromMinutes(6)); | |
| 235 | |
| 236 // Now the refresh request should be honored again. | |
| 237 service()->Refresh(); | |
| 238 EXPECT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); | |
| 239 } | |
| 240 | |
| 207 TEST_F(DoodleServiceTest, CallsObserverOnConfigReceived) { | 241 TEST_F(DoodleServiceTest, CallsObserverOnConfigReceived) { | 
| 208 StrictMock<MockDoodleObserver> observer; | 242 StrictMock<MockDoodleObserver> observer; | 
| 209 service()->AddObserver(&observer); | 243 service()->AddObserver(&observer); | 
| 210 | 244 | 
| 211 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 245 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 
| 212 | 246 | 
| 213 // Request a refresh of the doodle config. | 247 // Request a refresh of the doodle config. | 
| 214 service()->Refresh(); | 248 service()->Refresh(); | 
| 215 // The request should have arrived at the fetcher. | 249 // The request should have arrived at the fetcher. | 
| 216 ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); | 250 ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); | 
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 421 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 455 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 
| 422 | 456 | 
| 423 // The outcome should have been recorded, but not the time - we only record | 457 // The outcome should have been recorded, but not the time - we only record | 
| 424 // that for successful downloads. | 458 // that for successful downloads. | 
| 425 histograms.ExpectUniqueSample("Doodle.ConfigDownloadOutcome", | 459 histograms.ExpectUniqueSample("Doodle.ConfigDownloadOutcome", | 
| 426 5, // OUTCOME_DOWNLOAD_ERROR | 460 5, // OUTCOME_DOWNLOAD_ERROR | 
| 427 1); | 461 1); | 
| 428 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); | 462 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); | 
| 429 } | 463 } | 
| 430 | 464 | 
| 465 TEST_F(DoodleServiceTest, RecordsMetricsForEarlyRefreshRequest) { | |
| 466 // Create a service with the default refresh interval. | |
| 467 RecreateService(/*min_refresh_interval=*/base::nullopt); | |
| 
fhorschig
2017/03/27 15:45:05
nit: Can you please set some arbitrary value here?
 
Marc Treib
2017/03/27 16:02:22
Done.
 | |
| 468 | |
| 469 // Load a doodle config as usual. | |
| 470 service()->Refresh(); | |
| 471 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | |
| 472 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | |
| 473 base::TimeDelta::FromHours(1), config); | |
| 474 ASSERT_THAT(service()->config(), Eq(config)); | |
| 475 | |
| 476 base::HistogramTester histograms; | |
| 477 | |
| 478 // Request a refresh before the min refresh interval has passed. | |
| 479 task_runner()->FastForwardBy(base::TimeDelta::FromMinutes(5)); | |
| 480 service()->Refresh(); | |
| 481 | |
| 482 // This should not have resulted in a request. | |
| 483 ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(0u)); | |
| 484 | |
| 485 // The outcome should still have been recorded, but not the time - we only | |
| 486 // record that for successful downloads. | |
| 487 histograms.ExpectUniqueSample("Doodle.ConfigDownloadOutcome", | |
| 488 7, // OUTCOME_REFRESH_INTERVAL_NOT_PASSED | |
| 489 1); | |
| 490 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); | |
| 491 } | |
| 492 | |
| 431 TEST_F(DoodleServiceTest, DoesNotRecordMetricsAtStartup) { | 493 TEST_F(DoodleServiceTest, DoesNotRecordMetricsAtStartup) { | 
| 432 // Creating the service should not emit any histogram samples. | 494 // Creating the service should not emit any histogram samples. | 
| 433 base::HistogramTester histograms; | 495 base::HistogramTester histograms; | 
| 434 RecreateService(); | 496 RecreateServiceWithZeroRefreshInterval(); | 
| 435 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 497 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 
| 436 histograms.ExpectTotalCount("Doodle.ConfigDownloadOutcome", 0); | 498 histograms.ExpectTotalCount("Doodle.ConfigDownloadOutcome", 0); | 
| 437 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); | 499 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); | 
| 438 } | 500 } | 
| 439 | 501 | 
| 440 TEST_F(DoodleServiceTest, DoesNotRecordMetricsAtStartupWithConfig) { | 502 TEST_F(DoodleServiceTest, DoesNotRecordMetricsAtStartupWithConfig) { | 
| 441 // Load a doodle config as usual. | 503 // Load a doodle config as usual. | 
| 442 service()->Refresh(); | 504 service()->Refresh(); | 
| 443 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | 505 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | 
| 444 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | 506 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | 
| 445 base::TimeDelta::FromHours(1), config); | 507 base::TimeDelta::FromHours(1), config); | 
| 446 ASSERT_THAT(service()->config(), Eq(config)); | 508 ASSERT_THAT(service()->config(), Eq(config)); | 
| 447 | 509 | 
| 448 // Recreating the service should not emit any histogram samples, even though | 510 // Recreating the service should not emit any histogram samples, even though | 
| 449 // a config gets loaded. | 511 // a config gets loaded. | 
| 450 base::HistogramTester histograms; | 512 base::HistogramTester histograms; | 
| 451 RecreateService(); | 513 RecreateServiceWithZeroRefreshInterval(); | 
| 452 ASSERT_THAT(service()->config(), Eq(config)); | 514 ASSERT_THAT(service()->config(), Eq(config)); | 
| 453 histograms.ExpectTotalCount("Doodle.ConfigDownloadOutcome", 0); | 515 histograms.ExpectTotalCount("Doodle.ConfigDownloadOutcome", 0); | 
| 454 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); | 516 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); | 
| 455 } | 517 } | 
| 456 | 518 | 
| 457 TEST_F(DoodleServiceTest, DoesNotRecordMetricsWhenConfigExpires) { | 519 TEST_F(DoodleServiceTest, DoesNotRecordMetricsWhenConfigExpires) { | 
| 458 // Load some doodle config. | 520 // Load some doodle config. | 
| 459 service()->Refresh(); | 521 service()->Refresh(); | 
| 460 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | 522 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | 
| 461 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | 523 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | 
| 462 base::TimeDelta::FromHours(1), config); | 524 base::TimeDelta::FromHours(1), config); | 
| 463 ASSERT_THAT(service()->config(), Eq(config)); | 525 ASSERT_THAT(service()->config(), Eq(config)); | 
| 464 | 526 | 
| 465 base::HistogramTester histograms; | 527 base::HistogramTester histograms; | 
| 466 | 528 | 
| 467 // Fast-forward time so that the config expires. | 529 // Fast-forward time so that the config expires. | 
| 468 task_runner()->FastForwardBy(base::TimeDelta::FromHours(1)); | 530 task_runner()->FastForwardBy(base::TimeDelta::FromHours(1)); | 
| 469 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 531 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 
| 470 | 532 | 
| 471 // This should not have resulted in any metrics being emitted. | 533 // This should not have resulted in any metrics being emitted. | 
| 472 histograms.ExpectTotalCount("Doodle.ConfigDownloadOutcome", 0); | 534 histograms.ExpectTotalCount("Doodle.ConfigDownloadOutcome", 0); | 
| 473 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); | 535 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); | 
| 474 } | 536 } | 
| 475 | 537 | 
| 476 } // namespace doodle | 538 } // namespace doodle | 
| OLD | NEW |