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

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

Powered by Google App Engine
This is Rietveld 408576698