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

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

Issue 2285133004: Overhaul ntp_snippets_service_unittest.cc. (Closed) Base URL: https://chromium.googlesource.com/chromium/src@master
Patch Set: merge Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/ntp_snippets/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
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
OLDNEW
« no previous file with comments | « components/ntp_snippets/ntp_snippet_unittest.cc ('k') | components/ntp_snippets/ntp_snippets_status_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698