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 <utility> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
12 #include "base/files/file_path.h" | 12 #include "base/files/file_path.h" |
13 #include "base/files/scoped_temp_dir.h" | 13 #include "base/files/scoped_temp_dir.h" |
14 #include "base/json/json_reader.h" | 14 #include "base/json/json_reader.h" |
15 #include "base/macros.h" | 15 #include "base/macros.h" |
16 #include "base/memory/ptr_util.h" | 16 #include "base/memory/ptr_util.h" |
17 #include "base/message_loop/message_loop.h" | 17 #include "base/message_loop/message_loop.h" |
18 #include "base/run_loop.h" | 18 #include "base/run_loop.h" |
19 #include "base/strings/string_number_conversions.h" | 19 #include "base/strings/string_number_conversions.h" |
20 #include "base/strings/string_util.h" | 20 #include "base/strings/string_util.h" |
21 #include "base/strings/stringprintf.h" | 21 #include "base/strings/stringprintf.h" |
22 #include "base/test/histogram_tester.h" | 22 #include "base/test/histogram_tester.h" |
23 #include "base/threading/thread_task_runner_handle.h" | 23 #include "base/threading/thread_task_runner_handle.h" |
24 #include "base/time/time.h" | 24 #include "base/time/time.h" |
25 #include "components/image_fetcher/image_decoder.h" | 25 #include "components/image_fetcher/image_decoder.h" |
26 #include "components/image_fetcher/image_fetcher.h" | 26 #include "components/image_fetcher/image_fetcher.h" |
27 #include "components/image_fetcher/image_fetcher_delegate.h" | 27 #include "components/image_fetcher/image_fetcher_delegate.h" |
28 #include "components/ntp_snippets/category_factory.h" | 28 #include "components/ntp_snippets/category_factory.h" |
29 #include "components/ntp_snippets/mock_content_suggestions_provider_observer.h" | |
30 #include "components/ntp_snippets/ntp_snippet.h" | 29 #include "components/ntp_snippets/ntp_snippet.h" |
30 #include "components/ntp_snippets/ntp_snippets_constants.h" | |
31 #include "components/ntp_snippets/ntp_snippets_database.h" | 31 #include "components/ntp_snippets/ntp_snippets_database.h" |
32 #include "components/ntp_snippets/ntp_snippets_fetcher.h" | 32 #include "components/ntp_snippets/ntp_snippets_fetcher.h" |
33 #include "components/ntp_snippets/ntp_snippets_scheduler.h" | 33 #include "components/ntp_snippets/ntp_snippets_scheduler.h" |
34 #include "components/ntp_snippets/ntp_snippets_test_utils.h" | 34 #include "components/ntp_snippets/ntp_snippets_test_utils.h" |
35 #include "components/ntp_snippets/switches.h" | 35 #include "components/ntp_snippets/switches.h" |
36 #include "components/prefs/testing_pref_service.h" | 36 #include "components/prefs/testing_pref_service.h" |
37 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" | 37 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" |
38 #include "components/signin/core/browser/fake_signin_manager.h" | 38 #include "components/signin/core/browser/fake_signin_manager.h" |
39 #include "components/variations/variations_associated_data.h" | |
39 #include "google_apis/google_api_keys.h" | 40 #include "google_apis/google_api_keys.h" |
40 #include "net/url_request/test_url_fetcher_factory.h" | 41 #include "net/url_request/test_url_fetcher_factory.h" |
41 #include "net/url_request/url_request_test_util.h" | 42 #include "net/url_request/url_request_test_util.h" |
42 #include "testing/gmock/include/gmock/gmock.h" | 43 #include "testing/gmock/include/gmock/gmock.h" |
43 #include "testing/gtest/include/gtest/gtest.h" | 44 #include "testing/gtest/include/gtest/gtest.h" |
44 #include "ui/gfx/image/image.h" | 45 #include "ui/gfx/image/image.h" |
45 #include "ui/gfx/image/image_unittest_util.h" | 46 #include "ui/gfx/image/image_unittest_util.h" |
46 | 47 |
47 using image_fetcher::ImageFetcher; | 48 using image_fetcher::ImageFetcher; |
48 using image_fetcher::ImageFetcherDelegate; | 49 using image_fetcher::ImageFetcherDelegate; |
49 using testing::ElementsAre; | 50 using testing::ElementsAre; |
50 using testing::Eq; | 51 using testing::Eq; |
52 using testing::InSequence; | |
51 using testing::Invoke; | 53 using testing::Invoke; |
52 using testing::IsEmpty; | 54 using testing::IsEmpty; |
53 using testing::Mock; | 55 using testing::Mock; |
56 using testing::MockFunction; | |
57 using testing::NiceMock; | |
54 using testing::Return; | 58 using testing::Return; |
59 using testing::SaveArg; | |
55 using testing::SizeIs; | 60 using testing::SizeIs; |
56 using testing::StartsWith; | 61 using testing::StartsWith; |
62 using testing::StrictMock; | |
63 using testing::WithArgs; | |
57 using testing::_; | 64 using testing::_; |
58 | 65 |
59 namespace ntp_snippets { | 66 namespace ntp_snippets { |
60 | 67 |
61 namespace { | 68 namespace { |
62 | 69 |
63 MATCHER_P(IdEq, value, "") { | 70 MATCHER_P(IdEq, value, "") { |
64 return arg->id() == value; | 71 return arg->id() == value; |
65 } | 72 } |
66 | 73 |
67 const base::Time::Exploded kDefaultCreationTime = {2015, 11, 4, 25, 13, 46, 45}; | 74 const base::Time::Exploded kDefaultCreationTime = {2015, 11, 4, 25, 13, 46, 45}; |
68 const char kTestContentSnippetsServerFormat[] = | 75 const char kTestContentSuggestionsServerEndpoint[] = |
69 "https://chromereader-pa.googleapis.com/v1/fetch?key=%s"; | 76 "https://localunittest-chromecontentsuggestions-pa.googleapis.com/v1/" |
77 "suggestions/fetch"; | |
78 const char kTestContentSuggestionsServerFormat[] = | |
79 "https://localunittest-chromecontentsuggestions-pa.googleapis.com/v1/" | |
80 "suggestions/fetch?key=%s"; | |
70 | 81 |
71 const char kSnippetUrl[] = "http://localhost/foobar"; | 82 const char kSnippetUrl[] = "http://localhost/foobar"; |
72 const char kSnippetTitle[] = "Title"; | 83 const char kSnippetTitle[] = "Title"; |
73 const char kSnippetText[] = "Snippet"; | 84 const char kSnippetText[] = "Snippet"; |
74 const char kSnippetSalientImage[] = "http://localhost/salient_image"; | 85 const char kSnippetSalientImage[] = "http://localhost/salient_image"; |
75 const char kSnippetPublisherName[] = "Foo News"; | 86 const char kSnippetPublisherName[] = "Foo News"; |
76 const char kSnippetAmpUrl[] = "http://localhost/amp"; | 87 const char kSnippetAmpUrl[] = "http://localhost/amp"; |
77 const float kSnippetScore = 5.0; | |
78 | 88 |
79 const char kSnippetUrl2[] = "http://foo.com/bar"; | 89 const char kSnippetUrl2[] = "http://foo.com/bar"; |
80 | 90 |
81 base::Time GetDefaultCreationTime() { | 91 base::Time GetDefaultCreationTime() { |
82 base::Time out_time; | 92 base::Time out_time; |
83 EXPECT_TRUE(base::Time::FromUTCExploded(kDefaultCreationTime, &out_time)); | 93 EXPECT_TRUE(base::Time::FromUTCExploded(kDefaultCreationTime, &out_time)); |
84 return out_time; | 94 return out_time; |
85 } | 95 } |
86 | 96 |
87 base::Time GetDefaultExpirationTime() { | 97 base::Time GetDefaultExpirationTime() { |
88 return base::Time::Now() + base::TimeDelta::FromHours(1); | 98 return base::Time::Now() + base::TimeDelta::FromHours(1); |
89 } | 99 } |
90 | 100 |
91 std::string GetTestJson(const std::vector<std::string>& snippets) { | 101 std::string GetTestJson(const std::vector<std::string>& snippets) { |
92 return base::StringPrintf("{\"recos\":[%s]}", | 102 return base::StringPrintf( |
93 base::JoinString(snippets, ", ").c_str()); | 103 "{\n" |
104 " \"categories\": [{\n" | |
105 " \"id\": 1,\n" | |
106 " \"localizedTitle\": \"Articles for You\",\n" | |
107 " \"suggestions\": [%s]\n" | |
108 " }]\n" | |
109 "}\n", | |
110 base::JoinString(snippets, ", ").c_str()); | |
94 } | 111 } |
95 | 112 |
96 std::string GetSnippetWithUrlAndTimesAndSources( | 113 std::string FormatTime(const base::Time& t) { |
97 const std::string& url, | 114 base::Time::Exploded x; |
98 const std::string& content_creation_time_str, | 115 t.UTCExplode(&x); |
99 const std::string& expiry_time_str, | 116 return base::StringPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", x.year, x.month, |
100 const std::vector<std::string>& source_urls, | 117 x.day_of_month, x.hour, x.minute, x.second); |
101 const std::vector<std::string>& publishers, | |
102 const std::vector<std::string>& amp_urls) { | |
103 char json_str_format[] = | |
104 "{ \"contentInfo\": {" | |
105 "\"url\" : \"%s\"," | |
106 "\"title\" : \"%s\"," | |
107 "\"snippet\" : \"%s\"," | |
108 "\"thumbnailUrl\" : \"%s\"," | |
109 "\"creationTimestampSec\" : \"%s\"," | |
110 "\"expiryTimestampSec\" : \"%s\"," | |
111 "\"sourceCorpusInfo\" : [%s]" | |
112 "}, " | |
113 "\"score\" : %f}"; | |
114 | |
115 char source_corpus_info_format[] = | |
116 "{\"corpusId\": \"%s\"," | |
117 "\"publisherData\": {" | |
118 "\"sourceName\": \"%s\"" | |
119 "}," | |
120 "\"ampUrl\": \"%s\"}"; | |
121 | |
122 std::string source_corpus_info_list_str; | |
123 for (size_t i = 0; i < source_urls.size(); ++i) { | |
124 std::string source_corpus_info_str = | |
125 base::StringPrintf(source_corpus_info_format, | |
126 source_urls[i].empty() ? "" : source_urls[i].c_str(), | |
127 publishers[i].empty() ? "" : publishers[i].c_str(), | |
128 amp_urls[i].empty() ? "" : amp_urls[i].c_str()); | |
129 source_corpus_info_list_str.append(source_corpus_info_str); | |
130 source_corpus_info_list_str.append(","); | |
131 } | |
132 // Remove the last comma | |
133 source_corpus_info_list_str.erase(source_corpus_info_list_str.size()-1, 1); | |
134 return base::StringPrintf(json_str_format, url.c_str(), kSnippetTitle, | |
135 kSnippetText, kSnippetSalientImage, | |
136 content_creation_time_str.c_str(), | |
137 expiry_time_str.c_str(), | |
138 source_corpus_info_list_str.c_str(), kSnippetScore); | |
139 } | 118 } |
140 | 119 |
141 std::string GetSnippetWithSources(const std::vector<std::string>& source_urls, | 120 std::string GetSnippetWithUrlAndTimesAndSource( |
142 const std::vector<std::string>& publishers, | 121 const std::vector<std::string>& ids, |
143 const std::vector<std::string>& amp_urls) { | 122 const std::string& url, |
144 return GetSnippetWithUrlAndTimesAndSources( | 123 const base::Time& creation_time, |
145 kSnippetUrl, NTPSnippet::TimeToJsonString(GetDefaultCreationTime()), | 124 const base::Time& expiry_time, |
146 NTPSnippet::TimeToJsonString(GetDefaultExpirationTime()), source_urls, | 125 const std::string& publisher, |
147 publishers, amp_urls); | 126 const std::string& amp_url) { |
127 const std::string ids_string = base::JoinString(ids, "\",\n \""); | |
128 return base::StringPrintf( | |
129 "{\n" | |
130 " \"ids\": [\n" | |
131 " \"%s\"\n" | |
132 " ],\n" | |
133 " \"title\": \"%s\",\n" | |
134 " \"snippet\": \"%s\",\n" | |
135 " \"fullPageUrl\": \"%s\",\n" | |
136 " \"creationTime\": \"%s\",\n" | |
137 " \"expirationTime\": \"%s\",\n" | |
138 " \"attribution\": \"%s\",\n" | |
139 " \"imageUrl\": \"%s\",\n" | |
140 " \"ampUrl\": \"%s\"\n" | |
141 " }", | |
142 ids_string.c_str(), kSnippetTitle, kSnippetText, url.c_str(), | |
143 FormatTime(creation_time).c_str(), FormatTime(expiry_time).c_str(), | |
144 publisher.c_str(), kSnippetSalientImage, amp_url.c_str()); | |
148 } | 145 } |
149 | 146 |
150 std::string GetSnippetWithUrlAndTimes( | 147 std::string GetSnippetWithSources(const std::string& source_url, |
151 const std::string& url, | 148 const std::string& publisher, |
152 const std::string& content_creation_time_str, | 149 const std::string& amp_url) { |
153 const std::string& expiry_time_str) { | 150 return GetSnippetWithUrlAndTimesAndSource( |
154 return GetSnippetWithUrlAndTimesAndSources( | 151 {kSnippetUrl}, source_url, GetDefaultCreationTime(), |
155 url, content_creation_time_str, expiry_time_str, | 152 GetDefaultExpirationTime(), publisher, amp_url); |
156 {std::string(url)}, {std::string(kSnippetPublisherName)}, | |
157 {std::string(kSnippetAmpUrl)}); | |
158 } | 153 } |
159 | 154 |
160 std::string GetSnippetWithTimes(const std::string& content_creation_time_str, | 155 std::string GetSnippetWithUrlAndTimes(const std::string& url, |
161 const std::string& expiry_time_str) { | 156 const base::Time& content_creation_time, |
162 return GetSnippetWithUrlAndTimes(kSnippetUrl, content_creation_time_str, | 157 const base::Time& expiry_time) { |
163 expiry_time_str); | 158 return GetSnippetWithUrlAndTimesAndSource({url}, url, content_creation_time, |
159 expiry_time, kSnippetPublisherName, | |
160 kSnippetAmpUrl); | |
161 } | |
162 | |
163 std::string GetSnippetWithTimes(const base::Time& content_creation_time, | |
164 const base::Time& expiry_time) { | |
165 return GetSnippetWithUrlAndTimes(kSnippetUrl, content_creation_time, | |
166 expiry_time); | |
164 } | 167 } |
165 | 168 |
166 std::string GetSnippetWithUrl(const std::string& url) { | 169 std::string GetSnippetWithUrl(const std::string& url) { |
167 return GetSnippetWithUrlAndTimes( | 170 return GetSnippetWithUrlAndTimes(url, GetDefaultCreationTime(), |
168 url, NTPSnippet::TimeToJsonString(GetDefaultCreationTime()), | 171 GetDefaultExpirationTime()); |
169 NTPSnippet::TimeToJsonString(GetDefaultExpirationTime())); | |
170 } | 172 } |
171 | 173 |
172 std::string GetSnippet() { | 174 std::string GetSnippet() { |
173 return GetSnippetWithUrlAndTimes( | 175 return GetSnippetWithUrlAndTimes(kSnippetUrl, GetDefaultCreationTime(), |
174 kSnippetUrl, NTPSnippet::TimeToJsonString(GetDefaultCreationTime()), | 176 GetDefaultExpirationTime()); |
175 NTPSnippet::TimeToJsonString(GetDefaultExpirationTime())); | |
176 } | 177 } |
177 | 178 |
178 std::string GetExpiredSnippet() { | 179 std::string GetExpiredSnippet() { |
179 return GetSnippetWithTimes( | 180 return GetSnippetWithTimes(GetDefaultCreationTime(), base::Time::Now()); |
180 NTPSnippet::TimeToJsonString(GetDefaultCreationTime()), | |
181 NTPSnippet::TimeToJsonString(base::Time::Now())); | |
182 } | 181 } |
183 | 182 |
184 std::string GetInvalidSnippet() { | 183 std::string GetInvalidSnippet() { |
185 std::string json_str = GetSnippet(); | 184 std::string json_str = GetSnippet(); |
186 // Make the json invalid by removing the final closing brace. | 185 // Make the json invalid by removing the final closing brace. |
187 return json_str.substr(0, json_str.size() - 1); | 186 return json_str.substr(0, json_str.size() - 1); |
188 } | 187 } |
189 | 188 |
190 std::string GetIncompleteSnippet() { | 189 std::string GetIncompleteSnippet() { |
191 std::string json_str = GetSnippet(); | 190 std::string json_str = GetSnippet(); |
192 // Rename the "url" entry. The result is syntactically valid json that will | 191 // Rename the "url" entry. The result is syntactically valid json that will |
193 // fail to parse as snippets. | 192 // fail to parse as snippets. |
194 size_t pos = json_str.find("\"url\""); | 193 size_t pos = json_str.find("\"fullPageUrl\""); |
195 if (pos == std::string::npos) { | 194 if (pos == std::string::npos) { |
196 NOTREACHED(); | 195 NOTREACHED(); |
197 return std::string(); | 196 return std::string(); |
198 } | 197 } |
199 json_str[pos + 1] = 'x'; | 198 json_str[pos + 1] = 'x'; |
200 return json_str; | 199 return json_str; |
201 } | 200 } |
202 | 201 |
203 void ServeOneByOneImage( | 202 void ServeOneByOneImage( |
204 const std::string& id, | 203 const std::string& id, |
(...skipping 30 matching lines...) Expand all Loading... | |
235 class MockScheduler : public NTPSnippetsScheduler { | 234 class MockScheduler : public NTPSnippetsScheduler { |
236 public: | 235 public: |
237 MOCK_METHOD4(Schedule, | 236 MOCK_METHOD4(Schedule, |
238 bool(base::TimeDelta period_wifi_charging, | 237 bool(base::TimeDelta period_wifi_charging, |
239 base::TimeDelta period_wifi, | 238 base::TimeDelta period_wifi, |
240 base::TimeDelta period_fallback, | 239 base::TimeDelta period_fallback, |
241 base::Time reschedule_time)); | 240 base::Time reschedule_time)); |
242 MOCK_METHOD0(Unschedule, bool()); | 241 MOCK_METHOD0(Unschedule, bool()); |
243 }; | 242 }; |
244 | 243 |
245 class WaitForDBLoad { | |
246 public: | |
247 WaitForDBLoad(MockContentSuggestionsProviderObserver* observer, | |
248 NTPSnippetsService* service) | |
249 : observer_(observer) { | |
250 EXPECT_CALL(*observer_, OnCategoryStatusChanged(_, _, _)) | |
251 .WillOnce(Invoke(this, &WaitForDBLoad::OnCategoryStatusChanged)); | |
252 if (!service->ready()) | |
253 run_loop_.Run(); | |
254 } | |
255 | |
256 ~WaitForDBLoad() { Mock::VerifyAndClearExpectations(observer_); } | |
257 | |
258 private: | |
259 void OnCategoryStatusChanged(ContentSuggestionsProvider* provider, | |
260 Category category, | |
261 CategoryStatus new_status) { | |
262 EXPECT_EQ(new_status, CategoryStatus::AVAILABLE_LOADING); | |
263 run_loop_.Quit(); | |
264 } | |
265 | |
266 MockContentSuggestionsProviderObserver* observer_; | |
267 base::RunLoop run_loop_; | |
268 | |
269 DISALLOW_COPY_AND_ASSIGN(WaitForDBLoad); | |
270 }; | |
271 | |
272 class MockImageFetcher : public ImageFetcher { | 244 class MockImageFetcher : public ImageFetcher { |
273 public: | 245 public: |
274 MOCK_METHOD1(SetImageFetcherDelegate, void(ImageFetcherDelegate*)); | 246 MOCK_METHOD1(SetImageFetcherDelegate, void(ImageFetcherDelegate*)); |
275 MOCK_METHOD1(SetDataUseServiceName, void(DataUseServiceName)); | 247 MOCK_METHOD1(SetDataUseServiceName, void(DataUseServiceName)); |
276 MOCK_METHOD3( | 248 MOCK_METHOD3( |
277 StartOrQueueNetworkRequest, | 249 StartOrQueueNetworkRequest, |
278 void(const std::string&, | 250 void(const std::string&, |
279 const GURL&, | 251 const GURL&, |
280 base::Callback<void(const std::string&, const gfx::Image&)>)); | 252 base::Callback<void(const std::string&, const gfx::Image&)>)); |
281 }; | 253 }; |
282 | 254 |
283 class MockDismissedSuggestionsCallback | 255 class FakeContentSuggestionsProviderObserver |
284 : public ContentSuggestionsProvider::DismissedSuggestionsCallback { | 256 : public ContentSuggestionsProvider::Observer { |
285 public: | 257 public: |
286 MOCK_METHOD2(MockRun, | 258 FakeContentSuggestionsProviderObserver() |
287 void(Category category, | 259 : loaded_(base::WaitableEvent::ResetPolicy::MANUAL, |
288 std::vector<ContentSuggestion>* dismissed_suggestions)); | 260 base::WaitableEvent::InitialState::NOT_SIGNALED) {} |
289 void Run(Category category, | 261 |
290 std::vector<ContentSuggestion> dismissed_suggestions) { | 262 void OnNewSuggestions(ContentSuggestionsProvider* provider, |
291 MockRun(category, &dismissed_suggestions); | 263 Category category, |
264 std::vector<ContentSuggestion> suggestions) override { | |
265 suggestions_[category] = std::move(suggestions); | |
292 } | 266 } |
267 | |
268 void OnCategoryStatusChanged(ContentSuggestionsProvider* provider, | |
269 Category category, | |
270 CategoryStatus new_status) override { | |
271 loaded_.Signal(); | |
272 statuses_[category] = new_status; | |
273 } | |
274 | |
275 void OnSuggestionInvalidated(ContentSuggestionsProvider* provider, | |
276 Category category, | |
277 const std::string& suggestion_id) override {} | |
278 | |
279 CategoryStatus StatusForCategory(Category category) const { | |
280 auto it = statuses_.find(category); | |
281 if (it == statuses_.end()) { | |
282 return CategoryStatus::NOT_PROVIDED; | |
283 } | |
284 return it->second; | |
285 } | |
286 | |
287 const std::vector<ContentSuggestion>& SuggestionsForCategory( | |
288 Category category) { | |
289 return suggestions_[category]; | |
290 } | |
291 | |
292 void WaitForLoad() { loaded_.Wait(); } | |
293 | |
294 void Reset() { | |
295 loaded_.Reset(); | |
296 statuses_.clear(); | |
297 } | |
298 | |
299 private: | |
300 base::WaitableEvent loaded_; | |
301 std::map<Category, CategoryStatus, Category::CompareByID> statuses_; | |
302 std::map<Category, std::vector<ContentSuggestion>, Category::CompareByID> | |
303 suggestions_; | |
304 | |
305 DISALLOW_COPY_AND_ASSIGN(FakeContentSuggestionsProviderObserver); | |
293 }; | 306 }; |
294 | 307 |
295 } // namespace | 308 } // namespace |
296 | 309 |
297 class NTPSnippetsServiceTest : public test::NTPSnippetsTestBase { | 310 class NTPSnippetsServiceTest : public ::testing::Test { |
298 public: | 311 public: |
299 NTPSnippetsServiceTest() | 312 NTPSnippetsServiceTest() |
300 : fake_url_fetcher_factory_( | 313 : params_manager_(ntp_snippets::kStudyName, |
314 {{"content_suggestions_backend", | |
315 kTestContentSuggestionsServerEndpoint}}), | |
316 fake_url_fetcher_factory_( | |
301 /*default_factory=*/&failing_url_fetcher_factory_), | 317 /*default_factory=*/&failing_url_fetcher_factory_), |
302 test_url_(base::StringPrintf(kTestContentSnippetsServerFormat, | 318 test_url_(base::StringPrintf(kTestContentSuggestionsServerFormat, |
303 google_apis::GetAPIKey().c_str())) { | 319 google_apis::GetAPIKey().c_str())), |
304 NTPSnippetsService::RegisterProfilePrefs(pref_service()->registry()); | 320 |
305 RequestThrottler::RegisterProfilePrefs(pref_service()->registry()); | 321 observer_(base::MakeUnique<FakeContentSuggestionsProviderObserver>()) { |
322 NTPSnippetsService::RegisterProfilePrefs(utils_.pref_service()->registry()); | |
323 RequestThrottler::RegisterProfilePrefs(utils_.pref_service()->registry()); | |
306 | 324 |
307 // Since no SuggestionsService is injected in tests, we need to force the | 325 // Since no SuggestionsService is injected in tests, we need to force the |
308 // service to fetch from all hosts. | 326 // service to fetch from all hosts. |
309 base::CommandLine::ForCurrentProcess()->AppendSwitch( | 327 base::CommandLine::ForCurrentProcess()->AppendSwitch( |
310 switches::kDontRestrict); | 328 switches::kDontRestrict); |
311 EXPECT_TRUE(database_dir_.CreateUniqueTempDir()); | 329 EXPECT_TRUE(database_dir_.CreateUniqueTempDir()); |
312 } | 330 } |
313 | 331 |
314 ~NTPSnippetsServiceTest() override { | 332 ~NTPSnippetsServiceTest() override { |
315 // We need to run the message loop after deleting the database, because | 333 // We need to run the message loop after deleting the database, because |
316 // ProtoDatabaseImpl deletes the actual LevelDB asynchronously on the task | 334 // ProtoDatabaseImpl deletes the actual LevelDB asynchronously on the task |
317 // runner. Without this, we'd get reports of memory leaks. | 335 // runner. Without this, we'd get reports of memory leaks. |
318 Mock::VerifyAndClear(&mock_scheduler()); | |
319 service_.reset(); | |
320 base::RunLoop().RunUntilIdle(); | 336 base::RunLoop().RunUntilIdle(); |
321 } | 337 } |
322 | 338 |
323 void SetUp() override { | 339 std::unique_ptr<NTPSnippetsService> MakeSnippetsService() { |
324 test::NTPSnippetsTestBase::SetUp(); | |
325 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)).Times(1); | |
326 CreateSnippetsService(); | |
327 } | |
328 | |
329 void RecreateSnippetsService() { | |
330 Mock::VerifyAndClear(&mock_scheduler()); | |
331 service_.reset(); | |
332 base::RunLoop().RunUntilIdle(); | |
333 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)).Times(1); | |
334 CreateSnippetsService(); | |
335 } | |
336 | |
337 void CreateSnippetsService() { | |
338 DCHECK(!service_); | |
339 | |
340 scoped_refptr<base::SingleThreadTaskRunner> task_runner( | 340 scoped_refptr<base::SingleThreadTaskRunner> task_runner( |
341 base::ThreadTaskRunnerHandle::Get()); | 341 base::ThreadTaskRunnerHandle::Get()); |
342 scoped_refptr<net::TestURLRequestContextGetter> request_context_getter = | 342 scoped_refptr<net::TestURLRequestContextGetter> request_context_getter = |
343 new net::TestURLRequestContextGetter(task_runner.get()); | 343 new net::TestURLRequestContextGetter(task_runner.get()); |
344 | 344 |
345 ResetSigninManager(); | 345 utils_.ResetSigninManager(); |
346 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher = | 346 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher = |
347 base::MakeUnique<NTPSnippetsFetcher>( | 347 base::MakeUnique<NTPSnippetsFetcher>( |
348 fake_signin_manager(), fake_token_service_.get(), | 348 utils_.fake_signin_manager(), fake_token_service_.get(), |
349 std::move(request_context_getter), pref_service(), | 349 std::move(request_context_getter), utils_.pref_service(), |
350 &category_factory_, base::Bind(&ParseJson), | 350 &category_factory_, base::Bind(&ParseJson), |
351 /*is_stable_channel=*/true); | 351 /*is_stable_channel=*/true); |
352 | 352 |
353 fake_signin_manager()->SignIn("foo@bar.com"); | 353 utils_.fake_signin_manager()->SignIn("foo@bar.com"); |
354 snippets_fetcher->SetPersonalizationForTesting( | 354 snippets_fetcher->SetPersonalizationForTesting( |
355 NTPSnippetsFetcher::Personalization::kNonPersonal); | 355 NTPSnippetsFetcher::Personalization::kNonPersonal); |
356 | 356 |
357 auto image_fetcher = | 357 auto image_fetcher = base::MakeUnique<NiceMock<MockImageFetcher>>(); |
358 base::MakeUnique<testing::NiceMock<MockImageFetcher>>(); | |
359 image_fetcher_ = image_fetcher.get(); | 358 image_fetcher_ = image_fetcher.get(); |
360 | 359 |
361 // Add an initial fetch response, as the service tries to fetch when there | 360 // Add an initial fetch response, as the service tries to fetch when there |
362 // is nothing in the DB. | 361 // is nothing in the DB. |
363 SetUpFetchResponse(GetTestJson({GetSnippet()})); | 362 SetUpFetchResponse(GetTestJson({})); |
364 | 363 |
365 service_.reset(new NTPSnippetsService( | 364 auto service = base::MakeUnique<NTPSnippetsService>( |
366 &observer_, &category_factory_, pref_service(), nullptr, nullptr, "fr", | 365 observer_.get(), &category_factory_, utils_.pref_service(), nullptr, |
367 &scheduler_, std::move(snippets_fetcher), | 366 nullptr, "fr", &scheduler_, std::move(snippets_fetcher), |
368 std::move(image_fetcher), /*image_decoder=*/nullptr, | 367 std::move(image_fetcher), /*image_decoder=*/nullptr, |
369 base::MakeUnique<NTPSnippetsDatabase>(database_dir_.path(), | 368 base::MakeUnique<NTPSnippetsDatabase>(database_dir_.path(), |
370 task_runner), | 369 task_runner), |
371 base::MakeUnique<NTPSnippetsStatusService>(fake_signin_manager(), | 370 base::MakeUnique<NTPSnippetsStatusService>(utils_.fake_signin_manager(), |
372 pref_service()))); | 371 utils_.pref_service())); |
373 | 372 |
374 WaitForDBLoad(&observer_, service_.get()); | 373 base::RunLoop().RunUntilIdle(); |
374 observer_->WaitForLoad(); | |
375 return service; | |
375 } | 376 } |
376 | 377 |
377 std::string MakeUniqueID(const std::string& within_category_id) { | 378 void ResetSnippetsService(std::unique_ptr<NTPSnippetsService>* service) { |
378 return service()->MakeUniqueID(articles_category(), within_category_id); | 379 service->reset(); |
380 observer_ = base::MakeUnique<FakeContentSuggestionsProviderObserver>(); | |
381 *service = MakeSnippetsService(); | |
382 } | |
383 | |
384 std::string MakeUniqueID(const NTPSnippetsService& service, | |
385 const std::string& within_category_id) { | |
386 return service.MakeUniqueID(articles_category(), within_category_id); | |
379 } | 387 } |
380 | 388 |
381 Category articles_category() { | 389 Category articles_category() { |
382 return category_factory_.FromKnownCategory(KnownCategories::ARTICLES); | 390 return category_factory_.FromKnownCategory(KnownCategories::ARTICLES); |
383 } | 391 } |
384 | 392 |
385 protected: | 393 protected: |
386 const GURL& test_url() { return test_url_; } | 394 const GURL& test_url() { return test_url_; } |
387 NTPSnippetsService* service() { return service_.get(); } | 395 FakeContentSuggestionsProviderObserver& observer() { return *observer_; } |
388 MockContentSuggestionsProviderObserver& observer() { return observer_; } | |
389 MockScheduler& mock_scheduler() { return scheduler_; } | 396 MockScheduler& mock_scheduler() { return scheduler_; } |
390 testing::NiceMock<MockImageFetcher>* image_fetcher() { | 397 NiceMock<MockImageFetcher>* image_fetcher() { return image_fetcher_; } |
391 return image_fetcher_; | |
392 } | |
393 | 398 |
394 // Provide the json to be returned by the fake fetcher. | 399 // Provide the json to be returned by the fake fetcher. |
395 void SetUpFetchResponse(const std::string& json) { | 400 void SetUpFetchResponse(const std::string& json) { |
396 fake_url_fetcher_factory_.SetFakeResponse(test_url_, json, net::HTTP_OK, | 401 fake_url_fetcher_factory_.SetFakeResponse(test_url_, json, net::HTTP_OK, |
397 net::URLRequestStatus::SUCCESS); | 402 net::URLRequestStatus::SUCCESS); |
398 } | 403 } |
399 | 404 |
400 void LoadFromJSONString(const std::string& json) { | 405 void LoadFromJSONString(NTPSnippetsService* service, |
406 const std::string& json) { | |
401 SetUpFetchResponse(json); | 407 SetUpFetchResponse(json); |
402 service()->FetchSnippets(true); | 408 service->FetchSnippets(true); |
403 base::RunLoop().RunUntilIdle(); | 409 base::RunLoop().RunUntilIdle(); |
404 } | 410 } |
405 | 411 |
406 private: | 412 private: |
413 variations::testing::VariationParamsManager params_manager_; | |
414 test::NTPSnippetsTestUtils utils_; | |
407 base::MessageLoop message_loop_; | 415 base::MessageLoop message_loop_; |
408 FailingFakeURLFetcherFactory failing_url_fetcher_factory_; | 416 FailingFakeURLFetcherFactory failing_url_fetcher_factory_; |
409 // Instantiation of factory automatically sets itself as URLFetcher's factory. | 417 // Instantiation of factory automatically sets itself as URLFetcher's factory. |
410 net::FakeURLFetcherFactory fake_url_fetcher_factory_; | 418 net::FakeURLFetcherFactory fake_url_fetcher_factory_; |
411 const GURL test_url_; | 419 const GURL test_url_; |
412 std::unique_ptr<OAuth2TokenService> fake_token_service_; | 420 std::unique_ptr<OAuth2TokenService> fake_token_service_; |
413 MockScheduler scheduler_; | 421 StrictMock<MockScheduler> scheduler_; |
414 MockContentSuggestionsProviderObserver observer_; | 422 std::unique_ptr<FakeContentSuggestionsProviderObserver> observer_; |
415 CategoryFactory category_factory_; | 423 CategoryFactory category_factory_; |
416 testing::NiceMock<MockImageFetcher>* image_fetcher_; | 424 NiceMock<MockImageFetcher>* image_fetcher_; |
417 // Last so that the dependencies are deleted after the service. | |
418 std::unique_ptr<NTPSnippetsService> service_; | |
419 | 425 |
420 base::ScopedTempDir database_dir_; | 426 base::ScopedTempDir database_dir_; |
421 | 427 |
422 DISALLOW_COPY_AND_ASSIGN(NTPSnippetsServiceTest); | 428 DISALLOW_COPY_AND_ASSIGN(NTPSnippetsServiceTest); |
423 }; | 429 }; |
424 | 430 |
425 TEST_F(NTPSnippetsServiceTest, ScheduleOnStart) { | 431 TEST_F(NTPSnippetsServiceTest, ScheduleOnStart) { |
426 // SetUp() checks that Schedule is called. | 432 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
433 auto service = MakeSnippetsService(); | |
427 | 434 |
428 // When we have no snippets are all, loading the service initiates a fetch. | 435 // When we have no snippets are all, loading the service initiates a fetch. |
429 base::RunLoop().RunUntilIdle(); | 436 base::RunLoop().RunUntilIdle(); |
430 ASSERT_EQ("OK", service()->snippets_fetcher()->last_status()); | 437 ASSERT_EQ("OK", service->snippets_fetcher()->last_status()); |
431 } | 438 } |
432 | 439 |
433 TEST_F(NTPSnippetsServiceTest, Full) { | 440 TEST_F(NTPSnippetsServiceTest, Full) { |
434 std::string json_str(GetTestJson({GetSnippet()})); | 441 std::string json_str(GetTestJson({GetSnippet()})); |
435 | 442 |
436 LoadFromJSONString(json_str); | 443 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
437 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | 444 auto service = MakeSnippetsService(); |
438 const NTPSnippet& snippet = *service()->GetSnippetsForTesting().front(); | |
439 | 445 |
440 EXPECT_EQ(snippet.id(), kSnippetUrl); | 446 LoadFromJSONString(service.get(), json_str); |
441 EXPECT_EQ(snippet.title(), kSnippetTitle); | 447 ASSERT_THAT(observer().SuggestionsForCategory(articles_category()), |
442 EXPECT_EQ(snippet.snippet(), kSnippetText); | 448 SizeIs(1)); |
443 EXPECT_EQ(snippet.salient_image_url(), GURL(kSnippetSalientImage)); | 449 ASSERT_THAT(service->GetSnippetsForTesting(), SizeIs(1)); |
444 EXPECT_EQ(GetDefaultCreationTime(), snippet.publish_date()); | 450 const ContentSuggestion& suggestion = |
445 EXPECT_EQ(snippet.best_source().publisher_name, kSnippetPublisherName); | 451 observer().SuggestionsForCategory(articles_category()).front(); |
446 EXPECT_EQ(snippet.best_source().amp_url, GURL(kSnippetAmpUrl)); | 452 |
453 EXPECT_EQ(MakeUniqueID(*service, kSnippetUrl), suggestion.id()); | |
454 EXPECT_EQ(kSnippetTitle, base::UTF16ToUTF8(suggestion.title())); | |
455 EXPECT_EQ(kSnippetText, base::UTF16ToUTF8(suggestion.snippet_text())); | |
456 // EXPECT_EQ(GURL(kSnippetSalientImage), suggestion.salient_image_url()); | |
457 EXPECT_EQ(GetDefaultCreationTime(), suggestion.publish_date()); | |
458 EXPECT_EQ(kSnippetPublisherName, | |
459 base::UTF16ToUTF8(suggestion.publisher_name())); | |
460 EXPECT_EQ(GURL(kSnippetAmpUrl), suggestion.amp_url()); | |
447 } | 461 } |
448 | 462 |
449 TEST_F(NTPSnippetsServiceTest, Clear) { | 463 TEST_F(NTPSnippetsServiceTest, Clear) { |
464 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); | |
465 auto service = MakeSnippetsService(); | |
466 | |
450 std::string json_str(GetTestJson({GetSnippet()})); | 467 std::string json_str(GetTestJson({GetSnippet()})); |
451 | 468 |
452 LoadFromJSONString(json_str); | 469 LoadFromJSONString(service.get(), json_str); |
453 EXPECT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | 470 EXPECT_THAT(service->GetSnippetsForTesting(), SizeIs(1)); |
454 | 471 |
455 service()->ClearCachedSuggestions(articles_category()); | 472 service->ClearCachedSuggestions(articles_category()); |
456 EXPECT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | 473 EXPECT_THAT(service->GetSnippetsForTesting(), IsEmpty()); |
457 } | 474 } |
458 | 475 |
459 TEST_F(NTPSnippetsServiceTest, InsertAtFront) { | 476 TEST_F(NTPSnippetsServiceTest, InsertAtFront) { |
477 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); | |
478 auto service = MakeSnippetsService(); | |
479 | |
460 std::string first("http://first"); | 480 std::string first("http://first"); |
461 LoadFromJSONString(GetTestJson({GetSnippetWithUrl(first)})); | 481 LoadFromJSONString(service.get(), GetTestJson({GetSnippetWithUrl(first)})); |
462 EXPECT_THAT(service()->GetSnippetsForTesting(), ElementsAre(IdEq(first))); | 482 EXPECT_THAT(service->GetSnippetsForTesting(), ElementsAre(IdEq(first))); |
463 | 483 |
464 std::string second("http://second"); | 484 std::string second("http://second"); |
465 LoadFromJSONString(GetTestJson({GetSnippetWithUrl(second)})); | 485 LoadFromJSONString(service.get(), GetTestJson({GetSnippetWithUrl(second)})); |
466 // The snippet loaded last should be at the first position in the list now. | 486 // The snippet loaded last should be at the first position in the list now. |
467 EXPECT_THAT(service()->GetSnippetsForTesting(), | 487 EXPECT_THAT(service->GetSnippetsForTesting(), |
468 ElementsAre(IdEq(second), IdEq(first))); | 488 ElementsAre(IdEq(second), IdEq(first))); |
469 } | 489 } |
470 | 490 |
471 TEST_F(NTPSnippetsServiceTest, LimitNumSnippets) { | 491 TEST_F(NTPSnippetsServiceTest, LimitNumSnippets) { |
492 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); | |
493 auto service = MakeSnippetsService(); | |
494 | |
472 int max_snippet_count = NTPSnippetsService::GetMaxSnippetCountForTesting(); | 495 int max_snippet_count = NTPSnippetsService::GetMaxSnippetCountForTesting(); |
473 int snippets_per_load = max_snippet_count / 2 + 1; | 496 int snippets_per_load = max_snippet_count / 2 + 1; |
474 char url_format[] = "http://localhost/%i"; | 497 char url_format[] = "http://localhost/%i"; |
475 | 498 |
476 std::vector<std::string> snippets1; | 499 std::vector<std::string> snippets1; |
477 std::vector<std::string> snippets2; | 500 std::vector<std::string> snippets2; |
478 for (int i = 0; i < snippets_per_load; i++) { | 501 for (int i = 0; i < snippets_per_load; i++) { |
479 snippets1.push_back(GetSnippetWithUrl(base::StringPrintf(url_format, i))); | 502 snippets1.push_back(GetSnippetWithUrl(base::StringPrintf(url_format, i))); |
480 snippets2.push_back(GetSnippetWithUrl( | 503 snippets2.push_back(GetSnippetWithUrl( |
481 base::StringPrintf(url_format, snippets_per_load + i))); | 504 base::StringPrintf(url_format, snippets_per_load + i))); |
482 } | 505 } |
483 | 506 |
484 LoadFromJSONString(GetTestJson(snippets1)); | 507 LoadFromJSONString(service.get(), GetTestJson(snippets1)); |
485 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(snippets1.size())); | 508 ASSERT_THAT(service->GetSnippetsForTesting(), SizeIs(snippets1.size())); |
486 | 509 |
487 LoadFromJSONString(GetTestJson(snippets2)); | 510 LoadFromJSONString(service.get(), GetTestJson(snippets2)); |
488 EXPECT_THAT(service()->GetSnippetsForTesting(), SizeIs(max_snippet_count)); | 511 EXPECT_THAT(service->GetSnippetsForTesting(), SizeIs(max_snippet_count)); |
489 } | 512 } |
490 | 513 |
491 TEST_F(NTPSnippetsServiceTest, LoadInvalidJson) { | 514 TEST_F(NTPSnippetsServiceTest, LoadInvalidJson) { |
492 LoadFromJSONString(GetTestJson({GetInvalidSnippet()})); | 515 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
493 EXPECT_THAT(service()->snippets_fetcher()->last_status(), | 516 auto service = MakeSnippetsService(); |
517 | |
518 LoadFromJSONString(service.get(), GetTestJson({GetInvalidSnippet()})); | |
519 EXPECT_THAT(service->snippets_fetcher()->last_status(), | |
494 StartsWith("Received invalid JSON")); | 520 StartsWith("Received invalid JSON")); |
495 EXPECT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | 521 EXPECT_THAT(service->GetSnippetsForTesting(), IsEmpty()); |
496 } | 522 } |
497 | 523 |
498 TEST_F(NTPSnippetsServiceTest, LoadInvalidJsonWithExistingSnippets) { | 524 TEST_F(NTPSnippetsServiceTest, LoadInvalidJsonWithExistingSnippets) { |
499 LoadFromJSONString(GetTestJson({GetSnippet()})); | 525 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
500 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | 526 auto service = MakeSnippetsService(); |
501 ASSERT_EQ("OK", service()->snippets_fetcher()->last_status()); | |
502 | 527 |
503 LoadFromJSONString(GetTestJson({GetInvalidSnippet()})); | 528 LoadFromJSONString(service.get(), GetTestJson({GetSnippet()})); |
504 EXPECT_THAT(service()->snippets_fetcher()->last_status(), | 529 ASSERT_THAT(service->GetSnippetsForTesting(), SizeIs(1)); |
530 ASSERT_EQ("OK", service->snippets_fetcher()->last_status()); | |
531 | |
532 LoadFromJSONString(service.get(), GetTestJson({GetInvalidSnippet()})); | |
533 EXPECT_THAT(service->snippets_fetcher()->last_status(), | |
505 StartsWith("Received invalid JSON")); | 534 StartsWith("Received invalid JSON")); |
506 // This should not have changed the existing snippets. | 535 // This should not have changed the existing snippets. |
507 EXPECT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | 536 EXPECT_THAT(service->GetSnippetsForTesting(), SizeIs(1)); |
508 } | 537 } |
509 | 538 |
510 TEST_F(NTPSnippetsServiceTest, LoadIncompleteJson) { | 539 TEST_F(NTPSnippetsServiceTest, LoadIncompleteJson) { |
511 LoadFromJSONString(GetTestJson({GetIncompleteSnippet()})); | 540 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
541 auto service = MakeSnippetsService(); | |
542 | |
543 LoadFromJSONString(service.get(), GetTestJson({GetIncompleteSnippet()})); | |
512 EXPECT_EQ("Invalid / empty list.", | 544 EXPECT_EQ("Invalid / empty list.", |
513 service()->snippets_fetcher()->last_status()); | 545 service->snippets_fetcher()->last_status()); |
514 EXPECT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | 546 EXPECT_THAT(service->GetSnippetsForTesting(), IsEmpty()); |
515 } | 547 } |
516 | 548 |
517 TEST_F(NTPSnippetsServiceTest, LoadIncompleteJsonWithExistingSnippets) { | 549 TEST_F(NTPSnippetsServiceTest, LoadIncompleteJsonWithExistingSnippets) { |
518 LoadFromJSONString(GetTestJson({GetSnippet()})); | 550 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
519 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | 551 auto service = MakeSnippetsService(); |
520 | 552 |
521 LoadFromJSONString(GetTestJson({GetIncompleteSnippet()})); | 553 LoadFromJSONString(service.get(), GetTestJson({GetSnippet()})); |
554 ASSERT_THAT(service->GetSnippetsForTesting(), SizeIs(1)); | |
555 | |
556 LoadFromJSONString(service.get(), GetTestJson({GetIncompleteSnippet()})); | |
522 EXPECT_EQ("Invalid / empty list.", | 557 EXPECT_EQ("Invalid / empty list.", |
523 service()->snippets_fetcher()->last_status()); | 558 service->snippets_fetcher()->last_status()); |
524 // This should not have changed the existing snippets. | 559 // This should not have changed the existing snippets. |
525 EXPECT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | 560 EXPECT_THAT(service->GetSnippetsForTesting(), SizeIs(1)); |
526 } | 561 } |
527 | 562 |
528 TEST_F(NTPSnippetsServiceTest, Dismiss) { | 563 TEST_F(NTPSnippetsServiceTest, Dismiss) { |
529 std::vector<std::string> source_urls, publishers, amp_urls; | 564 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)).Times(2); |
530 source_urls.push_back(std::string("http://site.com")); | 565 auto service = MakeSnippetsService(); |
531 publishers.push_back(std::string("Source 1")); | 566 |
532 amp_urls.push_back(std::string()); | |
533 std::string json_str( | 567 std::string json_str( |
534 GetTestJson({GetSnippetWithSources(source_urls, publishers, amp_urls)})); | 568 GetTestJson({GetSnippetWithSources("http://site.com", "Source 1", "")})); |
535 | 569 |
536 LoadFromJSONString(json_str); | 570 LoadFromJSONString(service.get(), json_str); |
537 | 571 |
538 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | 572 ASSERT_THAT(service->GetSnippetsForTesting(), SizeIs(1)); |
539 | 573 |
540 // Dismissing a non-existent snippet shouldn't do anything. | 574 // Dismissing a non-existent snippet shouldn't do anything. |
541 service()->DismissSuggestion(MakeUniqueID("http://othersite.com")); | 575 service->DismissSuggestion(MakeUniqueID(*service, "http://othersite.com")); |
542 EXPECT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | 576 EXPECT_THAT(service->GetSnippetsForTesting(), SizeIs(1)); |
543 | 577 |
544 // Dismiss the snippet. | 578 // Dismiss the snippet. |
545 service()->DismissSuggestion(MakeUniqueID(kSnippetUrl)); | 579 service->DismissSuggestion(MakeUniqueID(*service, kSnippetUrl)); |
546 EXPECT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | 580 EXPECT_THAT(service->GetSnippetsForTesting(), IsEmpty()); |
547 | 581 |
548 // Make sure that fetching the same snippet again does not re-add it. | 582 // Make sure that fetching the same snippet again does not re-add it. |
549 LoadFromJSONString(json_str); | 583 LoadFromJSONString(service.get(), json_str); |
550 EXPECT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | 584 EXPECT_THAT(service->GetSnippetsForTesting(), IsEmpty()); |
551 | 585 |
552 // The snippet should stay dismissed even after re-creating the service. | 586 // The snippet should stay dismissed even after re-creating the service. |
553 RecreateSnippetsService(); | 587 ResetSnippetsService(&service); |
554 LoadFromJSONString(json_str); | 588 LoadFromJSONString(service.get(), json_str); |
555 EXPECT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | 589 EXPECT_THAT(service->GetSnippetsForTesting(), IsEmpty()); |
556 | 590 |
557 // The snippet can be added again after clearing dismissed snippets. | 591 // The snippet can be added again after clearing dismissed snippets. |
558 service()->ClearDismissedSuggestionsForDebugging(articles_category()); | 592 service->ClearDismissedSuggestionsForDebugging(articles_category()); |
559 EXPECT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | 593 EXPECT_THAT(service->GetSnippetsForTesting(), IsEmpty()); |
560 LoadFromJSONString(json_str); | 594 LoadFromJSONString(service.get(), json_str); |
561 EXPECT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | 595 EXPECT_THAT(service->GetSnippetsForTesting(), SizeIs(1)); |
562 } | 596 } |
563 | 597 |
564 TEST_F(NTPSnippetsServiceTest, GetDismissed) { | 598 TEST_F(NTPSnippetsServiceTest, GetDismissed) { |
565 LoadFromJSONString(GetTestJson({GetSnippet()})); | 599 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
600 auto service = MakeSnippetsService(); | |
566 | 601 |
567 service()->DismissSuggestion(MakeUniqueID(kSnippetUrl)); | 602 LoadFromJSONString(service.get(), GetTestJson({GetSnippet()})); |
568 | 603 |
569 MockDismissedSuggestionsCallback callback; | 604 service->DismissSuggestion(MakeUniqueID(*service, kSnippetUrl)); |
570 | 605 |
571 EXPECT_CALL(callback, MockRun(_, _)) | 606 service->GetDismissedSuggestionsForDebugging( |
572 .WillOnce( | 607 articles_category(), |
573 Invoke([this](Category category, | 608 base::Bind( |
574 std::vector<ContentSuggestion>* dismissed_suggestions) { | 609 [](NTPSnippetsService* service, NTPSnippetsServiceTest* test, |
575 EXPECT_EQ(1u, dismissed_suggestions->size()); | 610 std::vector<ContentSuggestion> dismissed_suggestions) { |
576 for (auto& suggestion : *dismissed_suggestions) { | 611 EXPECT_EQ(1u, dismissed_suggestions.size()); |
577 EXPECT_EQ(MakeUniqueID(kSnippetUrl), suggestion.id()); | 612 for (auto& suggestion : dismissed_suggestions) { |
613 EXPECT_EQ(test->MakeUniqueID(*service, kSnippetUrl), | |
614 suggestion.id()); | |
578 } | 615 } |
579 })); | 616 }, |
580 service()->GetDismissedSuggestionsForDebugging( | 617 service.get(), this)); |
581 articles_category(), | 618 base::RunLoop().RunUntilIdle(); |
582 base::Bind(&MockDismissedSuggestionsCallback::Run, | |
583 base::Unretained(&callback), articles_category())); | |
584 Mock::VerifyAndClearExpectations(&callback); | |
585 | 619 |
586 // There should be no dismissed snippet after clearing the list. | 620 // There should be no dismissed snippet after clearing the list. |
587 EXPECT_CALL(callback, MockRun(_, _)) | 621 service->ClearDismissedSuggestionsForDebugging(articles_category()); |
588 .WillOnce( | 622 service->GetDismissedSuggestionsForDebugging( |
589 Invoke([this](Category category, | |
590 std::vector<ContentSuggestion>* dismissed_suggestions) { | |
591 EXPECT_EQ(0u, dismissed_suggestions->size()); | |
592 })); | |
593 service()->ClearDismissedSuggestionsForDebugging(articles_category()); | |
594 service()->GetDismissedSuggestionsForDebugging( | |
595 articles_category(), | 623 articles_category(), |
596 base::Bind(&MockDismissedSuggestionsCallback::Run, | 624 base::Bind( |
597 base::Unretained(&callback), articles_category())); | 625 [](NTPSnippetsService* service, NTPSnippetsServiceTest* test, |
626 std::vector<ContentSuggestion> dismissed_suggestions) { | |
627 EXPECT_EQ(0u, dismissed_suggestions.size()); | |
628 }, | |
629 service.get(), this)); | |
630 base::RunLoop().RunUntilIdle(); | |
598 } | 631 } |
599 | 632 |
600 TEST_F(NTPSnippetsServiceTest, CreationTimestampParseFail) { | 633 TEST_F(NTPSnippetsServiceTest, CreationTimestampParseFail) { |
601 std::string json_str(GetTestJson({GetSnippetWithTimes( | 634 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
602 "aaa1448459205", | 635 auto service = MakeSnippetsService(); |
603 NTPSnippet::TimeToJsonString(GetDefaultExpirationTime()))})); | |
604 | 636 |
605 LoadFromJSONString(json_str); | 637 std::string json = |
606 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | 638 GetSnippetWithTimes(GetDefaultCreationTime(), GetDefaultExpirationTime()); |
607 const NTPSnippet& snippet = *service()->GetSnippetsForTesting().front(); | 639 base::ReplaceFirstSubstringAfterOffset( |
608 EXPECT_EQ(snippet.id(), kSnippetUrl); | 640 &json, 0, FormatTime(GetDefaultCreationTime()), "aaa1448459205"); |
609 EXPECT_EQ(snippet.title(), kSnippetTitle); | 641 std::string json_str(GetTestJson({json})); |
610 EXPECT_EQ(snippet.snippet(), kSnippetText); | 642 |
611 EXPECT_EQ(base::Time::UnixEpoch(), snippet.publish_date()); | 643 LoadFromJSONString(service.get(), json_str); |
644 EXPECT_THAT(service->GetSnippetsForTesting(), SizeIs(0)); | |
Marc Treib
2016/08/30 13:43:39
nit: IsEmpty() rather than SizeIs(0)
sfiera
2016/08/30 16:33:09
Done.
| |
612 } | 645 } |
613 | 646 |
614 TEST_F(NTPSnippetsServiceTest, RemoveExpiredContent) { | 647 TEST_F(NTPSnippetsServiceTest, RemoveExpiredContent) { |
648 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); | |
649 auto service = MakeSnippetsService(); | |
650 | |
615 std::string json_str(GetTestJson({GetExpiredSnippet()})); | 651 std::string json_str(GetTestJson({GetExpiredSnippet()})); |
616 | 652 |
617 LoadFromJSONString(json_str); | 653 LoadFromJSONString(service.get(), json_str); |
618 EXPECT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | 654 EXPECT_THAT(service->GetSnippetsForTesting(), IsEmpty()); |
619 } | 655 } |
620 | 656 |
621 TEST_F(NTPSnippetsServiceTest, TestSingleSource) { | 657 TEST_F(NTPSnippetsServiceTest, TestSingleSource) { |
622 std::vector<std::string> source_urls, publishers, amp_urls; | 658 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
623 source_urls.push_back(std::string("http://source1.com")); | 659 auto service = MakeSnippetsService(); |
624 publishers.push_back(std::string("Source 1")); | |
625 amp_urls.push_back(std::string("http://source1.amp.com")); | |
626 std::string json_str( | |
627 GetTestJson({GetSnippetWithSources(source_urls, publishers, amp_urls)})); | |
628 | 660 |
629 LoadFromJSONString(json_str); | 661 std::string json_str(GetTestJson({GetSnippetWithSources( |
630 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | 662 "http://source1.com", "Source 1", "http://source1.amp.com")})); |
631 const NTPSnippet& snippet = *service()->GetSnippetsForTesting().front(); | 663 |
664 LoadFromJSONString(service.get(), json_str); | |
665 ASSERT_THAT(service->GetSnippetsForTesting(), SizeIs(1)); | |
666 const NTPSnippet& snippet = *service->GetSnippetsForTesting().front(); | |
632 EXPECT_EQ(snippet.sources().size(), 1u); | 667 EXPECT_EQ(snippet.sources().size(), 1u); |
633 EXPECT_EQ(snippet.id(), kSnippetUrl); | 668 EXPECT_EQ(snippet.id(), kSnippetUrl); |
634 EXPECT_EQ(snippet.best_source().url, GURL("http://source1.com")); | 669 EXPECT_EQ(snippet.best_source().url, GURL("http://source1.com")); |
635 EXPECT_EQ(snippet.best_source().publisher_name, std::string("Source 1")); | 670 EXPECT_EQ(snippet.best_source().publisher_name, std::string("Source 1")); |
636 EXPECT_EQ(snippet.best_source().amp_url, GURL("http://source1.amp.com")); | 671 EXPECT_EQ(snippet.best_source().amp_url, GURL("http://source1.amp.com")); |
637 } | 672 } |
638 | 673 |
639 TEST_F(NTPSnippetsServiceTest, TestSingleSourceWithMalformedUrl) { | 674 TEST_F(NTPSnippetsServiceTest, TestSingleSourceWithMalformedUrl) { |
640 std::vector<std::string> source_urls, publishers, amp_urls; | 675 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
641 source_urls.push_back(std::string("aaaa")); | 676 auto service = MakeSnippetsService(); |
642 publishers.push_back(std::string("Source 1")); | |
643 amp_urls.push_back(std::string("http://source1.amp.com")); | |
644 std::string json_str( | |
645 GetTestJson({GetSnippetWithSources(source_urls, publishers, amp_urls)})); | |
646 | 677 |
647 LoadFromJSONString(json_str); | 678 std::string json_str(GetTestJson({GetSnippetWithSources( |
648 EXPECT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | 679 "ceci n'est pas un url", "Source 1", "http://source1.amp.com")})); |
680 | |
681 LoadFromJSONString(service.get(), json_str); | |
682 EXPECT_THAT(service->GetSnippetsForTesting(), IsEmpty()); | |
649 } | 683 } |
650 | 684 |
651 TEST_F(NTPSnippetsServiceTest, TestSingleSourceWithMissingData) { | 685 TEST_F(NTPSnippetsServiceTest, TestSingleSourceWithMissingData) { |
652 std::vector<std::string> source_urls, publishers, amp_urls; | 686 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
653 source_urls.push_back(std::string("http://source1.com")); | 687 auto service = MakeSnippetsService(); |
654 publishers.push_back(std::string()); | 688 |
655 amp_urls.push_back(std::string()); | |
656 std::string json_str( | 689 std::string json_str( |
657 GetTestJson({GetSnippetWithSources(source_urls, publishers, amp_urls)})); | 690 GetTestJson({GetSnippetWithSources("http://source1.com", "", "")})); |
658 | 691 |
659 LoadFromJSONString(json_str); | 692 LoadFromJSONString(service.get(), json_str); |
660 EXPECT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | 693 EXPECT_THAT(service->GetSnippetsForTesting(), IsEmpty()); |
661 } | |
662 | |
663 TEST_F(NTPSnippetsServiceTest, TestMultipleSources) { | |
664 std::vector<std::string> source_urls, publishers, amp_urls; | |
665 source_urls.push_back(std::string("http://source1.com")); | |
666 source_urls.push_back(std::string("http://source2.com")); | |
667 publishers.push_back(std::string("Source 1")); | |
668 publishers.push_back(std::string("Source 2")); | |
669 amp_urls.push_back(std::string("http://source1.amp.com")); | |
670 amp_urls.push_back(std::string("http://source2.amp.com")); | |
671 std::string json_str( | |
672 GetTestJson({GetSnippetWithSources(source_urls, publishers, amp_urls)})); | |
673 | |
674 LoadFromJSONString(json_str); | |
675 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | |
676 const NTPSnippet& snippet = *service()->GetSnippetsForTesting().front(); | |
677 // Expect the first source to be chosen | |
678 EXPECT_EQ(snippet.sources().size(), 2u); | |
679 EXPECT_EQ(snippet.id(), kSnippetUrl); | |
680 EXPECT_EQ(snippet.best_source().url, GURL("http://source1.com")); | |
681 EXPECT_EQ(snippet.best_source().publisher_name, std::string("Source 1")); | |
682 EXPECT_EQ(snippet.best_source().amp_url, GURL("http://source1.amp.com")); | |
683 } | |
684 | |
685 TEST_F(NTPSnippetsServiceTest, TestMultipleIncompleteSources) { | |
686 // Set Source 2 to have no AMP url, and Source 1 to have no publisher name | |
687 // Source 2 should win since we favor publisher name over amp url | |
688 std::vector<std::string> source_urls, publishers, amp_urls; | |
689 source_urls.push_back(std::string("http://source1.com")); | |
690 source_urls.push_back(std::string("http://source2.com")); | |
691 publishers.push_back(std::string()); | |
692 publishers.push_back(std::string("Source 2")); | |
693 amp_urls.push_back(std::string("http://source1.amp.com")); | |
694 amp_urls.push_back(std::string()); | |
695 std::string json_str( | |
696 GetTestJson({GetSnippetWithSources(source_urls, publishers, amp_urls)})); | |
697 | |
698 LoadFromJSONString(json_str); | |
699 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | |
700 { | |
701 const NTPSnippet& snippet = *service()->GetSnippetsForTesting().front(); | |
702 EXPECT_EQ(snippet.sources().size(), 2u); | |
703 EXPECT_EQ(snippet.id(), kSnippetUrl); | |
704 EXPECT_EQ(snippet.best_source().url, GURL("http://source2.com")); | |
705 EXPECT_EQ(snippet.best_source().publisher_name, std::string("Source 2")); | |
706 EXPECT_EQ(snippet.best_source().amp_url, GURL()); | |
707 } | |
708 | |
709 service()->ClearCachedSuggestions(articles_category()); | |
710 // Set Source 1 to have no AMP url, and Source 2 to have no publisher name | |
711 // Source 1 should win in this case since we prefer publisher name to AMP url | |
712 source_urls.clear(); | |
713 source_urls.push_back(std::string("http://source1.com")); | |
714 source_urls.push_back(std::string("http://source2.com")); | |
715 publishers.clear(); | |
716 publishers.push_back(std::string("Source 1")); | |
717 publishers.push_back(std::string()); | |
718 amp_urls.clear(); | |
719 amp_urls.push_back(std::string()); | |
720 amp_urls.push_back(std::string("http://source2.amp.com")); | |
721 json_str = | |
722 GetTestJson({GetSnippetWithSources(source_urls, publishers, amp_urls)}); | |
723 | |
724 LoadFromJSONString(json_str); | |
725 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | |
726 { | |
727 const NTPSnippet& snippet = *service()->GetSnippetsForTesting().front(); | |
728 EXPECT_EQ(snippet.sources().size(), 2u); | |
729 EXPECT_EQ(snippet.id(), kSnippetUrl); | |
730 EXPECT_EQ(snippet.best_source().url, GURL("http://source1.com")); | |
731 EXPECT_EQ(snippet.best_source().publisher_name, std::string("Source 1")); | |
732 EXPECT_EQ(snippet.best_source().amp_url, GURL()); | |
733 } | |
734 | |
735 service()->ClearCachedSuggestions(articles_category()); | |
736 // Set source 1 to have no AMP url and no source, and source 2 to only have | |
737 // amp url. There should be no snippets since we only add sources we consider | |
738 // complete | |
739 source_urls.clear(); | |
740 source_urls.push_back(std::string("http://source1.com")); | |
741 source_urls.push_back(std::string("http://source2.com")); | |
742 publishers.clear(); | |
743 publishers.push_back(std::string()); | |
744 publishers.push_back(std::string()); | |
745 amp_urls.clear(); | |
746 amp_urls.push_back(std::string()); | |
747 amp_urls.push_back(std::string("http://source2.amp.com")); | |
748 json_str = | |
749 GetTestJson({GetSnippetWithSources(source_urls, publishers, amp_urls)}); | |
750 | |
751 LoadFromJSONString(json_str); | |
752 EXPECT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | |
753 } | |
754 | |
755 TEST_F(NTPSnippetsServiceTest, TestMultipleCompleteSources) { | |
756 // Test 2 complete sources, we should choose the first complete source | |
757 std::vector<std::string> source_urls, publishers, amp_urls; | |
758 source_urls.push_back(std::string("http://source1.com")); | |
759 source_urls.push_back(std::string("http://source2.com")); | |
760 source_urls.push_back(std::string("http://source3.com")); | |
761 publishers.push_back(std::string("Source 1")); | |
762 publishers.push_back(std::string()); | |
763 publishers.push_back(std::string("Source 3")); | |
764 amp_urls.push_back(std::string("http://source1.amp.com")); | |
765 amp_urls.push_back(std::string("http://source2.amp.com")); | |
766 amp_urls.push_back(std::string("http://source3.amp.com")); | |
767 std::string json_str( | |
768 GetTestJson({GetSnippetWithSources(source_urls, publishers, amp_urls)})); | |
769 | |
770 LoadFromJSONString(json_str); | |
771 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | |
772 { | |
773 const NTPSnippet& snippet = *service()->GetSnippetsForTesting().front(); | |
774 EXPECT_EQ(snippet.sources().size(), 3u); | |
775 EXPECT_EQ(snippet.id(), kSnippetUrl); | |
776 EXPECT_EQ(snippet.best_source().url, GURL("http://source1.com")); | |
777 EXPECT_EQ(snippet.best_source().publisher_name, std::string("Source 1")); | |
778 EXPECT_EQ(snippet.best_source().amp_url, GURL("http://source1.amp.com")); | |
779 } | |
780 | |
781 // Test 2 complete sources, we should choose the first complete source | |
782 service()->ClearCachedSuggestions(articles_category()); | |
783 source_urls.clear(); | |
784 source_urls.push_back(std::string("http://source1.com")); | |
785 source_urls.push_back(std::string("http://source2.com")); | |
786 source_urls.push_back(std::string("http://source3.com")); | |
787 publishers.clear(); | |
788 publishers.push_back(std::string()); | |
789 publishers.push_back(std::string("Source 2")); | |
790 publishers.push_back(std::string("Source 3")); | |
791 amp_urls.clear(); | |
792 amp_urls.push_back(std::string("http://source1.amp.com")); | |
793 amp_urls.push_back(std::string("http://source2.amp.com")); | |
794 amp_urls.push_back(std::string("http://source3.amp.com")); | |
795 json_str = | |
796 GetTestJson({GetSnippetWithSources(source_urls, publishers, amp_urls)}); | |
797 | |
798 LoadFromJSONString(json_str); | |
799 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | |
800 { | |
801 const NTPSnippet& snippet = *service()->GetSnippetsForTesting().front(); | |
802 EXPECT_EQ(snippet.sources().size(), 3u); | |
803 EXPECT_EQ(snippet.id(), kSnippetUrl); | |
804 EXPECT_EQ(snippet.best_source().url, GURL("http://source2.com")); | |
805 EXPECT_EQ(snippet.best_source().publisher_name, std::string("Source 2")); | |
806 EXPECT_EQ(snippet.best_source().amp_url, GURL("http://source2.amp.com")); | |
807 } | |
808 | |
809 // Test 3 complete sources, we should choose the first complete source | |
810 service()->ClearCachedSuggestions(articles_category()); | |
811 source_urls.clear(); | |
812 source_urls.push_back(std::string("http://source1.com")); | |
813 source_urls.push_back(std::string("http://source2.com")); | |
814 source_urls.push_back(std::string("http://source3.com")); | |
815 publishers.clear(); | |
816 publishers.push_back(std::string("Source 1")); | |
817 publishers.push_back(std::string("Source 2")); | |
818 publishers.push_back(std::string("Source 3")); | |
819 amp_urls.clear(); | |
820 amp_urls.push_back(std::string()); | |
821 amp_urls.push_back(std::string("http://source2.amp.com")); | |
822 amp_urls.push_back(std::string("http://source3.amp.com")); | |
823 json_str = | |
824 GetTestJson({GetSnippetWithSources(source_urls, publishers, amp_urls)}); | |
825 | |
826 LoadFromJSONString(json_str); | |
827 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | |
828 { | |
829 const NTPSnippet& snippet = *service()->GetSnippetsForTesting().front(); | |
830 EXPECT_EQ(snippet.sources().size(), 3u); | |
831 EXPECT_EQ(snippet.id(), kSnippetUrl); | |
832 EXPECT_EQ(snippet.best_source().url, GURL("http://source2.com")); | |
833 EXPECT_EQ(snippet.best_source().publisher_name, std::string("Source 2")); | |
834 EXPECT_EQ(snippet.best_source().amp_url, GURL("http://source2.amp.com")); | |
835 } | |
836 } | 694 } |
837 | 695 |
838 TEST_F(NTPSnippetsServiceTest, LogNumArticlesHistogram) { | 696 TEST_F(NTPSnippetsServiceTest, LogNumArticlesHistogram) { |
697 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)).Times(3); | |
Marc Treib
2016/08/30 13:43:39
?
I think this is evidence in favor of Tim's argu
sfiera
2016/08/30 16:33:09
Done.
| |
698 auto service = MakeSnippetsService(); | |
699 | |
839 base::HistogramTester tester; | 700 base::HistogramTester tester; |
840 LoadFromJSONString(GetTestJson({GetInvalidSnippet()})); | 701 LoadFromJSONString(service.get(), GetTestJson({GetInvalidSnippet()})); |
841 | 702 |
842 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"), | 703 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"), |
843 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1))); | 704 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1))); |
705 | |
844 // Invalid JSON shouldn't contribute to NumArticlesFetched. | 706 // Invalid JSON shouldn't contribute to NumArticlesFetched. |
845 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"), | 707 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"), |
846 IsEmpty()); | 708 IsEmpty()); |
709 | |
847 // Valid JSON with empty list. | 710 // Valid JSON with empty list. |
848 LoadFromJSONString(GetTestJson(std::vector<std::string>())); | 711 LoadFromJSONString(service.get(), GetTestJson(std::vector<std::string>())); |
849 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"), | 712 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"), |
850 ElementsAre(base::Bucket(/*min=*/0, /*count=*/2))); | 713 ElementsAre(base::Bucket(/*min=*/0, /*count=*/2))); |
851 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"), | 714 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"), |
852 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1))); | 715 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1))); |
716 | |
853 // Snippet list should be populated with size 1. | 717 // Snippet list should be populated with size 1. |
854 LoadFromJSONString(GetTestJson({GetSnippet()})); | 718 LoadFromJSONString(service.get(), GetTestJson({GetSnippet()})); |
855 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"), | 719 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"), |
856 ElementsAre(base::Bucket(/*min=*/0, /*count=*/2), | 720 ElementsAre(base::Bucket(/*min=*/0, /*count=*/2), |
857 base::Bucket(/*min=*/1, /*count=*/1))); | 721 base::Bucket(/*min=*/1, /*count=*/1))); |
858 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"), | 722 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"), |
859 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1), | 723 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1), |
860 base::Bucket(/*min=*/1, /*count=*/1))); | 724 base::Bucket(/*min=*/1, /*count=*/1))); |
725 | |
861 // Duplicate snippet shouldn't increase the list size. | 726 // Duplicate snippet shouldn't increase the list size. |
862 LoadFromJSONString(GetTestJson({GetSnippet()})); | 727 LoadFromJSONString(service.get(), GetTestJson({GetSnippet()})); |
863 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"), | 728 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"), |
864 ElementsAre(base::Bucket(/*min=*/0, /*count=*/2), | 729 ElementsAre(base::Bucket(/*min=*/0, /*count=*/2), |
865 base::Bucket(/*min=*/1, /*count=*/2))); | 730 base::Bucket(/*min=*/1, /*count=*/2))); |
866 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"), | 731 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"), |
867 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1), | 732 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1), |
868 base::Bucket(/*min=*/1, /*count=*/2))); | 733 base::Bucket(/*min=*/1, /*count=*/2))); |
869 EXPECT_THAT( | 734 EXPECT_THAT( |
870 tester.GetAllSamples("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded"), | 735 tester.GetAllSamples("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded"), |
871 IsEmpty()); | 736 IsEmpty()); |
737 | |
872 // Dismissing a snippet should decrease the list size. This will only be | 738 // Dismissing a snippet should decrease the list size. This will only be |
873 // logged after the next fetch. | 739 // logged after the next fetch. |
874 service()->DismissSuggestion(MakeUniqueID(kSnippetUrl)); | 740 service->DismissSuggestion(MakeUniqueID(*service, kSnippetUrl)); |
875 LoadFromJSONString(GetTestJson({GetSnippet()})); | 741 LoadFromJSONString(service.get(), GetTestJson({GetSnippet()})); |
876 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"), | 742 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"), |
877 ElementsAre(base::Bucket(/*min=*/0, /*count=*/3), | 743 ElementsAre(base::Bucket(/*min=*/0, /*count=*/3), |
878 base::Bucket(/*min=*/1, /*count=*/2))); | 744 base::Bucket(/*min=*/1, /*count=*/2))); |
879 // Dismissed snippets shouldn't influence NumArticlesFetched. | 745 // Dismissed snippets shouldn't influence NumArticlesFetched. |
880 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"), | 746 EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"), |
881 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1), | 747 ElementsAre(base::Bucket(/*min=*/0, /*count=*/1), |
882 base::Bucket(/*min=*/1, /*count=*/3))); | 748 base::Bucket(/*min=*/1, /*count=*/3))); |
883 EXPECT_THAT( | 749 EXPECT_THAT( |
884 tester.GetAllSamples("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded"), | 750 tester.GetAllSamples("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded"), |
885 ElementsAre(base::Bucket(/*min=*/1, /*count=*/1))); | 751 ElementsAre(base::Bucket(/*min=*/1, /*count=*/1))); |
886 // Recreating the service and loading from prefs shouldn't count as fetched | 752 |
887 // articles. | 753 // There is only a single, dismissed snippet in the database, so recreating |
888 RecreateSnippetsService(); | 754 // the service will require us to re-fetch. |
889 tester.ExpectTotalCount("NewTabPage.Snippets.NumArticlesFetched", 4); | 755 tester.ExpectTotalCount("NewTabPage.Snippets.NumArticlesFetched", 4); |
756 ResetSnippetsService(&service); | |
757 EXPECT_EQ(observer().StatusForCategory(articles_category()), | |
758 CategoryStatus::AVAILABLE); | |
759 tester.ExpectTotalCount("NewTabPage.Snippets.NumArticlesFetched", 5); | |
760 EXPECT_THAT( | |
761 tester.GetAllSamples("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded"), | |
762 ElementsAre(base::Bucket(/*min=*/1, /*count=*/2))); | |
763 | |
764 // But if there's a non-dismissed snippet in the database, recreating it | |
765 // shouldn't trigger a fetch. | |
766 LoadFromJSONString( | |
767 service.get(), | |
768 GetTestJson({GetSnippetWithUrl("http://not-dismissed.com")})); | |
769 tester.ExpectTotalCount("NewTabPage.Snippets.NumArticlesFetched", 6); | |
770 ResetSnippetsService(&service); | |
771 tester.ExpectTotalCount("NewTabPage.Snippets.NumArticlesFetched", 6); | |
890 } | 772 } |
891 | 773 |
892 TEST_F(NTPSnippetsServiceTest, DismissShouldRespectAllKnownUrls) { | 774 TEST_F(NTPSnippetsServiceTest, DismissShouldRespectAllKnownUrls) { |
893 const std::string creation = | 775 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
894 NTPSnippet::TimeToJsonString(GetDefaultCreationTime()); | 776 auto service = MakeSnippetsService(); |
895 const std::string expiry = | 777 |
896 NTPSnippet::TimeToJsonString(GetDefaultExpirationTime()); | 778 const base::Time creation = GetDefaultCreationTime(); |
779 const base::Time expiry = GetDefaultExpirationTime(); | |
897 const std::vector<std::string> source_urls = { | 780 const std::vector<std::string> source_urls = { |
898 "http://mashable.com/2016/05/11/stolen", | 781 "http://mashable.com/2016/05/11/stolen", |
899 "http://www.aol.com/article/2016/05/stolen-doggie", | 782 "http://www.aol.com/article/2016/05/stolen-doggie"}; |
900 "http://mashable.com/2016/05/11/stolen?utm_cid=1"}; | 783 const std::vector<std::string> publishers = {"Mashable", "AOL"}; |
901 const std::vector<std::string> publishers = {"Mashable", "AOL", "Mashable"}; | |
902 const std::vector<std::string> amp_urls = { | 784 const std::vector<std::string> amp_urls = { |
903 "http://mashable-amphtml.googleusercontent.com/1", | 785 "http://mashable-amphtml.googleusercontent.com/1", |
904 "http://t2.gstatic.com/images?q=tbn:3", | |
905 "http://t2.gstatic.com/images?q=tbn:3"}; | 786 "http://t2.gstatic.com/images?q=tbn:3"}; |
906 | 787 |
907 // Add the snippet from the mashable domain. | 788 // Add the snippet from the mashable domain. |
908 LoadFromJSONString(GetTestJson({GetSnippetWithUrlAndTimesAndSources( | 789 LoadFromJSONString(service.get(), |
909 source_urls[0], creation, expiry, source_urls, publishers, amp_urls)})); | 790 GetTestJson({GetSnippetWithUrlAndTimesAndSource( |
910 ASSERT_THAT(service()->GetSnippetsForTesting(), SizeIs(1)); | 791 source_urls, source_urls[0], creation, expiry, |
792 publishers[0], amp_urls[0])})); | |
793 ASSERT_THAT(service->GetSnippetsForTesting(), SizeIs(1)); | |
911 // Dismiss the snippet via the mashable source corpus ID. | 794 // Dismiss the snippet via the mashable source corpus ID. |
912 service()->DismissSuggestion(MakeUniqueID(source_urls[0])); | 795 service->DismissSuggestion(MakeUniqueID(*service, source_urls[0])); |
913 EXPECT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | 796 EXPECT_THAT(service->GetSnippetsForTesting(), IsEmpty()); |
914 | 797 |
915 // The same article from the AOL domain should now be detected as dismissed. | 798 // The same article from the AOL domain should now be detected as dismissed. |
916 LoadFromJSONString(GetTestJson({GetSnippetWithUrlAndTimesAndSources( | 799 LoadFromJSONString(service.get(), |
917 source_urls[1], creation, expiry, source_urls, publishers, amp_urls)})); | 800 GetTestJson({GetSnippetWithUrlAndTimesAndSource( |
918 ASSERT_THAT(service()->GetSnippetsForTesting(), IsEmpty()); | 801 source_urls, source_urls[1], creation, expiry, |
802 publishers[1], amp_urls[1])})); | |
803 ASSERT_THAT(service->GetSnippetsForTesting(), IsEmpty()); | |
Marc Treib
2016/08/30 13:43:39
This should be EXPECT_THAT
sfiera
2016/08/30 16:33:09
Done.
| |
919 } | 804 } |
920 | 805 |
921 TEST_F(NTPSnippetsServiceTest, StatusChanges) { | 806 TEST_F(NTPSnippetsServiceTest, StatusChanges) { |
807 { | |
808 InSequence s; | |
809 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); | |
810 EXPECT_CALL(mock_scheduler(), Unschedule()); | |
811 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); | |
812 } | |
813 auto service = MakeSnippetsService(); | |
814 | |
922 // Simulate user signed out | 815 // Simulate user signed out |
923 SetUpFetchResponse(GetTestJson({GetSnippet()})); | 816 SetUpFetchResponse(GetTestJson({GetSnippet()})); |
924 EXPECT_CALL(observer(), | 817 service->OnDisabledReasonChanged(DisabledReason::SIGNED_OUT); |
925 OnCategoryStatusChanged(_, _, CategoryStatus::SIGNED_OUT)); | 818 |
926 service()->OnDisabledReasonChanged(DisabledReason::SIGNED_OUT); | |
927 base::RunLoop().RunUntilIdle(); | 819 base::RunLoop().RunUntilIdle(); |
928 EXPECT_EQ(NTPSnippetsService::State::DISABLED, service()->state_); | 820 EXPECT_THAT(observer().StatusForCategory(articles_category()), |
929 EXPECT_THAT(service()->GetSnippetsForTesting(), | 821 Eq(CategoryStatus::SIGNED_OUT)); |
822 EXPECT_THAT(NTPSnippetsService::State::DISABLED, Eq(service->state_)); | |
823 EXPECT_THAT(service->GetSnippetsForTesting(), | |
930 IsEmpty()); // No fetch should be made. | 824 IsEmpty()); // No fetch should be made. |
931 | 825 |
932 // Simulate user sign in. The service should be ready again and load snippets. | 826 // Simulate user sign in. The service should be ready again and load snippets. |
933 SetUpFetchResponse(GetTestJson({GetSnippet()})); | 827 SetUpFetchResponse(GetTestJson({GetSnippet()})); |
934 EXPECT_CALL(observer(), | 828 service->OnDisabledReasonChanged(DisabledReason::NONE); |
935 OnCategoryStatusChanged(_, _, CategoryStatus::AVAILABLE_LOADING)); | 829 EXPECT_THAT(observer().StatusForCategory(articles_category()), |
936 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)).Times(1); | 830 Eq(CategoryStatus::AVAILABLE_LOADING)); |
937 service()->OnDisabledReasonChanged(DisabledReason::NONE); | 831 |
938 EXPECT_CALL(observer(), | |
939 OnCategoryStatusChanged(_, _, CategoryStatus::AVAILABLE)); | |
940 base::RunLoop().RunUntilIdle(); | 832 base::RunLoop().RunUntilIdle(); |
941 EXPECT_EQ(NTPSnippetsService::State::READY, service()->state_); | 833 EXPECT_THAT(observer().StatusForCategory(articles_category()), |
942 EXPECT_FALSE(service()->GetSnippetsForTesting().empty()); | 834 Eq(CategoryStatus::AVAILABLE)); |
835 EXPECT_THAT(NTPSnippetsService::State::READY, Eq(service->state_)); | |
836 EXPECT_FALSE(service->GetSnippetsForTesting().empty()); | |
943 } | 837 } |
944 | 838 |
945 TEST_F(NTPSnippetsServiceTest, ImageReturnedWithTheSameId) { | 839 TEST_F(NTPSnippetsServiceTest, ImageReturnedWithTheSameId) { |
946 LoadFromJSONString(GetTestJson({GetSnippet()})); | 840 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); |
841 auto service = MakeSnippetsService(); | |
842 | |
843 LoadFromJSONString(service.get(), GetTestJson({GetSnippet()})); | |
947 | 844 |
948 gfx::Image image; | 845 gfx::Image image; |
949 EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _)) | 846 MockFunction<void(gfx::Image&)> image_fetched; |
950 .WillOnce(testing::WithArgs<0, 2>(Invoke(ServeOneByOneImage))); | 847 { |
951 testing::MockFunction<void(const gfx::Image&)> image_fetched; | 848 InSequence s; |
952 EXPECT_CALL(image_fetched, Call(_)).WillOnce(testing::SaveArg<0>(&image)); | 849 EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _)) |
850 .WillOnce(WithArgs<0, 2>(Invoke(ServeOneByOneImage))); | |
851 EXPECT_CALL(image_fetched, Call(_)) | |
852 .WillOnce(SaveArg<0>(&image)); | |
853 } | |
953 | 854 |
954 service()->FetchSuggestionImage( | 855 service->FetchSuggestionImage( |
955 MakeUniqueID(kSnippetUrl), | 856 MakeUniqueID(*service, kSnippetUrl), |
956 base::Bind(&testing::MockFunction<void(const gfx::Image&)>::Call, | 857 base::Bind( |
957 base::Unretained(&image_fetched))); | 858 &MockFunction<void(const gfx::Image&)>::Call, |
859 base::Unretained(&image_fetched))); | |
958 base::RunLoop().RunUntilIdle(); | 860 base::RunLoop().RunUntilIdle(); |
959 // Check that the image by ServeOneByOneImage is really served. | 861 // Check that the image by ServeOneByOneImage is really served. |
960 EXPECT_EQ(1, image.Width()); | 862 EXPECT_EQ(1, image.Width()); |
961 } | 863 } |
962 | 864 |
963 TEST_F(NTPSnippetsServiceTest, EmptyImageReturnedForNonExistentId) { | 865 TEST_F(NTPSnippetsServiceTest, EmptyImageReturnedForNonExistentId) { |
866 EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)); | |
867 auto service = MakeSnippetsService(); | |
868 | |
964 // Create a non-empty image so that we can test the image gets updated. | 869 // Create a non-empty image so that we can test the image gets updated. |
965 gfx::Image image = gfx::test::CreateImage(1, 1); | 870 gfx::Image image = gfx::test::CreateImage(1, 1); |
966 testing::MockFunction<void(const gfx::Image&)> image_fetched; | 871 MockFunction<void(const gfx::Image&)> image_fetched; |
967 EXPECT_CALL(image_fetched, Call(_)).WillOnce(testing::SaveArg<0>(&image)); | 872 EXPECT_CALL(image_fetched, Call(_)) |
873 .WillOnce(SaveArg<0>(&image)); | |
968 | 874 |
969 service()->FetchSuggestionImage( | 875 service->FetchSuggestionImage( |
970 MakeUniqueID(kSnippetUrl2), | 876 MakeUniqueID(*service, kSnippetUrl2), |
971 base::Bind(&testing::MockFunction<void(const gfx::Image&)>::Call, | 877 base::Bind( |
972 base::Unretained(&image_fetched))); | 878 &MockFunction<void(const gfx::Image&)>::Call, |
879 base::Unretained(&image_fetched))); | |
973 | 880 |
974 base::RunLoop().RunUntilIdle(); | 881 base::RunLoop().RunUntilIdle(); |
975 EXPECT_TRUE(image.IsEmpty()); | 882 EXPECT_TRUE(image.IsEmpty()); |
976 } | 883 } |
977 | 884 |
978 } // namespace ntp_snippets | 885 } // namespace ntp_snippets |
OLD | NEW |