Chromium Code Reviews| OLD | NEW |
|---|---|
| 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/ntp_snippets_service.h" | 5 #include "components/ntp_snippets/ntp_snippets_service.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 #include <utility> | |
| 8 #include <vector> | 9 #include <vector> |
| 9 | 10 |
| 10 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 11 #include "base/files/file_path.h" | 12 #include "base/files/file_path.h" |
| 12 #include "base/files/scoped_temp_dir.h" | 13 #include "base/files/scoped_temp_dir.h" |
| 13 #include "base/json/json_reader.h" | 14 #include "base/json/json_reader.h" |
| 14 #include "base/macros.h" | 15 #include "base/macros.h" |
| 15 #include "base/memory/ptr_util.h" | 16 #include "base/memory/ptr_util.h" |
| 16 #include "base/message_loop/message_loop.h" | 17 #include "base/message_loop/message_loop.h" |
| 17 #include "base/run_loop.h" | 18 #include "base/run_loop.h" |
| 18 #include "base/strings/string_number_conversions.h" | 19 #include "base/strings/string_number_conversions.h" |
| 19 #include "base/strings/string_util.h" | 20 #include "base/strings/string_util.h" |
| 20 #include "base/strings/stringprintf.h" | 21 #include "base/strings/stringprintf.h" |
| 21 #include "base/test/histogram_tester.h" | 22 #include "base/test/histogram_tester.h" |
| 22 #include "base/threading/thread_task_runner_handle.h" | 23 #include "base/threading/thread_task_runner_handle.h" |
| 23 #include "base/time/time.h" | 24 #include "base/time/time.h" |
| 24 #include "components/image_fetcher/image_decoder.h" | 25 #include "components/image_fetcher/image_decoder.h" |
| 25 #include "components/image_fetcher/image_fetcher.h" | 26 #include "components/image_fetcher/image_fetcher.h" |
| 27 #include "components/image_fetcher/image_fetcher_delegate.h" | |
| 26 #include "components/ntp_snippets/category_factory.h" | 28 #include "components/ntp_snippets/category_factory.h" |
| 27 #include "components/ntp_snippets/ntp_snippet.h" | 29 #include "components/ntp_snippets/ntp_snippet.h" |
| 28 #include "components/ntp_snippets/ntp_snippets_database.h" | 30 #include "components/ntp_snippets/ntp_snippets_database.h" |
| 29 #include "components/ntp_snippets/ntp_snippets_fetcher.h" | 31 #include "components/ntp_snippets/ntp_snippets_fetcher.h" |
| 30 #include "components/ntp_snippets/ntp_snippets_scheduler.h" | 32 #include "components/ntp_snippets/ntp_snippets_scheduler.h" |
| 31 #include "components/ntp_snippets/ntp_snippets_test_utils.h" | 33 #include "components/ntp_snippets/ntp_snippets_test_utils.h" |
| 32 #include "components/ntp_snippets/switches.h" | 34 #include "components/ntp_snippets/switches.h" |
| 33 #include "components/prefs/testing_pref_service.h" | 35 #include "components/prefs/testing_pref_service.h" |
| 34 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" | 36 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" |
| 35 #include "components/signin/core/browser/fake_signin_manager.h" | 37 #include "components/signin/core/browser/fake_signin_manager.h" |
| 36 #include "google_apis/google_api_keys.h" | 38 #include "google_apis/google_api_keys.h" |
| 37 #include "net/url_request/test_url_fetcher_factory.h" | 39 #include "net/url_request/test_url_fetcher_factory.h" |
| 38 #include "net/url_request/url_request_test_util.h" | 40 #include "net/url_request/url_request_test_util.h" |
| 39 #include "testing/gmock/include/gmock/gmock.h" | 41 #include "testing/gmock/include/gmock/gmock.h" |
| 40 #include "testing/gtest/include/gtest/gtest.h" | 42 #include "testing/gtest/include/gtest/gtest.h" |
| 43 #include "ui/gfx/image/image.h" | |
| 41 | 44 |
| 45 using image_fetcher::ImageFetcher; | |
| 46 using image_fetcher::ImageFetcherDelegate; | |
| 42 using testing::ElementsAre; | 47 using testing::ElementsAre; |
| 43 using testing::Eq; | 48 using testing::Eq; |
| 44 using testing::Invoke; | 49 using testing::Invoke; |
| 45 using testing::IsEmpty; | 50 using testing::IsEmpty; |
| 46 using testing::Mock; | 51 using testing::Mock; |
| 47 using testing::Return; | 52 using testing::Return; |
| 48 using testing::SizeIs; | 53 using testing::SizeIs; |
| 49 using testing::StartsWith; | 54 using testing::StartsWith; |
| 50 using testing::_; | 55 using testing::_; |
| 51 | 56 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 62 "https://chromereader-pa.googleapis.com/v1/fetch?key=%s"; | 67 "https://chromereader-pa.googleapis.com/v1/fetch?key=%s"; |
| 63 | 68 |
| 64 const char kSnippetUrl[] = "http://localhost/foobar"; | 69 const char kSnippetUrl[] = "http://localhost/foobar"; |
| 65 const char kSnippetTitle[] = "Title"; | 70 const char kSnippetTitle[] = "Title"; |
| 66 const char kSnippetText[] = "Snippet"; | 71 const char kSnippetText[] = "Snippet"; |
| 67 const char kSnippetSalientImage[] = "http://localhost/salient_image"; | 72 const char kSnippetSalientImage[] = "http://localhost/salient_image"; |
| 68 const char kSnippetPublisherName[] = "Foo News"; | 73 const char kSnippetPublisherName[] = "Foo News"; |
| 69 const char kSnippetAmpUrl[] = "http://localhost/amp"; | 74 const char kSnippetAmpUrl[] = "http://localhost/amp"; |
| 70 const float kSnippetScore = 5.0; | 75 const float kSnippetScore = 5.0; |
| 71 | 76 |
| 77 const char kSnippetUrl2[] = "http://foo.com/bar"; | |
| 78 | |
| 72 base::Time GetDefaultCreationTime() { | 79 base::Time GetDefaultCreationTime() { |
| 73 base::Time out_time; | 80 base::Time out_time; |
| 74 EXPECT_TRUE(base::Time::FromUTCExploded(kDefaultCreationTime, &out_time)); | 81 EXPECT_TRUE(base::Time::FromUTCExploded(kDefaultCreationTime, &out_time)); |
| 75 return out_time; | 82 return out_time; |
| 76 } | 83 } |
| 77 | 84 |
| 78 base::Time GetDefaultExpirationTime() { | 85 base::Time GetDefaultExpirationTime() { |
| 79 return base::Time::Now() + base::TimeDelta::FromHours(1); | 86 return base::Time::Now() + base::TimeDelta::FromHours(1); |
| 80 } | 87 } |
| 81 | 88 |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 184 // fail to parse as snippets. | 191 // fail to parse as snippets. |
| 185 size_t pos = json_str.find("\"url\""); | 192 size_t pos = json_str.find("\"url\""); |
| 186 if (pos == std::string::npos) { | 193 if (pos == std::string::npos) { |
| 187 NOTREACHED(); | 194 NOTREACHED(); |
| 188 return std::string(); | 195 return std::string(); |
| 189 } | 196 } |
| 190 json_str[pos + 1] = 'x'; | 197 json_str[pos + 1] = 'x'; |
| 191 return json_str; | 198 return json_str; |
| 192 } | 199 } |
| 193 | 200 |
| 201 void ServeEmptyImage( | |
| 202 const std::string& id, | |
| 203 base::Callback<void(const std::string&, const gfx::Image&)> callback) { | |
| 204 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 205 FROM_HERE, base::Bind(callback, id, gfx::Image())); | |
| 206 } | |
| 207 | |
| 194 void ParseJson( | 208 void ParseJson( |
| 195 const std::string& json, | 209 const std::string& json, |
| 196 const ntp_snippets::NTPSnippetsFetcher::SuccessCallback& success_callback, | 210 const ntp_snippets::NTPSnippetsFetcher::SuccessCallback& success_callback, |
| 197 const ntp_snippets::NTPSnippetsFetcher::ErrorCallback& error_callback) { | 211 const ntp_snippets::NTPSnippetsFetcher::ErrorCallback& error_callback) { |
| 198 base::JSONReader json_reader; | 212 base::JSONReader json_reader; |
| 199 std::unique_ptr<base::Value> value = json_reader.ReadToValue(json); | 213 std::unique_ptr<base::Value> value = json_reader.ReadToValue(json); |
| 200 if (value) { | 214 if (value) { |
| 201 success_callback.Run(std::move(value)); | 215 success_callback.Run(std::move(value)); |
| 202 } else { | 216 } else { |
| 203 error_callback.Run(json_reader.GetErrorMessage()); | 217 error_callback.Run(json_reader.GetErrorMessage()); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 256 EXPECT_EQ(new_status, CategoryStatus::AVAILABLE_LOADING); | 270 EXPECT_EQ(new_status, CategoryStatus::AVAILABLE_LOADING); |
| 257 run_loop_.Quit(); | 271 run_loop_.Quit(); |
| 258 } | 272 } |
| 259 | 273 |
| 260 MockProviderObserver* observer_; | 274 MockProviderObserver* observer_; |
| 261 base::RunLoop run_loop_; | 275 base::RunLoop run_loop_; |
| 262 | 276 |
| 263 DISALLOW_COPY_AND_ASSIGN(WaitForDBLoad); | 277 DISALLOW_COPY_AND_ASSIGN(WaitForDBLoad); |
| 264 }; | 278 }; |
| 265 | 279 |
| 280 class MockImageFetcher : public ImageFetcher { | |
| 281 public: | |
| 282 MOCK_METHOD1(SetImageFetcherDelegate, void(ImageFetcherDelegate*)); | |
| 283 MOCK_METHOD1(SetDataUseServiceName, void(DataUseServiceName)); | |
| 284 MOCK_METHOD3( | |
| 285 StartOrQueueNetworkRequest, | |
| 286 void(const std::string&, | |
| 287 const GURL&, | |
| 288 base::Callback<void(const std::string&, const gfx::Image&)>)); | |
| 289 }; | |
| 290 | |
| 266 } // namespace | 291 } // namespace |
| 267 | 292 |
| 268 class NTPSnippetsServiceTest : public test::NTPSnippetsTestBase { | 293 class NTPSnippetsServiceTest : public test::NTPSnippetsTestBase { |
| 269 public: | 294 public: |
| 270 NTPSnippetsServiceTest() | 295 NTPSnippetsServiceTest() |
| 271 : fake_url_fetcher_factory_( | 296 : fake_url_fetcher_factory_( |
| 272 /*default_factory=*/&failing_url_fetcher_factory_), | 297 /*default_factory=*/&failing_url_fetcher_factory_), |
| 273 test_url_(base::StringPrintf(kTestContentSnippetsServerFormat, | 298 test_url_(base::StringPrintf(kTestContentSnippetsServerFormat, |
| 274 google_apis::GetAPIKey().c_str())) { | 299 google_apis::GetAPIKey().c_str())) { |
| 275 NTPSnippetsService::RegisterProfilePrefs(pref_service()->registry()); | 300 NTPSnippetsService::RegisterProfilePrefs(pref_service()->registry()); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 318 base::MakeUnique<NTPSnippetsFetcher>( | 343 base::MakeUnique<NTPSnippetsFetcher>( |
| 319 fake_signin_manager(), fake_token_service_.get(), | 344 fake_signin_manager(), fake_token_service_.get(), |
| 320 std::move(request_context_getter), pref_service(), | 345 std::move(request_context_getter), pref_service(), |
| 321 base::Bind(&ParseJson), | 346 base::Bind(&ParseJson), |
| 322 /*is_stable_channel=*/true); | 347 /*is_stable_channel=*/true); |
| 323 | 348 |
| 324 fake_signin_manager()->SignIn("foo@bar.com"); | 349 fake_signin_manager()->SignIn("foo@bar.com"); |
| 325 snippets_fetcher->SetPersonalizationForTesting( | 350 snippets_fetcher->SetPersonalizationForTesting( |
| 326 NTPSnippetsFetcher::Personalization::kNonPersonal); | 351 NTPSnippetsFetcher::Personalization::kNonPersonal); |
| 327 | 352 |
| 353 auto image_fetcher = | |
|
tschumann
2016/08/12 12:37:09
nit: no need to allocate the mock on the heap.
Sim
jkrcal
2016/08/16 12:31:43
I am a bit puzzled by this comment.
I need to wr
Marc Treib
2016/08/16 13:29:18
I think Tim just didn't notice that you have to pa
tschumann
2016/08/16 14:56:55
yes to both. Actually I realized this later and *t
| |
| 354 base::MakeUnique<testing::NiceMock<MockImageFetcher>>(); | |
| 355 image_fetcher_ = image_fetcher.get(); | |
| 356 | |
| 328 // Add an initial fetch response, as the service tries to fetch when there | 357 // Add an initial fetch response, as the service tries to fetch when there |
| 329 // is nothing in the DB. | 358 // is nothing in the DB. |
| 330 SetUpFetchResponse(GetTestJson({GetSnippet()})); | 359 SetUpFetchResponse(GetTestJson({GetSnippet()})); |
| 331 | 360 |
| 332 service_.reset(new NTPSnippetsService( | 361 service_.reset(new NTPSnippetsService( |
| 333 &observer_, &category_factory_, pref_service(), nullptr, "fr", | 362 &observer_, &category_factory_, pref_service(), nullptr, "fr", |
| 334 &scheduler_, std::move(snippets_fetcher), /*image_fetcher=*/nullptr, | 363 &scheduler_, std::move(snippets_fetcher), |
| 335 /*image_fetcher=*/nullptr, base::MakeUnique<NTPSnippetsDatabase>( | 364 std::move(image_fetcher), /*image_decoder=*/nullptr, |
| 336 database_dir_.path(), task_runner), | 365 base::MakeUnique<NTPSnippetsDatabase>(database_dir_.path(), |
| 366 task_runner), | |
| 337 base::MakeUnique<NTPSnippetsStatusService>(fake_signin_manager(), | 367 base::MakeUnique<NTPSnippetsStatusService>(fake_signin_manager(), |
| 338 pref_service()))); | 368 pref_service()))); |
| 339 | 369 |
| 340 WaitForDBLoad(&observer_, service_.get()); | 370 WaitForDBLoad(&observer_, service_.get()); |
| 341 } | 371 } |
| 342 | 372 |
| 343 std::string MakeUniqueID(const std::string& within_category_id) { | 373 std::string MakeUniqueID(const std::string& within_category_id) { |
| 344 return service()->MakeUniqueID(articles_category(), within_category_id); | 374 return service()->MakeUniqueID(articles_category(), within_category_id); |
| 345 } | 375 } |
| 346 | 376 |
| 347 Category articles_category() { | 377 Category articles_category() { |
| 348 return category_factory_.FromKnownCategory(KnownCategories::ARTICLES); | 378 return category_factory_.FromKnownCategory(KnownCategories::ARTICLES); |
| 349 } | 379 } |
| 350 | 380 |
| 351 protected: | 381 protected: |
| 352 const GURL& test_url() { return test_url_; } | 382 const GURL& test_url() { return test_url_; } |
| 353 NTPSnippetsService* service() { return service_.get(); } | 383 NTPSnippetsService* service() { return service_.get(); } |
| 354 MockProviderObserver& observer() { return observer_; } | 384 MockProviderObserver& observer() { return observer_; } |
| 355 MockScheduler& mock_scheduler() { return scheduler_; } | 385 MockScheduler& mock_scheduler() { return scheduler_; } |
| 386 testing::NiceMock<MockImageFetcher>* image_fetcher() { | |
| 387 return image_fetcher_; | |
| 388 } | |
| 356 | 389 |
| 357 // Provide the json to be returned by the fake fetcher. | 390 // Provide the json to be returned by the fake fetcher. |
| 358 void SetUpFetchResponse(const std::string& json) { | 391 void SetUpFetchResponse(const std::string& json) { |
| 359 fake_url_fetcher_factory_.SetFakeResponse(test_url_, json, net::HTTP_OK, | 392 fake_url_fetcher_factory_.SetFakeResponse(test_url_, json, net::HTTP_OK, |
| 360 net::URLRequestStatus::SUCCESS); | 393 net::URLRequestStatus::SUCCESS); |
| 361 } | 394 } |
| 362 | 395 |
| 363 void LoadFromJSONString(const std::string& json) { | 396 void LoadFromJSONString(const std::string& json) { |
| 364 SetUpFetchResponse(json); | 397 SetUpFetchResponse(json); |
| 365 service()->FetchSnippets(true); | 398 service()->FetchSnippets(true); |
| 366 base::RunLoop().RunUntilIdle(); | 399 base::RunLoop().RunUntilIdle(); |
| 367 } | 400 } |
| 368 | 401 |
| 369 private: | 402 private: |
| 370 base::MessageLoop message_loop_; | 403 base::MessageLoop message_loop_; |
| 371 FailingFakeURLFetcherFactory failing_url_fetcher_factory_; | 404 FailingFakeURLFetcherFactory failing_url_fetcher_factory_; |
| 372 // Instantiation of factory automatically sets itself as URLFetcher's factory. | 405 // Instantiation of factory automatically sets itself as URLFetcher's factory. |
| 373 net::FakeURLFetcherFactory fake_url_fetcher_factory_; | 406 net::FakeURLFetcherFactory fake_url_fetcher_factory_; |
| 374 const GURL test_url_; | 407 const GURL test_url_; |
| 375 std::unique_ptr<OAuth2TokenService> fake_token_service_; | 408 std::unique_ptr<OAuth2TokenService> fake_token_service_; |
| 376 MockScheduler scheduler_; | 409 MockScheduler scheduler_; |
| 377 MockProviderObserver observer_; | 410 MockProviderObserver observer_; |
| 378 CategoryFactory category_factory_; | 411 CategoryFactory category_factory_; |
| 412 testing::NiceMock<MockImageFetcher>* image_fetcher_; | |
| 379 // Last so that the dependencies are deleted after the service. | 413 // Last so that the dependencies are deleted after the service. |
| 380 std::unique_ptr<NTPSnippetsService> service_; | 414 std::unique_ptr<NTPSnippetsService> service_; |
| 381 | 415 |
| 382 base::ScopedTempDir database_dir_; | 416 base::ScopedTempDir database_dir_; |
| 383 | 417 |
| 384 DISALLOW_COPY_AND_ASSIGN(NTPSnippetsServiceTest); | 418 DISALLOW_COPY_AND_ASSIGN(NTPSnippetsServiceTest); |
| 385 }; | 419 }; |
| 386 | 420 |
| 387 TEST_F(NTPSnippetsServiceTest, ScheduleOnStart) { | 421 TEST_F(NTPSnippetsServiceTest, ScheduleOnStart) { |
| 388 // SetUp() checks that Schedule is called. | 422 // SetUp() checks that Schedule is called. |
| (...skipping 490 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 879 OnCategoryStatusChanged(_, _, CategoryStatus::AVAILABLE_LOADING)); | 913 OnCategoryStatusChanged(_, _, CategoryStatus::AVAILABLE_LOADING)); |
| 880 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)).Times(1); | 914 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)).Times(1); |
| 881 service()->OnDisabledReasonChanged(DisabledReason::NONE); | 915 service()->OnDisabledReasonChanged(DisabledReason::NONE); |
| 882 EXPECT_CALL(observer(), | 916 EXPECT_CALL(observer(), |
| 883 OnCategoryStatusChanged(_, _, CategoryStatus::AVAILABLE)); | 917 OnCategoryStatusChanged(_, _, CategoryStatus::AVAILABLE)); |
| 884 base::RunLoop().RunUntilIdle(); | 918 base::RunLoop().RunUntilIdle(); |
| 885 EXPECT_EQ(NTPSnippetsService::State::READY, service()->state_); | 919 EXPECT_EQ(NTPSnippetsService::State::READY, service()->state_); |
| 886 EXPECT_FALSE(service()->GetSnippetsForTesting().empty()); | 920 EXPECT_FALSE(service()->GetSnippetsForTesting().empty()); |
| 887 } | 921 } |
| 888 | 922 |
| 923 TEST_F(NTPSnippetsServiceTest, ImageReturnedWithTheSameId) { | |
| 924 LoadFromJSONString(GetTestJson({GetSnippet()})); | |
| 925 | |
| 926 EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _)) | |
| 927 .Times(1) | |
|
tschumann
2016/08/12 12:37:09
Times(1) is implicit, you can remove that line.
S
jkrcal
2016/08/16 12:31:43
Done.
| |
| 928 .WillOnce(testing::WithArgs<0, 2>(Invoke(ServeEmptyImage))); | |
| 929 testing::MockFunction<void(const std::string&, const gfx::Image&)> | |
| 930 on_image_fetched_mock_function; | |
|
tschumann
2016/08/12 12:37:09
nit: feel free to pick shorter names (the test is
jkrcal
2016/08/16 12:31:43
Done.
| |
| 931 EXPECT_CALL(on_image_fetched_mock_function, | |
| 932 Call(MakeUniqueID(kSnippetUrl), _)) | |
| 933 .Times(1); | |
| 934 | |
| 935 service()->FetchSuggestionImage( | |
| 936 MakeUniqueID(kSnippetUrl), | |
| 937 base::Bind(&testing::MockFunction<void(const std::string&, | |
| 938 const gfx::Image&)>::Call, | |
| 939 base::Unretained(&on_image_fetched_mock_function))); | |
| 940 base::RunLoop().RunUntilIdle(); | |
| 941 } | |
| 942 | |
| 943 TEST_F(NTPSnippetsServiceTest, EmptyImageReturnedForNonExistentId) { | |
| 944 gfx::Image image; | |
| 945 testing::MockFunction<void(const std::string&, const gfx::Image&)> | |
| 946 on_image_fetched_mock_function; | |
| 947 EXPECT_CALL(on_image_fetched_mock_function, | |
| 948 Call(MakeUniqueID(kSnippetUrl2), _)) | |
| 949 .Times(1) | |
| 950 .WillOnce(testing::SaveArg<1>(&image)); | |
| 951 | |
| 952 service()->FetchSuggestionImage( | |
| 953 MakeUniqueID(kSnippetUrl2), | |
| 954 base::Bind(&testing::MockFunction<void(const std::string&, | |
| 955 const gfx::Image&)>::Call, | |
| 956 base::Unretained(&on_image_fetched_mock_function))); | |
| 957 | |
| 958 base::RunLoop().RunUntilIdle(); | |
| 959 EXPECT_TRUE(image.IsEmpty()); | |
| 960 } | |
| 961 | |
| 889 } // namespace ntp_snippets | 962 } // namespace ntp_snippets |
| OLD | NEW |