Chromium Code Reviews| 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 <list> | |
| 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 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 151 void WrappedRun( | 153 void WrappedRun( |
| 152 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { | 154 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) { |
| 153 Run(&fetched_categories); | 155 Run(&fetched_categories); |
| 154 } | 156 } |
| 155 | 157 |
| 156 MOCK_METHOD1( | 158 MOCK_METHOD1( |
| 157 Run, | 159 Run, |
| 158 void(NTPSnippetsFetcher::OptionalFetchedCategories* fetched_categories)); | 160 void(NTPSnippetsFetcher::OptionalFetchedCategories* fetched_categories)); |
| 159 }; | 161 }; |
| 160 | 162 |
| 163 // DelegateCallingTestURLFetcherFactory can be used to temporarily inject fake | |
| 164 // url fetcher instances into a scope. | |
|
Marc Treib
2016/12/09 12:59:24
s/fake url fetcher/TestURLFetcher/ ?
Since both Te
fhorschig
2016/12/12 09:54:03
Done.
| |
| 165 // Client code can access the last created fetcher to verify expected | |
| 166 // properties. When the factory gets destroyed, all available delegates of still | |
| 167 // valid fetchers will be called. | |
| 168 // This ensures once-bound callbacks (like SnippetsAvailableCallback) will be | |
| 169 // called at some point and are not leaked. | |
| 170 class DelegateCallingTestURLFetcherFactory | |
|
tschumann
2016/12/09 12:53:38
hmm... I wonder why the previous (much simpler ver
fhorschig
2016/12/12 09:54:03
Yes, you mentioned that. I replaced that multiple
tschumann
2016/12/12 10:00:42
to be more precise, what's the new bit of function
fhorschig
2016/12/12 11:05:10
It's true that we deal with a lot of implementatio
| |
| 171 : public net::TestURLFetcherFactory, | |
| 172 public net::TestURLFetcherDelegateForTests { | |
| 173 public: | |
| 174 DelegateCallingTestURLFetcherFactory() { | |
| 175 SetDelegateForTests(this); | |
| 176 set_remove_fetcher_on_delete(true); | |
| 177 } | |
| 178 | |
| 179 ~DelegateCallingTestURLFetcherFactory() override { | |
| 180 while (!fetcher_id_queue_.empty()) { | |
| 181 DropAndCallDelegate(fetcher_id_queue_.front()); | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 std::unique_ptr<net::URLFetcher> CreateURLFetcher( | |
| 186 int id, | |
| 187 const GURL& url, | |
| 188 net::URLFetcher::RequestType request_type, | |
| 189 net::URLFetcherDelegate* d) override { | |
| 190 if (GetFetcherByID(id)) { | |
| 191 LOG(ERROR) << "The ID " << id << " was already assigned to a fetcher." | |
|
Marc Treib
2016/12/09 12:59:24
Is this actually an error? If so, should we just f
fhorschig
2016/12/12 09:54:03
It's a warning now.
I thought about just failing
| |
| 192 << "Its delegate will thereforde be called right now."; | |
| 193 DropAndCallDelegate(id); | |
| 194 } | |
| 195 fetcher_id_queue_.push_back(id); | |
| 196 return TestURLFetcherFactory::CreateURLFetcher(id, url, request_type, d); | |
| 197 } | |
| 198 | |
| 199 // Returns the raw pointer of the last created URL fetcher. | |
| 200 // If it was destroyed or no fetcher was created, it will return a nulltpr. | |
| 201 net::TestURLFetcher* GetLastCreatedFetcher() { | |
| 202 if (fetcher_id_queue_.empty()) { | |
| 203 return nullptr; | |
| 204 } | |
| 205 return GetFetcherByID(fetcher_id_queue_.front()); | |
|
tschumann
2016/12/09 12:53:38
shouldn't this be back?
fhorschig
2016/12/12 09:54:03
What came in first should be handled first (--> qu
| |
| 206 } | |
| 207 | |
| 208 private: | |
| 209 std::list<int> fetcher_id_queue_; // Use a list as searching is essential. | |
|
tschumann
2016/12/09 12:53:38
drop the type in the variable name (it's outdated
Marc Treib
2016/12/09 12:59:24
You mean, this can't be std::queue because that do
fhorschig
2016/12/12 09:54:03
@tschumann: Done.
@treib: std::deque it is. And do
| |
| 210 | |
| 211 // The fetcher can either be destroyed because the delegate was called during | |
| 212 // execution or because we called it on destruction. | |
| 213 void DropAndCallDelegate(int fetcher_id) { | |
| 214 auto found_id_iter = std::find(fetcher_id_queue_.begin(), | |
| 215 fetcher_id_queue_.end(), fetcher_id); | |
| 216 if (found_id_iter == fetcher_id_queue_.end()) { | |
| 217 return; | |
| 218 } | |
| 219 fetcher_id_queue_.erase(found_id_iter); | |
| 220 net::TestURLFetcher* fetcher = GetFetcherByID(fetcher_id); | |
| 221 if (!fetcher) { | |
| 222 return; | |
| 223 } | |
| 224 if (!fetcher->delegate()) { | |
|
Marc Treib
2016/12/09 12:59:24
nit: Could merge the two ifs:
if (!fetcher || !fet
fhorschig
2016/12/12 09:54:03
Okay.
| |
| 225 return; | |
| 226 } | |
| 227 fetcher->delegate()->OnURLFetchComplete(fetcher); | |
| 228 } | |
| 229 | |
| 230 // net::TestURLFetcherDelegateForTests overrides: | |
| 231 void OnRequestStart(int fetcher_id) override {} | |
| 232 void OnChunkUpload(int fetcher_id) override {} | |
| 233 void OnRequestEnd(int fetcher_id) override { | |
| 234 DropAndCallDelegate(fetcher_id); | |
| 235 } | |
| 236 }; | |
| 237 | |
| 161 // Factory for FakeURLFetcher objects that always generate errors. | 238 // Factory for FakeURLFetcher objects that always generate errors. |
| 162 class FailingFakeURLFetcherFactory : public net::URLFetcherFactory { | 239 class FailingFakeURLFetcherFactory : public net::URLFetcherFactory { |
| 163 public: | 240 public: |
| 164 std::unique_ptr<net::URLFetcher> CreateURLFetcher( | 241 std::unique_ptr<net::URLFetcher> CreateURLFetcher( |
| 165 int id, const GURL& url, net::URLFetcher::RequestType request_type, | 242 int id, const GURL& url, net::URLFetcher::RequestType request_type, |
| 166 net::URLFetcherDelegate* d) override { | 243 net::URLFetcherDelegate* d) override { |
| 167 return base::MakeUnique<net::FakeURLFetcher>( | 244 return base::MakeUnique<net::FakeURLFetcher>( |
| 168 url, d, /*response_data=*/std::string(), net::HTTP_NOT_FOUND, | 245 url, d, /*response_data=*/std::string(), net::HTTP_NOT_FOUND, |
| 169 net::URLRequestStatus::FAILED); | 246 net::URLRequestStatus::FAILED); |
| 170 } | 247 } |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 202 class RequestBuilderWithMockedFlagsForTesting | 279 class RequestBuilderWithMockedFlagsForTesting |
| 203 : public NTPSnippetsFetcher::RequestBuilder { | 280 : public NTPSnippetsFetcher::RequestBuilder { |
| 204 public: | 281 public: |
| 205 RequestBuilderWithMockedFlagsForTesting() : RequestBuilder() {} | 282 RequestBuilderWithMockedFlagsForTesting() : RequestBuilder() {} |
| 206 | 283 |
| 207 private: | 284 private: |
| 208 bool IsSendingUserClassEnabled() const override { return true; } | 285 bool IsSendingUserClassEnabled() const override { return true; } |
| 209 bool IsSendingTopLanguagesEnabled() const override { return true; } | 286 bool IsSendingTopLanguagesEnabled() const override { return true; } |
| 210 }; | 287 }; |
| 211 | 288 |
| 289 struct ExpectationForVariationParam { | |
| 290 std::string param_value; | |
| 291 int expected_value; | |
| 292 }; | |
| 293 | |
| 212 NTPSnippetsFetcherTest() | 294 NTPSnippetsFetcherTest() |
| 213 : NTPSnippetsFetcherTest(GURL(kTestChromeReaderUrl), | 295 : NTPSnippetsFetcherTest(GURL(kTestChromeReaderUrl), |
| 214 std::map<std::string, std::string>()) {} | 296 std::map<std::string, std::string>()) {} |
| 215 | 297 |
| 216 NTPSnippetsFetcherTest(const GURL& gurl, | 298 NTPSnippetsFetcherTest(const GURL& gurl, |
| 217 const std::map<std::string, std::string>& params) | 299 const std::map<std::string, std::string>& params) |
| 218 : params_manager_( | 300 : params_manager_( |
| 219 base::MakeUnique<variations::testing::VariationParamsManager>( | 301 base::MakeUnique<variations::testing::VariationParamsManager>( |
| 220 ntp_snippets::kStudyName, | 302 ntp_snippets::kStudyName, |
| 221 params)), | 303 params)), |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 283 void SetFetchingPersonalizationVariation( | 365 void SetFetchingPersonalizationVariation( |
| 284 const std::string& personalization_string) { | 366 const std::string& personalization_string) { |
| 285 params_manager_.reset(); | 367 params_manager_.reset(); |
| 286 std::map<std::string, std::string> params = { | 368 std::map<std::string, std::string> params = { |
| 287 {"fetching_personalization", personalization_string}}; | 369 {"fetching_personalization", personalization_string}}; |
| 288 params_manager_ = | 370 params_manager_ = |
| 289 base::MakeUnique<variations::testing::VariationParamsManager>( | 371 base::MakeUnique<variations::testing::VariationParamsManager>( |
| 290 ntp_snippets::kStudyName, params); | 372 ntp_snippets::kStudyName, params); |
| 291 } | 373 } |
| 292 | 374 |
| 375 void SetVariationParametersForFeatures( | |
|
Marc Treib
2016/12/09 12:59:24
Hm, now SetFetchingPersonalizationVariation and Se
fhorschig
2016/12/12 09:54:03
The basic difference is that one is tied to a feat
| |
| 376 const std::map<std::string, std::string>& params, | |
| 377 const std::set<std::string>& features) { | |
| 378 params_manager_.reset(); | |
| 379 params_manager_ = | |
| 380 base::MakeUnique<variations::testing::VariationParamsManager>( | |
| 381 ntp_snippets::kStudyName, params, features); | |
| 382 } | |
| 383 | |
| 293 void SetFakeResponse(const std::string& response_data, | 384 void SetFakeResponse(const std::string& response_data, |
| 294 net::HttpStatusCode response_code, | 385 net::HttpStatusCode response_code, |
| 295 net::URLRequestStatus::Status status) { | 386 net::URLRequestStatus::Status status) { |
| 296 InitFakeURLFetcherFactory(); | 387 InitFakeURLFetcherFactory(); |
| 297 fake_url_fetcher_factory_->SetFakeResponse(test_url_, response_data, | 388 fake_url_fetcher_factory_->SetFakeResponse(test_url_, response_data, |
| 298 response_code, status); | 389 response_code, status); |
| 299 } | 390 } |
| 300 | 391 |
| 301 std::unique_ptr<translate::LanguageModel> MakeLanguageModel( | 392 std::unique_ptr<translate::LanguageModel> MakeLanguageModel( |
| 302 const std::vector<std::string>& codes) { | 393 const std::vector<std::string>& codes) { |
| (...skipping 597 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 900 EXPECT_THAT(snippets_fetcher().last_json(), Eq(kJsonStr)); | 991 EXPECT_THAT(snippets_fetcher().last_json(), Eq(kJsonStr)); |
| 901 EXPECT_THAT( | 992 EXPECT_THAT( |
| 902 histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"), | 993 histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"), |
| 903 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1))); | 994 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1))); |
| 904 EXPECT_THAT(histogram_tester().GetAllSamples( | 995 EXPECT_THAT(histogram_tester().GetAllSamples( |
| 905 "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"), | 996 "NewTabPage.Snippets.FetchHttpResponseOrErrorCode"), |
| 906 ElementsAre(base::Bucket(/*min=*/200, /*count=*/1))); | 997 ElementsAre(base::Bucket(/*min=*/200, /*count=*/1))); |
| 907 } | 998 } |
| 908 | 999 |
| 909 TEST_F(NTPSnippetsFetcherTest, ShouldRestrictToHosts) { | 1000 TEST_F(NTPSnippetsFetcherTest, ShouldRestrictToHosts) { |
| 910 net::TestURLFetcherFactory test_url_fetcher_factory; | 1001 DelegateCallingTestURLFetcherFactory fetcher_factory; |
| 911 NTPSnippetsFetcher::Params params = test_params(); | 1002 NTPSnippetsFetcher::Params params = test_params(); |
| 912 params.hosts = {"www.somehost1.com", "www.somehost2.com"}; | 1003 params.hosts = {"www.somehost1.com", "www.somehost2.com"}; |
| 913 params.count_to_fetch = 17; | 1004 params.count_to_fetch = 17; |
| 1005 | |
| 914 snippets_fetcher().FetchSnippets( | 1006 snippets_fetcher().FetchSnippets( |
| 915 params, ToSnippetsAvailableCallback(&mock_callback())); | 1007 params, ToSnippetsAvailableCallback(&mock_callback())); |
| 916 net::TestURLFetcher* fetcher = test_url_fetcher_factory.GetFetcherByID(0); | 1008 |
| 1009 net::TestURLFetcher* fetcher = fetcher_factory.GetLastCreatedFetcher(); | |
| 917 ASSERT_THAT(fetcher, NotNull()); | 1010 ASSERT_THAT(fetcher, NotNull()); |
| 918 std::unique_ptr<base::Value> value = | 1011 std::unique_ptr<base::Value> value = |
| 919 base::JSONReader::Read(fetcher->upload_data()); | 1012 base::JSONReader::Read(fetcher->upload_data()); |
| 920 ASSERT_TRUE(value) << " failed to parse JSON: " | 1013 ASSERT_TRUE(value) << " failed to parse JSON: " |
| 921 << PrintToString(fetcher->upload_data()); | 1014 << PrintToString(fetcher->upload_data()); |
| 922 const base::DictionaryValue* dict = nullptr; | 1015 const base::DictionaryValue* dict = nullptr; |
| 923 ASSERT_TRUE(value->GetAsDictionary(&dict)); | 1016 ASSERT_TRUE(value->GetAsDictionary(&dict)); |
| 924 const base::DictionaryValue* local_scoring_params = nullptr; | 1017 const base::DictionaryValue* local_scoring_params = nullptr; |
| 925 ASSERT_TRUE(dict->GetDictionary("advanced_options.local_scoring_params", | 1018 ASSERT_TRUE(dict->GetDictionary("advanced_options.local_scoring_params", |
| 926 &local_scoring_params)); | 1019 &local_scoring_params)); |
| 927 const base::ListValue* content_selectors = nullptr; | 1020 const base::ListValue* content_selectors = nullptr; |
| 928 ASSERT_TRUE( | 1021 ASSERT_TRUE( |
| 929 local_scoring_params->GetList("content_selectors", &content_selectors)); | 1022 local_scoring_params->GetList("content_selectors", &content_selectors)); |
| 930 ASSERT_THAT(content_selectors->GetSize(), Eq(static_cast<size_t>(2))); | 1023 ASSERT_THAT(content_selectors->GetSize(), Eq(static_cast<size_t>(2))); |
| 931 const base::DictionaryValue* content_selector = nullptr; | 1024 const base::DictionaryValue* content_selector = nullptr; |
| 932 ASSERT_TRUE(content_selectors->GetDictionary(0, &content_selector)); | 1025 ASSERT_TRUE(content_selectors->GetDictionary(0, &content_selector)); |
| 933 std::string content_selector_value; | 1026 std::string content_selector_value; |
| 934 EXPECT_TRUE(content_selector->GetString("value", &content_selector_value)); | 1027 EXPECT_TRUE(content_selector->GetString("value", &content_selector_value)); |
| 935 EXPECT_THAT(content_selector_value, Eq("www.somehost1.com")); | 1028 EXPECT_THAT(content_selector_value, Eq("www.somehost1.com")); |
| 936 ASSERT_TRUE(content_selectors->GetDictionary(1, &content_selector)); | 1029 ASSERT_TRUE(content_selectors->GetDictionary(1, &content_selector)); |
| 937 EXPECT_TRUE(content_selector->GetString("value", &content_selector_value)); | 1030 EXPECT_TRUE(content_selector->GetString("value", &content_selector_value)); |
| 938 EXPECT_THAT(content_selector_value, Eq("www.somehost2.com")); | 1031 EXPECT_THAT(content_selector_value, Eq("www.somehost2.com")); |
| 939 // Call the delegate callback manually as the TestURLFetcher deletes any | 1032 } |
| 940 // call to the delegate that usually happens on |Start|. | 1033 |
| 941 // Without the call to the delegate, it leaks the request that owns itself. | 1034 TEST_F(NTPSnippetsFetcherTest, RetryOnInteractiveRequests) { |
| 942 ASSERT_THAT(fetcher->delegate(), NotNull()); | 1035 DelegateCallingTestURLFetcherFactory fetcher_factory; |
| 943 EXPECT_CALL(mock_callback(), Run(/*snippets=*/Not(HasValue()))).Times(1); | 1036 NTPSnippetsFetcher::Params params = test_params(); |
| 944 // An 4XX response needs the least configuration to successfully invoke the | 1037 params.interactive_request = true; |
| 945 // callback properly as the results are not important in this test. | 1038 |
| 946 fetcher->set_response_code(net::HTTP_NOT_FOUND); | 1039 snippets_fetcher().FetchSnippets( |
| 947 fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED, -2)); | 1040 params, ToSnippetsAvailableCallback(&mock_callback())); |
| 948 fetcher->delegate()->OnURLFetchComplete(fetcher); | 1041 |
| 1042 net::TestURLFetcher* fetcher = fetcher_factory.GetLastCreatedFetcher(); | |
| 1043 ASSERT_THAT(fetcher, NotNull()); | |
| 1044 EXPECT_THAT(fetcher->GetMaxRetriesOn5xx(), Eq(2)); | |
| 1045 } | |
| 1046 | |
| 1047 TEST_F(NTPSnippetsFetcherTest, RetriesConfigurableOnNonInteractiveRequests) { | |
| 1048 NTPSnippetsFetcher::Params params = test_params(); | |
| 1049 params.interactive_request = false; | |
| 1050 const std::vector<ExpectationForVariationParam> retry_config_expectation = { | |
| 1051 {"", 0}, // Keep 2 retries if no variation is set. | |
| 1052 {"0", 0}, | |
| 1053 {"-1", 0}, // Allow no negative retry count. | |
| 1054 {"3", 3}}; | |
| 1055 | |
| 1056 for (const auto& retry_config : retry_config_expectation) { | |
| 1057 DelegateCallingTestURLFetcherFactory fetcher_factory; | |
| 1058 SetVariationParametersForFeatures( | |
| 1059 {{"background_5xx_retries_count", retry_config.param_value}}, | |
| 1060 {ntp_snippets::kArticleSuggestionsFeature.name}); | |
| 1061 | |
| 1062 snippets_fetcher().FetchSnippets( | |
| 1063 params, ToSnippetsAvailableCallback(&mock_callback())); | |
| 1064 | |
| 1065 net::TestURLFetcher* fetcher = fetcher_factory.GetLastCreatedFetcher(); | |
| 1066 ASSERT_THAT(fetcher, NotNull()); | |
| 1067 EXPECT_THAT(fetcher->GetMaxRetriesOn5xx(), Eq(retry_config.expected_value)); | |
| 1068 } | |
| 949 } | 1069 } |
| 950 | 1070 |
| 951 TEST_F(NTPSnippetsFetcherTest, ShouldReportUrlStatusError) { | 1071 TEST_F(NTPSnippetsFetcherTest, ShouldReportUrlStatusError) { |
| 952 SetFakeResponse(/*response_data=*/std::string(), net::HTTP_NOT_FOUND, | 1072 SetFakeResponse(/*response_data=*/std::string(), net::HTTP_NOT_FOUND, |
| 953 net::URLRequestStatus::FAILED); | 1073 net::URLRequestStatus::FAILED); |
| 954 EXPECT_CALL(mock_callback(), Run(/*snippets=*/Not(HasValue()))).Times(1); | 1074 EXPECT_CALL(mock_callback(), Run(/*snippets=*/Not(HasValue()))).Times(1); |
| 955 snippets_fetcher().FetchSnippets( | 1075 snippets_fetcher().FetchSnippets( |
| 956 test_params(), ToSnippetsAvailableCallback(&mock_callback())); | 1076 test_params(), ToSnippetsAvailableCallback(&mock_callback())); |
| 957 FastForwardUntilNoTasksRemain(); | 1077 FastForwardUntilNoTasksRemain(); |
| 958 EXPECT_THAT(snippets_fetcher().last_status(), | 1078 EXPECT_THAT(snippets_fetcher().last_status(), |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1088 const NTPSnippetsFetcher::OptionalFetchedCategories& fetched_categories) { | 1208 const NTPSnippetsFetcher::OptionalFetchedCategories& fetched_categories) { |
| 1089 if (fetched_categories) { | 1209 if (fetched_categories) { |
| 1090 // Matchers above aren't any more precise than this, so this is sufficient | 1210 // Matchers above aren't any more precise than this, so this is sufficient |
| 1091 // for test-failure diagnostics. | 1211 // for test-failure diagnostics. |
| 1092 return os << "list with " << fetched_categories->size() << " elements"; | 1212 return os << "list with " << fetched_categories->size() << " elements"; |
| 1093 } | 1213 } |
| 1094 return os << "null"; | 1214 return os << "null"; |
| 1095 } | 1215 } |
| 1096 | 1216 |
| 1097 } // namespace ntp_snippets | 1217 } // namespace ntp_snippets |
| OLD | NEW |