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

Side by Side Diff: components/ntp_snippets/remote/ntp_snippets_service_unittest.cc

Issue 2386103009: NTPSnippetsService: Garbage collect orphaned images at startup. (Closed)
Patch Set: patch restored Created 4 years, 2 months 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
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 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/ntp_snippets/remote/ntp_snippets_service.h" 5 #include "components/ntp_snippets/remote/ntp_snippets_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 208 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 // fail to parse as snippets. 219 // fail to parse as snippets.
220 size_t pos = json_str.find("\"fullPageUrl\""); 220 size_t pos = json_str.find("\"fullPageUrl\"");
221 if (pos == std::string::npos) { 221 if (pos == std::string::npos) {
222 NOTREACHED(); 222 NOTREACHED();
223 return std::string(); 223 return std::string();
224 } 224 }
225 json_str[pos + 1] = 'x'; 225 json_str[pos + 1] = 'x';
226 return json_str; 226 return json_str;
227 } 227 }
228 228
229 using ServeImageCallback = base::Callback<void(
230 const std::string&,
231 base::Callback<void(const std::string&, const gfx::Image&)>)>;
232
229 void ServeOneByOneImage( 233 void ServeOneByOneImage(
234 image_fetcher::ImageFetcherDelegate* notify,
230 const std::string& id, 235 const std::string& id,
231 base::Callback<void(const std::string&, const gfx::Image&)> callback) { 236 base::Callback<void(const std::string&, const gfx::Image&)> callback) {
Marc Treib 2016/10/06 10:21:30 Add a using declaration for this callback type? Im
tschumann 2016/10/06 10:56:11 not sure. IMO, this should be exposed by the Image
Marc Treib 2016/10/06 11:37:20 Ah indeed, good point! If you want to do this, go
tschumann 2016/10/06 12:21:44 Added it to my TODO() list.
232 base::ThreadTaskRunnerHandle::Get()->PostTask( 237 base::ThreadTaskRunnerHandle::Get()->PostTask(
233 FROM_HERE, base::Bind(callback, id, gfx::test::CreateImage(1, 1))); 238 FROM_HERE, base::Bind(callback, id, gfx::test::CreateImage(1, 1)));
239 notify->OnImageDataFetched(id, "1-by-1-image-data");
240 }
241
242 void ServeEmptyImage(
243 const std::string& id,
244 base::Callback<void(const std::string&, const gfx::Image&)> callback) {
245 base::ThreadTaskRunnerHandle::Get()->PostTask(
246 FROM_HERE, base::Bind(callback, id, gfx::Image()));
234 } 247 }
235 248
236 void ParseJson( 249 void ParseJson(
237 const std::string& json, 250 const std::string& json,
238 const ntp_snippets::NTPSnippetsFetcher::SuccessCallback& success_callback, 251 const ntp_snippets::NTPSnippetsFetcher::SuccessCallback& success_callback,
239 const ntp_snippets::NTPSnippetsFetcher::ErrorCallback& error_callback) { 252 const ntp_snippets::NTPSnippetsFetcher::ErrorCallback& error_callback) {
240 base::JSONReader json_reader; 253 base::JSONReader json_reader;
241 std::unique_ptr<base::Value> value = json_reader.ReadToValue(json); 254 std::unique_ptr<base::Value> value = json_reader.ReadToValue(json);
242 if (value) { 255 if (value) {
243 success_callback.Run(std::move(value)); 256 success_callback.Run(std::move(value));
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 345
333 private: 346 private:
334 base::WaitableEvent loaded_; 347 base::WaitableEvent loaded_;
335 std::map<Category, CategoryStatus, Category::CompareByID> statuses_; 348 std::map<Category, CategoryStatus, Category::CompareByID> statuses_;
336 std::map<Category, std::vector<ContentSuggestion>, Category::CompareByID> 349 std::map<Category, std::vector<ContentSuggestion>, Category::CompareByID>
337 suggestions_; 350 suggestions_;
338 351
339 DISALLOW_COPY_AND_ASSIGN(FakeContentSuggestionsProviderObserver); 352 DISALLOW_COPY_AND_ASSIGN(FakeContentSuggestionsProviderObserver);
340 }; 353 };
341 354
355 class FakeImageDecoder : public image_fetcher::ImageDecoder {
356 public:
357 FakeImageDecoder() {}
358 ~FakeImageDecoder() override {}
Marc Treib 2016/10/06 10:21:30 nit: = default
tschumann 2016/10/06 10:56:11 Done.
359 void DecodeImage(
360 const std::string& image_data,
361 const image_fetcher::ImageDecodedCallback& callback) override {
362 callback.Run(decoded_image_);
363 }
364
365 void SetDecodedImage(const gfx::Image& image) { decoded_image_ = image; }
366
367 private:
368 gfx::Image decoded_image_;
369 };
370
342 } // namespace 371 } // namespace
343 372
344 class NTPSnippetsServiceTest : public ::testing::Test { 373 class NTPSnippetsServiceTest : public ::testing::Test {
345 public: 374 public:
346 NTPSnippetsServiceTest() 375 NTPSnippetsServiceTest()
347 : params_manager_(ntp_snippets::kStudyName, 376 : params_manager_(ntp_snippets::kStudyName,
348 {{"content_suggestions_backend", 377 {{"content_suggestions_backend",
349 kTestContentSuggestionsServerEndpoint}}), 378 kTestContentSuggestionsServerEndpoint}}),
350 fake_url_fetcher_factory_( 379 fake_url_fetcher_factory_(
351 /*default_factory=*/&failing_url_fetcher_factory_), 380 /*default_factory=*/&failing_url_fetcher_factory_),
352 test_url_(kTestContentSuggestionsServerWithAPIKey), 381 test_url_(kTestContentSuggestionsServerWithAPIKey),
353 user_classifier_(/*pref_service=*/nullptr), 382 user_classifier_(/*pref_service=*/nullptr),
354 image_fetcher_(nullptr) { 383 image_fetcher_(nullptr),
384 image_decoder_(nullptr) {
355 NTPSnippetsService::RegisterProfilePrefs(utils_.pref_service()->registry()); 385 NTPSnippetsService::RegisterProfilePrefs(utils_.pref_service()->registry());
356 RequestThrottler::RegisterProfilePrefs(utils_.pref_service()->registry()); 386 RequestThrottler::RegisterProfilePrefs(utils_.pref_service()->registry());
357 387
358 // Since no SuggestionsService is injected in tests, we need to force the 388 // Since no SuggestionsService is injected in tests, we need to force the
359 // service to fetch from all hosts. 389 // service to fetch from all hosts.
360 base::CommandLine::ForCurrentProcess()->AppendSwitch( 390 base::CommandLine::ForCurrentProcess()->AppendSwitch(
361 switches::kDontRestrict); 391 switches::kDontRestrict);
362 EXPECT_TRUE(database_dir_.CreateUniqueTempDir()); 392 EXPECT_TRUE(database_dir_.CreateUniqueTempDir());
363 } 393 }
364 394
(...skipping 23 matching lines...) Expand all
388 base::MakeUnique<NTPSnippetsFetcher>( 418 base::MakeUnique<NTPSnippetsFetcher>(
389 utils_.fake_signin_manager(), fake_token_service_.get(), 419 utils_.fake_signin_manager(), fake_token_service_.get(),
390 std::move(request_context_getter), utils_.pref_service(), 420 std::move(request_context_getter), utils_.pref_service(),
391 &category_factory_, base::Bind(&ParseJson), kAPIKey); 421 &category_factory_, base::Bind(&ParseJson), kAPIKey);
392 422
393 utils_.fake_signin_manager()->SignIn("foo@bar.com"); 423 utils_.fake_signin_manager()->SignIn("foo@bar.com");
394 snippets_fetcher->SetPersonalizationForTesting( 424 snippets_fetcher->SetPersonalizationForTesting(
395 NTPSnippetsFetcher::Personalization::kNonPersonal); 425 NTPSnippetsFetcher::Personalization::kNonPersonal);
396 426
397 auto image_fetcher = base::MakeUnique<NiceMock<MockImageFetcher>>(); 427 auto image_fetcher = base::MakeUnique<NiceMock<MockImageFetcher>>();
428
398 image_fetcher_ = image_fetcher.get(); 429 image_fetcher_ = image_fetcher.get();
430 EXPECT_CALL(*image_fetcher, SetImageFetcherDelegate(_));
431 auto image_decoder = base::MakeUnique<FakeImageDecoder>();
432 image_decoder_ = image_decoder.get();
399 EXPECT_FALSE(observer_); 433 EXPECT_FALSE(observer_);
400 observer_ = base::MakeUnique<FakeContentSuggestionsProviderObserver>(); 434 observer_ = base::MakeUnique<FakeContentSuggestionsProviderObserver>();
401 return base::MakeUnique<NTPSnippetsService>( 435 return base::MakeUnique<NTPSnippetsService>(
402 observer_.get(), &category_factory_, utils_.pref_service(), nullptr, 436 observer_.get(), &category_factory_, utils_.pref_service(), nullptr,
403 "fr", &user_classifier_, &scheduler_, std::move(snippets_fetcher), 437 "fr", &user_classifier_, &scheduler_, std::move(snippets_fetcher),
404 std::move(image_fetcher), /*image_decoder=*/nullptr, 438 std::move(image_fetcher), std::move(image_decoder),
405 base::MakeUnique<NTPSnippetsDatabase>(database_dir_.GetPath(), 439 base::MakeUnique<NTPSnippetsDatabase>(database_dir_.GetPath(),
406 task_runner), 440 task_runner),
407 base::MakeUnique<NTPSnippetsStatusService>(utils_.fake_signin_manager(), 441 base::MakeUnique<NTPSnippetsStatusService>(utils_.fake_signin_manager(),
408 utils_.pref_service())); 442 utils_.pref_service()));
409 } 443 }
410 444
411 void WaitForSnippetsServiceInitialization(bool set_empty_response = true) { 445 void WaitForSnippetsServiceInitialization(bool set_empty_response = true) {
412 EXPECT_TRUE(observer_); 446 EXPECT_TRUE(observer_);
413 EXPECT_FALSE(observer_->Loaded()); 447 EXPECT_FALSE(observer_->Loaded());
414 448
(...skipping 24 matching lines...) Expand all
439 return ContentSuggestion::ID(other_category(), id_within_category); 473 return ContentSuggestion::ID(other_category(), id_within_category);
440 } 474 }
441 475
442 Category other_category() { return category_factory_.FromRemoteCategory(2); } 476 Category other_category() { return category_factory_.FromRemoteCategory(2); }
443 477
444 protected: 478 protected:
445 const GURL& test_url() { return test_url_; } 479 const GURL& test_url() { return test_url_; }
446 FakeContentSuggestionsProviderObserver& observer() { return *observer_; } 480 FakeContentSuggestionsProviderObserver& observer() { return *observer_; }
447 MockScheduler& mock_scheduler() { return scheduler_; } 481 MockScheduler& mock_scheduler() { return scheduler_; }
448 NiceMock<MockImageFetcher>* image_fetcher() { return image_fetcher_; } 482 NiceMock<MockImageFetcher>* image_fetcher() { return image_fetcher_; }
483 FakeImageDecoder* image_decoder() { return image_decoder_; }
449 484
450 // Provide the json to be returned by the fake fetcher. 485 // Provide the json to be returned by the fake fetcher.
451 void SetUpFetchResponse(const std::string& json) { 486 void SetUpFetchResponse(const std::string& json) {
452 fake_url_fetcher_factory_.SetFakeResponse(test_url_, json, net::HTTP_OK, 487 fake_url_fetcher_factory_.SetFakeResponse(test_url_, json, net::HTTP_OK,
453 net::URLRequestStatus::SUCCESS); 488 net::URLRequestStatus::SUCCESS);
454 } 489 }
455 490
456 void LoadFromJSONString(NTPSnippetsService* service, 491 void LoadFromJSONString(NTPSnippetsService* service,
457 const std::string& json) { 492 const std::string& json) {
458 SetUpFetchResponse(json); 493 SetUpFetchResponse(json);
459 service->FetchSnippets(true); 494 service->FetchSnippets(true);
460 base::RunLoop().RunUntilIdle(); 495 base::RunLoop().RunUntilIdle();
461 } 496 }
462 497
463 private: 498 private:
464 variations::testing::VariationParamsManager params_manager_; 499 variations::testing::VariationParamsManager params_manager_;
465 test::NTPSnippetsTestUtils utils_; 500 test::NTPSnippetsTestUtils utils_;
466 base::MessageLoop message_loop_; 501 base::MessageLoop message_loop_;
467 FailingFakeURLFetcherFactory failing_url_fetcher_factory_; 502 FailingFakeURLFetcherFactory failing_url_fetcher_factory_;
468 // Instantiation of factory automatically sets itself as URLFetcher's factory. 503 // Instantiation of factory automatically sets itself as URLFetcher's factory.
469 net::FakeURLFetcherFactory fake_url_fetcher_factory_; 504 net::FakeURLFetcherFactory fake_url_fetcher_factory_;
470 const GURL test_url_; 505 const GURL test_url_;
471 std::unique_ptr<OAuth2TokenService> fake_token_service_; 506 std::unique_ptr<OAuth2TokenService> fake_token_service_;
472 UserClassifier user_classifier_; 507 UserClassifier user_classifier_;
473 NiceMock<MockScheduler> scheduler_; 508 NiceMock<MockScheduler> scheduler_;
474 std::unique_ptr<FakeContentSuggestionsProviderObserver> observer_; 509 std::unique_ptr<FakeContentSuggestionsProviderObserver> observer_;
475 CategoryFactory category_factory_; 510 CategoryFactory category_factory_;
476 NiceMock<MockImageFetcher>* image_fetcher_; 511 NiceMock<MockImageFetcher>* image_fetcher_;
512 FakeImageDecoder* image_decoder_;
477 513
478 base::ScopedTempDir database_dir_; 514 base::ScopedTempDir database_dir_;
479 515
480 DISALLOW_COPY_AND_ASSIGN(NTPSnippetsServiceTest); 516 DISALLOW_COPY_AND_ASSIGN(NTPSnippetsServiceTest);
481 }; 517 };
482 518
483 TEST_F(NTPSnippetsServiceTest, ScheduleOnStart) { 519 TEST_F(NTPSnippetsServiceTest, ScheduleOnStart) {
484 // We should get two |Schedule| calls: The first when initialization 520 // We should get two |Schedule| calls: The first when initialization
485 // completes, the second one after the automatic (since the service doesn't 521 // completes, the second one after the automatic (since the service doesn't
486 // have any data yet) fetch finishes. 522 // have any data yet) fetch finishes.
(...skipping 531 matching lines...) Expand 10 before | Expand all | Expand 10 after
1018 EXPECT_FALSE(service->GetSnippetsForTesting(articles_category()).empty()); 1054 EXPECT_FALSE(service->GetSnippetsForTesting(articles_category()).empty());
1019 } 1055 }
1020 1056
1021 TEST_F(NTPSnippetsServiceTest, ImageReturnedWithTheSameId) { 1057 TEST_F(NTPSnippetsServiceTest, ImageReturnedWithTheSameId) {
1022 auto service = MakeSnippetsService(); 1058 auto service = MakeSnippetsService();
1023 1059
1024 LoadFromJSONString(service.get(), GetTestJson({GetSnippet()})); 1060 LoadFromJSONString(service.get(), GetTestJson({GetSnippet()}));
1025 1061
1026 gfx::Image image; 1062 gfx::Image image;
1027 MockFunction<void(const gfx::Image&)> image_fetched; 1063 MockFunction<void(const gfx::Image&)> image_fetched;
1064 ServeImageCallback cb = base::Bind(&ServeOneByOneImage, service.get());
1028 { 1065 {
1029 InSequence s; 1066 InSequence s;
1030 EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _)) 1067 EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _))
1031 .WillOnce(WithArgs<0, 2>(Invoke(ServeOneByOneImage))); 1068 .WillOnce(WithArgs<0, 2>(Invoke(&cb, &ServeImageCallback::Run)));
1032 EXPECT_CALL(image_fetched, Call(_)).WillOnce(SaveArg<0>(&image)); 1069 EXPECT_CALL(image_fetched, Call(_)).WillOnce(SaveArg<0>(&image));
1033 } 1070 }
1034 1071
1035 service->FetchSuggestionImage( 1072 service->FetchSuggestionImage(
1036 MakeArticleID(kSnippetUrl), 1073 MakeArticleID(kSnippetUrl),
1037 base::Bind(&MockFunction<void(const gfx::Image&)>::Call, 1074 base::Bind(&MockFunction<void(const gfx::Image&)>::Call,
1038 base::Unretained(&image_fetched))); 1075 base::Unretained(&image_fetched)));
1039 base::RunLoop().RunUntilIdle(); 1076 base::RunLoop().RunUntilIdle();
1040 // Check that the image by ServeOneByOneImage is really served. 1077 // Check that the image by ServeOneByOneImage is really served.
1041 EXPECT_EQ(1, image.Width()); 1078 EXPECT_EQ(1, image.Width());
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
1095 EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1)); 1132 EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1));
1096 1133
1097 // Sign in to check a transition from signed out to signed in. 1134 // Sign in to check a transition from signed out to signed in.
1098 SetUpFetchResponse(GetTestJson({GetSnippetN(1), GetSnippetN(2)})); 1135 SetUpFetchResponse(GetTestJson({GetSnippetN(1), GetSnippetN(2)}));
1099 service->OnSnippetsStatusChanged(SnippetsStatus::ENABLED_AND_SIGNED_OUT, 1136 service->OnSnippetsStatusChanged(SnippetsStatus::ENABLED_AND_SIGNED_OUT,
1100 SnippetsStatus::ENABLED_AND_SIGNED_IN); 1137 SnippetsStatus::ENABLED_AND_SIGNED_IN);
1101 base::RunLoop().RunUntilIdle(); 1138 base::RunLoop().RunUntilIdle();
1102 EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(2)); 1139 EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(2));
1103 } 1140 }
1104 1141
1142 namespace {
1143
1144 gfx::Image FetchImage(NTPSnippetsService* service,
1145 const ContentSuggestion::ID& suggestion_id) {
1146 gfx::Image result;
1147 base::RunLoop run_loop;
1148 service->FetchSuggestionImage(suggestion_id,
1149 base::Bind(
1150 [](base::Closure signal, gfx::Image* output,
1151 const gfx::Image& loaded) {
1152 *output = loaded;
1153 signal.Run();
1154 LOG(INFO) << "signalling loop out";
Marc Treib 2016/10/06 10:21:30 nit: remove the LOG
tschumann 2016/10/06 10:56:11 Done.
1155 },
1156 run_loop.QuitClosure(), &result));
1157 run_loop.Run();
1158 return result;
1159 }
1160
1161 } // namespace
1162
1163 TEST_F(NTPSnippetsServiceTest, ShouldClearOrphanedImages) {
Marc Treib 2016/10/06 10:21:30 nit: ShouldClearOrphanedImagesOnRestart?
tschumann 2016/10/06 10:56:11 Done.
1164 auto service = MakeSnippetsService();
1165
1166 LoadFromJSONString(service.get(), GetTestJson({GetSnippet()}));
1167 ServeImageCallback cb = base::Bind(&ServeOneByOneImage, service.get());
1168
1169 EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _))
1170 .WillOnce(WithArgs<0, 2>(Invoke(&cb, &ServeImageCallback::Run)));
1171 image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
1172
1173 gfx::Image image = FetchImage(service.get(), MakeArticleID(kSnippetUrl));
1174 EXPECT_EQ(1, image.Width());
1175 EXPECT_FALSE(image.IsEmpty());
1176
1177 // Send new suggestion which don't include the snippet referencing the image.
1178 LoadFromJSONString(service.get(),
1179 GetTestJson({GetSnippetWithUrl(
1180 "http://something.com/pletely/unrelated")}));
1181 // The image should still be available until a restart happens.
1182 EXPECT_FALSE(FetchImage(service.get(), MakeArticleID(kSnippetUrl)).IsEmpty());
1183 ResetSnippetsService(&service);
1184 // After the restart, the image should be garbage collected.
1185 EXPECT_TRUE(FetchImage(service.get(), MakeArticleID(kSnippetUrl)).IsEmpty());
1186 }
1187
1105 } // namespace ntp_snippets 1188 } // namespace ntp_snippets
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698