| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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_fetcher.h" | 5 #include "components/ntp_snippets/remote/ntp_snippets_fetcher.h" |
| 6 | 6 |
| 7 #include <deque> |
| 7 #include <map> | 8 #include <map> |
| 8 #include <utility> | 9 #include <utility> |
| 9 | 10 |
| 10 #include "base/json/json_reader.h" | 11 #include "base/json/json_reader.h" |
| 11 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
| 12 #include "base/strings/stringprintf.h" | 13 #include "base/strings/stringprintf.h" |
| 13 #include "base/test/histogram_tester.h" | 14 #include "base/test/histogram_tester.h" |
| 14 #include "base/test/test_mock_time_task_runner.h" | 15 #include "base/test/test_mock_time_task_runner.h" |
| 15 #include "base/threading/thread_task_runner_handle.h" | 16 #include "base/threading/thread_task_runner_handle.h" |
| 16 #include "base/time/time.h" | 17 #include "base/time/time.h" |
| 17 #include "base/values.h" | 18 #include "base/values.h" |
| 18 #include "components/ntp_snippets/category_factory.h" | 19 #include "components/ntp_snippets/category_factory.h" |
| 20 #include "components/ntp_snippets/features.h" |
| 19 #include "components/ntp_snippets/ntp_snippets_constants.h" | 21 #include "components/ntp_snippets/ntp_snippets_constants.h" |
| 20 #include "components/ntp_snippets/remote/ntp_snippet.h" | 22 #include "components/ntp_snippets/remote/ntp_snippet.h" |
| 21 #include "components/ntp_snippets/user_classifier.h" | 23 #include "components/ntp_snippets/user_classifier.h" |
| 22 #include "components/prefs/testing_pref_service.h" | 24 #include "components/prefs/testing_pref_service.h" |
| 23 #include "components/signin/core/browser/account_tracker_service.h" | 25 #include "components/signin/core/browser/account_tracker_service.h" |
| 24 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" | 26 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" |
| 25 #include "components/signin/core/browser/fake_signin_manager.h" | 27 #include "components/signin/core/browser/fake_signin_manager.h" |
| 26 #include "components/signin/core/browser/test_signin_client.h" | 28 #include "components/signin/core/browser/test_signin_client.h" |
| 27 #include "components/variations/entropy_provider.h" | 29 #include "components/variations/entropy_provider.h" |
| 28 #include "components/variations/variations_params_manager.h" | 30 #include "components/variations/variations_params_manager.h" |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 153 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { | 155 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { |
| 154 Run(fetch_result, &fetched_categories); | 156 Run(fetch_result, &fetched_categories); |
| 155 } | 157 } |
| 156 | 158 |
| 157 MOCK_METHOD2( | 159 MOCK_METHOD2( |
| 158 Run, | 160 Run, |
| 159 void(NTPSnippetsFetcher::FetchResult fetch_result, | 161 void(NTPSnippetsFetcher::FetchResult fetch_result, |
| 160 NTPSnippetsFetcher::OptionalFetchedCategories* fetched_categories)); | 162 NTPSnippetsFetcher::OptionalFetchedCategories* fetched_categories)); |
| 161 }; | 163 }; |
| 162 | 164 |
| 165 // TODO(fhorschig): Transfer this class' functionality to call delegates |
| 166 // automatically as option to TestURLFetcherFactory where it was just deleted. |
| 167 // This can be represented as a single member there and would reduce the amount |
| 168 // of fake implementations from three to two. |
| 169 |
| 170 // DelegateCallingTestURLFetcherFactory can be used to temporarily inject |
| 171 // TestURLFetcher instances into a scope. |
| 172 // Client code can access the last created fetcher to verify expected |
| 173 // properties. When the factory gets destroyed, all available delegates of still |
| 174 // valid fetchers will be called. |
| 175 // This ensures once-bound callbacks (like SnippetsAvailableCallback) will be |
| 176 // called at some point and are not leaked. |
| 177 class DelegateCallingTestURLFetcherFactory |
| 178 : public net::TestURLFetcherFactory, |
| 179 public net::TestURLFetcherDelegateForTests { |
| 180 public: |
| 181 DelegateCallingTestURLFetcherFactory() { |
| 182 SetDelegateForTests(this); |
| 183 set_remove_fetcher_on_delete(true); |
| 184 } |
| 185 |
| 186 ~DelegateCallingTestURLFetcherFactory() override { |
| 187 while (!fetchers_.empty()) { |
| 188 DropAndCallDelegate(fetchers_.front()); |
| 189 } |
| 190 } |
| 191 |
| 192 std::unique_ptr<net::URLFetcher> CreateURLFetcher( |
| 193 int id, |
| 194 const GURL& url, |
| 195 net::URLFetcher::RequestType request_type, |
| 196 net::URLFetcherDelegate* d) override { |
| 197 if (GetFetcherByID(id)) { |
| 198 LOG(WARNING) << "The ID " << id << " was already assigned to a fetcher." |
| 199 << "Its delegate will thereforde be called right now."; |
| 200 DropAndCallDelegate(id); |
| 201 } |
| 202 fetchers_.push_back(id); |
| 203 return TestURLFetcherFactory::CreateURLFetcher(id, url, request_type, d); |
| 204 } |
| 205 |
| 206 // Returns the raw pointer of the last created URL fetcher. |
| 207 // If it was destroyed or no fetcher was created, it will return a nulltpr. |
| 208 net::TestURLFetcher* GetLastCreatedFetcher() { |
| 209 if (fetchers_.empty()) { |
| 210 return nullptr; |
| 211 } |
| 212 return GetFetcherByID(fetchers_.front()); |
| 213 } |
| 214 |
| 215 private: |
| 216 // The fetcher can either be destroyed because the delegate was called during |
| 217 // execution or because we called it on destruction. |
| 218 void DropAndCallDelegate(int fetcher_id) { |
| 219 auto found_id_iter = |
| 220 std::find(fetchers_.begin(), fetchers_.end(), fetcher_id); |
| 221 if (found_id_iter == fetchers_.end()) { |
| 222 return; |
| 223 } |
| 224 fetchers_.erase(found_id_iter); |
| 225 net::TestURLFetcher* fetcher = GetFetcherByID(fetcher_id); |
| 226 if (!fetcher->delegate()) { |
| 227 return; |
| 228 } |
| 229 fetcher->delegate()->OnURLFetchComplete(fetcher); |
| 230 } |
| 231 |
| 232 // net::TestURLFetcherDelegateForTests overrides: |
| 233 void OnRequestStart(int fetcher_id) override {} |
| 234 void OnChunkUpload(int fetcher_id) override {} |
| 235 void OnRequestEnd(int fetcher_id) override { |
| 236 DropAndCallDelegate(fetcher_id); |
| 237 } |
| 238 |
| 239 std::deque<int> fetchers_; // std::queue doesn't support std::find. |
| 240 }; |
| 241 |
| 163 // Factory for FakeURLFetcher objects that always generate errors. | 242 // Factory for FakeURLFetcher objects that always generate errors. |
| 164 class FailingFakeURLFetcherFactory : public net::URLFetcherFactory { | 243 class FailingFakeURLFetcherFactory : public net::URLFetcherFactory { |
| 165 public: | 244 public: |
| 166 std::unique_ptr<net::URLFetcher> CreateURLFetcher( | 245 std::unique_ptr<net::URLFetcher> CreateURLFetcher( |
| 167 int id, const GURL& url, net::URLFetcher::RequestType request_type, | 246 int id, const GURL& url, net::URLFetcher::RequestType request_type, |
| 168 net::URLFetcherDelegate* d) override { | 247 net::URLFetcherDelegate* d) override { |
| 169 return base::MakeUnique<net::FakeURLFetcher>( | 248 return base::MakeUnique<net::FakeURLFetcher>( |
| 170 url, d, /*response_data=*/std::string(), net::HTTP_NOT_FOUND, | 249 url, d, /*response_data=*/std::string(), net::HTTP_NOT_FOUND, |
| 171 net::URLRequestStatus::FAILED); | 250 net::URLRequestStatus::FAILED); |
| 172 } | 251 } |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 285 void SetFetchingPersonalizationVariation( | 364 void SetFetchingPersonalizationVariation( |
| 286 const std::string& personalization_string) { | 365 const std::string& personalization_string) { |
| 287 params_manager_.reset(); | 366 params_manager_.reset(); |
| 288 std::map<std::string, std::string> params = { | 367 std::map<std::string, std::string> params = { |
| 289 {"fetching_personalization", personalization_string}}; | 368 {"fetching_personalization", personalization_string}}; |
| 290 params_manager_ = | 369 params_manager_ = |
| 291 base::MakeUnique<variations::testing::VariationParamsManager>( | 370 base::MakeUnique<variations::testing::VariationParamsManager>( |
| 292 ntp_snippets::kStudyName, params); | 371 ntp_snippets::kStudyName, params); |
| 293 } | 372 } |
| 294 | 373 |
| 374 void SetVariationParametersForFeatures( |
| 375 const std::map<std::string, std::string>& params, |
| 376 const std::set<std::string>& features) { |
| 377 params_manager_.reset(); |
| 378 params_manager_ = |
| 379 base::MakeUnique<variations::testing::VariationParamsManager>( |
| 380 ntp_snippets::kStudyName, params, features); |
| 381 } |
| 382 |
| 295 void SetFakeResponse(const std::string& response_data, | 383 void SetFakeResponse(const std::string& response_data, |
| 296 net::HttpStatusCode response_code, | 384 net::HttpStatusCode response_code, |
| 297 net::URLRequestStatus::Status status) { | 385 net::URLRequestStatus::Status status) { |
| 298 InitFakeURLFetcherFactory(); | 386 InitFakeURLFetcherFactory(); |
| 299 fake_url_fetcher_factory_->SetFakeResponse(test_url_, response_data, | 387 fake_url_fetcher_factory_->SetFakeResponse(test_url_, response_data, |
| 300 response_code, status); | 388 response_code, status); |
| 301 } | 389 } |
| 302 | 390 |
| 303 std::unique_ptr<translate::LanguageModel> MakeLanguageModel( | 391 std::unique_ptr<translate::LanguageModel> MakeLanguageModel( |
| 304 const std::vector<std::string>& codes) { | 392 const std::vector<std::string>& codes) { |
| (...skipping 600 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 905 EXPECT_THAT(snippets_fetcher().last_json(), Eq(kJsonStr)); | 993 EXPECT_THAT(snippets_fetcher().last_json(), Eq(kJsonStr)); |
| 906 EXPECT_THAT( | 994 EXPECT_THAT( |
| 907 histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"), | 995 histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"), |
| 908 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1))); | 996 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1))); |
| 909 EXPECT_THAT(histogram_tester().GetAllSamples( | 997 EXPECT_THAT(histogram_tester().GetAllSamples( |
| 910 "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"), | 998 "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"), |
| 911 ElementsAre(base::Bucket(/*min=*/200, /*count=*/1))); | 999 ElementsAre(base::Bucket(/*min=*/200, /*count=*/1))); |
| 912 } | 1000 } |
| 913 | 1001 |
| 914 TEST_F(NTPSnippetsFetcherTest, ShouldRestrictToHosts) { | 1002 TEST_F(NTPSnippetsFetcherTest, ShouldRestrictToHosts) { |
| 915 net::TestURLFetcherFactory test_url_fetcher_factory; | 1003 DelegateCallingTestURLFetcherFactory fetcher_factory; |
| 916 NTPSnippetsFetcher::Params params = test_params(); | 1004 NTPSnippetsFetcher::Params params = test_params(); |
| 917 params.hosts = {"www.somehost1.com", "www.somehost2.com"}; | 1005 params.hosts = {"www.somehost1.com", "www.somehost2.com"}; |
| 918 params.count_to_fetch = 17; | 1006 params.count_to_fetch = 17; |
| 1007 |
| 919 snippets_fetcher().FetchSnippets( | 1008 snippets_fetcher().FetchSnippets( |
| 920 params, ToSnippetsAvailableCallback(&mock_callback())); | 1009 params, ToSnippetsAvailableCallback(&mock_callback())); |
| 921 net::TestURLFetcher* fetcher = test_url_fetcher_factory.GetFetcherByID(0); | 1010 |
| 1011 net::TestURLFetcher* fetcher = fetcher_factory.GetLastCreatedFetcher(); |
| 922 ASSERT_THAT(fetcher, NotNull()); | 1012 ASSERT_THAT(fetcher, NotNull()); |
| 923 std::unique_ptr<base::Value> value = | 1013 std::unique_ptr<base::Value> value = |
| 924 base::JSONReader::Read(fetcher->upload_data()); | 1014 base::JSONReader::Read(fetcher->upload_data()); |
| 925 ASSERT_TRUE(value) << " failed to parse JSON: " | 1015 ASSERT_TRUE(value) << " failed to parse JSON: " |
| 926 << PrintToString(fetcher->upload_data()); | 1016 << PrintToString(fetcher->upload_data()); |
| 927 const base::DictionaryValue* dict = nullptr; | 1017 const base::DictionaryValue* dict = nullptr; |
| 928 ASSERT_TRUE(value->GetAsDictionary(&dict)); | 1018 ASSERT_TRUE(value->GetAsDictionary(&dict)); |
| 929 const base::DictionaryValue* local_scoring_params = nullptr; | 1019 const base::DictionaryValue* local_scoring_params = nullptr; |
| 930 ASSERT_TRUE(dict->GetDictionary("advanced_options.local_scoring_params", | 1020 ASSERT_TRUE(dict->GetDictionary("advanced_options.local_scoring_params", |
| 931 &local_scoring_params)); | 1021 &local_scoring_params)); |
| 932 const base::ListValue* content_selectors = nullptr; | 1022 const base::ListValue* content_selectors = nullptr; |
| 933 ASSERT_TRUE( | 1023 ASSERT_TRUE( |
| 934 local_scoring_params->GetList("content_selectors", &content_selectors)); | 1024 local_scoring_params->GetList("content_selectors", &content_selectors)); |
| 935 ASSERT_THAT(content_selectors->GetSize(), Eq(static_cast<size_t>(2))); | 1025 ASSERT_THAT(content_selectors->GetSize(), Eq(static_cast<size_t>(2))); |
| 936 const base::DictionaryValue* content_selector = nullptr; | 1026 const base::DictionaryValue* content_selector = nullptr; |
| 937 ASSERT_TRUE(content_selectors->GetDictionary(0, &content_selector)); | 1027 ASSERT_TRUE(content_selectors->GetDictionary(0, &content_selector)); |
| 938 std::string content_selector_value; | 1028 std::string content_selector_value; |
| 939 EXPECT_TRUE(content_selector->GetString("value", &content_selector_value)); | 1029 EXPECT_TRUE(content_selector->GetString("value", &content_selector_value)); |
| 940 EXPECT_THAT(content_selector_value, Eq("www.somehost1.com")); | 1030 EXPECT_THAT(content_selector_value, Eq("www.somehost1.com")); |
| 941 ASSERT_TRUE(content_selectors->GetDictionary(1, &content_selector)); | 1031 ASSERT_TRUE(content_selectors->GetDictionary(1, &content_selector)); |
| 942 EXPECT_TRUE(content_selector->GetString("value", &content_selector_value)); | 1032 EXPECT_TRUE(content_selector->GetString("value", &content_selector_value)); |
| 943 EXPECT_THAT(content_selector_value, Eq("www.somehost2.com")); | 1033 EXPECT_THAT(content_selector_value, Eq("www.somehost2.com")); |
| 944 // Call the delegate callback manually as the TestURLFetcher deletes any | 1034 } |
| 945 // call to the delegate that usually happens on |Start|. | 1035 |
| 946 // Without the call to the delegate, it leaks the request that owns itself. | 1036 TEST_F(NTPSnippetsFetcherTest, RetryOnInteractiveRequests) { |
| 947 ASSERT_THAT(fetcher->delegate(), NotNull()); | 1037 DelegateCallingTestURLFetcherFactory fetcher_factory; |
| 948 EXPECT_CALL(mock_callback(), | 1038 NTPSnippetsFetcher::Params params = test_params(); |
| 949 Run(NTPSnippetsFetcher::FetchResult::URL_REQUEST_STATUS_ERROR, | 1039 params.interactive_request = true; |
| 950 /*snippets=*/Not(HasValue()))) | 1040 |
| 951 .Times(1); | 1041 snippets_fetcher().FetchSnippets( |
| 952 // An 4XX response needs the least configuration to successfully invoke the | 1042 params, ToSnippetsAvailableCallback(&mock_callback())); |
| 953 // callback properly as the results are not important in this test. | 1043 |
| 954 fetcher->set_response_code(net::HTTP_NOT_FOUND); | 1044 net::TestURLFetcher* fetcher = fetcher_factory.GetLastCreatedFetcher(); |
| 955 fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED, -2)); | 1045 ASSERT_THAT(fetcher, NotNull()); |
| 956 fetcher->delegate()->OnURLFetchComplete(fetcher); | 1046 EXPECT_THAT(fetcher->GetMaxRetriesOn5xx(), Eq(2)); |
| 1047 } |
| 1048 |
| 1049 TEST_F(NTPSnippetsFetcherTest, RetriesConfigurableOnNonInteractiveRequests) { |
| 1050 struct ExpectationForVariationParam { |
| 1051 std::string param_value; |
| 1052 int expected_value; |
| 1053 std::string description; |
| 1054 }; |
| 1055 const std::vector<ExpectationForVariationParam> retry_config_expectation = { |
| 1056 {"", 0, "Do not retry by default"}, |
| 1057 {"0", 0, "Do not retry on param value 0"}, |
| 1058 {"-1", 0, "Do not retry on negative param values."}, |
| 1059 {"4", 4, "Retry as set in param value."}}; |
| 1060 |
| 1061 NTPSnippetsFetcher::Params params = test_params(); |
| 1062 params.interactive_request = false; |
| 1063 |
| 1064 for (const auto& retry_config : retry_config_expectation) { |
| 1065 DelegateCallingTestURLFetcherFactory fetcher_factory; |
| 1066 SetVariationParametersForFeatures( |
| 1067 {{"background_5xx_retries_count", retry_config.param_value}}, |
| 1068 {ntp_snippets::kArticleSuggestionsFeature.name}); |
| 1069 |
| 1070 snippets_fetcher().FetchSnippets( |
| 1071 params, ToSnippetsAvailableCallback(&mock_callback())); |
| 1072 |
| 1073 net::TestURLFetcher* fetcher = fetcher_factory.GetLastCreatedFetcher(); |
| 1074 ASSERT_THAT(fetcher, NotNull()); |
| 1075 EXPECT_THAT(fetcher->GetMaxRetriesOn5xx(), Eq(retry_config.expected_value)) |
| 1076 << retry_config.description; |
| 1077 } |
| 957 } | 1078 } |
| 958 | 1079 |
| 959 TEST_F(NTPSnippetsFetcherTest, ShouldReportUrlStatusError) { | 1080 TEST_F(NTPSnippetsFetcherTest, ShouldReportUrlStatusError) { |
| 960 SetFakeResponse(/*response_data=*/std::string(), net::HTTP_NOT_FOUND, | 1081 SetFakeResponse(/*response_data=*/std::string(), net::HTTP_NOT_FOUND, |
| 961 net::URLRequestStatus::FAILED); | 1082 net::URLRequestStatus::FAILED); |
| 962 EXPECT_CALL(mock_callback(), | 1083 EXPECT_CALL(mock_callback(), |
| 963 Run(NTPSnippetsFetcher::FetchResult::URL_REQUEST_STATUS_ERROR, | 1084 Run(NTPSnippetsFetcher::FetchResult::URL_REQUEST_STATUS_ERROR, |
| 964 /*snippets=*/Not(HasValue()))) | 1085 /*snippets=*/Not(HasValue()))) |
| 965 .Times(1); | 1086 .Times(1); |
| 966 snippets_fetcher().FetchSnippets( | 1087 snippets_fetcher().FetchSnippets( |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1116 const NTPSnippetsFetcher::OptionalFetchedCategories& fetched_categories) { | 1237 const NTPSnippetsFetcher::OptionalFetchedCategories& fetched_categories) { |
| 1117 if (fetched_categories) { | 1238 if (fetched_categories) { |
| 1118 // Matchers above aren't any more precise than this, so this is sufficient | 1239 // Matchers above aren't any more precise than this, so this is sufficient |
| 1119 // for test-failure diagnostics. | 1240 // for test-failure diagnostics. |
| 1120 return os << "list with " << fetched_categories->size() << " elements"; | 1241 return os << "list with " << fetched_categories->size() << " elements"; |
| 1121 } | 1242 } |
| 1122 return os << "null"; | 1243 return os << "null"; |
| 1123 } | 1244 } |
| 1124 | 1245 |
| 1125 } // namespace ntp_snippets | 1246 } // namespace ntp_snippets |
| OLD | NEW |