1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/autocomplete/zero_suggest_provider.h"
7 #include "base/metrics/field_trial.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/run_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
12 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
13 #include "chrome/browser/history/top_sites_factory.h"
14 #include "chrome/browser/search_engines/template_url_service_factory.h"
15 #include "chrome/common/pref_names.h"
16 #include "chrome/test/base/testing_profile.h"
17 #include "components/history/core/browser/top_sites.h"
18 #include "components/metrics/proto/omnibox_event.pb.h"
19 #include "components/omnibox/autocomplete_provider_listener.h"
20 #include "components/omnibox/omnibox_field_trial.h"
21 #include "components/search_engines/template_url.h"
22 #include "components/search_engines/template_url_service.h"
23 #include "components/variations/entropy_provider.h"
24 #include "components/variations/variations_associated_data.h"
25 #include "content/public/test/test_browser_thread_bundle.h"
26 #include "net/url_request/test_url_fetcher_factory.h"
27 #include "testing/gtest/include/gtest/gtest.h"
30 class FakeEmptyTopSites
: public history::TopSites
{
36 bool SetPageThumbnail(const GURL
& url
, const gfx::Image
& thumbnail
,
37 const ThumbnailScore
& score
) override
{
40 bool SetPageThumbnailToJPEGBytes(const GURL
& url
,
41 const base::RefCountedMemory
* memory
,
42 const ThumbnailScore
& score
) override
{
45 void GetMostVisitedURLs(const GetMostVisitedURLsCallback
& callback
,
46 bool include_forced_urls
) override
;
47 bool GetPageThumbnail(const GURL
& url
, bool prefix_match
,
48 scoped_refptr
<base::RefCountedMemory
>* bytes
) override
{
51 bool GetPageThumbnailScore(const GURL
& url
, ThumbnailScore
* score
) override
{
54 bool GetTemporaryPageThumbnailScore(const GURL
& url
, ThumbnailScore
* score
)
58 void SyncWithHistory() override
{}
59 bool HasBlacklistedItems() const override
{
62 void AddBlacklistedURL(const GURL
& url
) override
{}
63 void RemoveBlacklistedURL(const GURL
& url
) override
{}
64 bool IsBlacklisted(const GURL
& url
) override
{
67 void ClearBlacklistedURLs() override
{}
68 base::CancelableTaskTracker::TaskId
StartQueryForMostVisited() override
{
71 bool IsKnownURL(const GURL
& url
) override
{
74 const std::string
& GetCanonicalURLString(const GURL
& url
) const override
{
76 return *(new std::string());
78 bool IsNonForcedFull() override
{
81 bool IsForcedFull() override
{
84 bool loaded() const override
{
87 history::PrepopulatedPageList
GetPrepopulatedPages() override
{
88 return history::PrepopulatedPageList();
90 bool AddForcedURL(const GURL
& url
, const base::Time
& time
) override
{
94 // RefcountedKeyedService:
95 void ShutdownOnUIThread() override
{}
97 // A test-specific field for controlling when most visited callback is run
98 // after top sites have been requested.
99 GetMostVisitedURLsCallback mv_callback
;
102 ~FakeEmptyTopSites() override
{}
105 void FakeEmptyTopSites::GetMostVisitedURLs(
106 const GetMostVisitedURLsCallback
& callback
,
107 bool include_forced_urls
) {
108 mv_callback
= callback
;
111 scoped_refptr
<RefcountedKeyedService
> BuildFakeEmptyTopSites(
112 content::BrowserContext
* profile
) {
113 scoped_refptr
<history::TopSites
> top_sites
= new FakeEmptyTopSites();
120 class ZeroSuggestProviderTest
: public testing::Test
,
121 public AutocompleteProviderListener
{
123 ZeroSuggestProviderTest();
125 void SetUp() override
;
126 void TearDown() override
;
129 // AutocompleteProviderListener:
130 void OnProviderUpdate(bool updated_matches
) override
;
132 void ResetFieldTrialList();
134 void CreatePersonalizedFieldTrial();
135 void CreateMostVisitedFieldTrial();
137 // Set up threads for testing; this needs to be instantiated before
139 content::TestBrowserThreadBundle thread_bundle_
;
141 // Needed for OmniboxFieldTrial::ActivateStaticTrials().
142 scoped_ptr
<base::FieldTrialList
> field_trial_list_
;
144 // URLFetcherFactory implementation registered.
145 net::TestURLFetcherFactory test_factory_
;
148 TestingProfile profile_
;
150 // ZeroSuggestProvider object under test.
151 scoped_refptr
<ZeroSuggestProvider
> provider_
;
153 // Default template URL.
154 TemplateURL
* default_t_url_
;
157 ZeroSuggestProviderTest::ZeroSuggestProviderTest() {
158 ResetFieldTrialList();
161 void ZeroSuggestProviderTest::SetUp() {
162 // Make sure that fetchers are automatically ungregistered upon destruction.
163 test_factory_
.set_remove_fetcher_on_delete(true);
164 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
165 &profile_
, &TemplateURLServiceFactory::BuildInstanceFor
);
166 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
167 &profile_
, &AutocompleteClassifierFactory::BuildInstanceFor
);
169 TemplateURLService
* turl_model
=
170 TemplateURLServiceFactory::GetForProfile(&profile_
);
173 TemplateURLData data
;
174 data
.short_name
= base::ASCIIToUTF16("t");
175 data
.SetURL("https://www.google.com/?q={searchTerms}");
176 data
.suggestions_url
= "https://www.google.com/complete/?q={searchTerms}";
177 data
.instant_url
= "https://does/not/exist?strk=1";
178 data
.search_terms_replacement_key
= "strk";
179 default_t_url_
= new TemplateURL(data
);
180 turl_model
->Add(default_t_url_
);
181 turl_model
->SetUserSelectedDefaultSearchProvider(default_t_url_
);
183 profile_
.DestroyTopSites();
184 TopSitesFactory::GetInstance()->SetTestingFactory(&profile_
,
185 BuildFakeEmptyTopSites
);
186 provider_
= ZeroSuggestProvider::Create(this, turl_model
, &profile_
);
189 void ZeroSuggestProviderTest::TearDown() {
190 // Shutdown the provider before the profile.
194 void ZeroSuggestProviderTest::OnProviderUpdate(bool updated_matches
) {
197 void ZeroSuggestProviderTest::ResetFieldTrialList() {
198 // Destroy the existing FieldTrialList before creating a new one to avoid
200 field_trial_list_
.reset();
201 field_trial_list_
.reset(new base::FieldTrialList(
202 new metrics::SHA1EntropyProvider("foo")));
203 variations::testing::ClearAllVariationParams();
206 void ZeroSuggestProviderTest::CreatePersonalizedFieldTrial() {
207 std::map
<std::string
, std::string
> params
;
208 params
[std::string(OmniboxFieldTrial::kZeroSuggestRule
)] = "true";
209 params
[std::string(OmniboxFieldTrial::kZeroSuggestVariantRule
)] =
211 variations::AssociateVariationParams(
212 OmniboxFieldTrial::kBundledExperimentFieldTrialName
, "A", params
);
213 base::FieldTrialList::CreateFieldTrial(
214 OmniboxFieldTrial::kBundledExperimentFieldTrialName
, "A");
217 void ZeroSuggestProviderTest::CreateMostVisitedFieldTrial() {
218 std::map
<std::string
, std::string
> params
;
219 params
[std::string(OmniboxFieldTrial::kZeroSuggestRule
)] = "true";
220 params
[std::string(OmniboxFieldTrial::kZeroSuggestVariantRule
)] =
221 "MostVisitedWithoutSERP";
222 variations::AssociateVariationParams(
223 OmniboxFieldTrial::kBundledExperimentFieldTrialName
, "A", params
);
224 base::FieldTrialList::CreateFieldTrial(
225 OmniboxFieldTrial::kBundledExperimentFieldTrialName
, "A");
228 TEST_F(ZeroSuggestProviderTest
, TestDoesNotReturnMatchesForPrefix
) {
229 CreatePersonalizedFieldTrial();
231 std::string
url("http://www.cnn.com/");
232 AutocompleteInput
input(base::ASCIIToUTF16(url
), base::string16::npos
,
233 std::string(), GURL(url
),
234 metrics::OmniboxEventProto::INVALID_SPEC
, true, false,
236 ChromeAutocompleteSchemeClassifier(&profile_
));
238 // Set up the pref to cache the response from the previous run.
239 std::string
json_response("[\"\",[\"search1\", \"search2\", \"search3\"],"
240 "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
241 "\"google:verbatimrelevance\":1300}]");
242 PrefService
* prefs
= profile_
.GetPrefs();
243 prefs
->SetString(prefs::kZeroSuggestCachedResults
, json_response
);
245 provider_
->Start(input
, false, false);
247 // Expect that matches don't get populated out of cache because we are not
248 // in zero suggest mode.
249 EXPECT_TRUE(provider_
->matches().empty());
251 // Expect that fetcher did not get created.
252 net::TestURLFetcher
* fetcher
= test_factory_
.GetFetcherByID(1);
253 EXPECT_FALSE(fetcher
);
256 TEST_F(ZeroSuggestProviderTest
, TestMostVisitedCallback
) {
257 CreateMostVisitedFieldTrial();
259 std::string
current_url("http://www.foxnews.com/");
260 std::string
input_url("http://www.cnn.com/");
261 AutocompleteInput
input(base::ASCIIToUTF16(input_url
), base::string16::npos
,
262 std::string(), GURL(current_url
),
263 metrics::OmniboxEventProto::OTHER
, false, false,
265 ChromeAutocompleteSchemeClassifier(&profile_
));
266 history::MostVisitedURLList urls
;
267 history::MostVisitedURL
url(GURL("http://foo.com/"),
268 base::ASCIIToUTF16("Foo"));
271 provider_
->Start(input
, false, true);
272 EXPECT_TRUE(provider_
->matches().empty());
273 scoped_refptr
<history::TopSites
> top_sites
=
274 TopSitesFactory::GetForProfile(&profile_
);
275 static_cast<FakeEmptyTopSites
*>(top_sites
.get())->mv_callback
.Run(urls
);
276 // Should have verbatim match + most visited url match.
277 EXPECT_EQ(2U, provider_
->matches().size());
278 provider_
->Stop(false, false);
280 provider_
->Start(input
, false, true);
281 provider_
->Stop(false, false);
282 EXPECT_TRUE(provider_
->matches().empty());
283 // Most visited results arriving after Stop() has been called, ensure they
284 // are not displayed.
285 static_cast<FakeEmptyTopSites
*>(top_sites
.get())->mv_callback
.Run(urls
);
286 EXPECT_TRUE(provider_
->matches().empty());
289 TEST_F(ZeroSuggestProviderTest
, TestMostVisitedNavigateToSearchPage
) {
290 CreateMostVisitedFieldTrial();
292 std::string
current_url("http://www.foxnews.com/");
293 std::string
input_url("http://www.cnn.com/");
294 AutocompleteInput
input(base::ASCIIToUTF16(input_url
), base::string16::npos
,
295 std::string(), GURL(current_url
),
296 metrics::OmniboxEventProto::OTHER
, false, false,
298 ChromeAutocompleteSchemeClassifier(&profile_
));
299 history::MostVisitedURLList urls
;
300 history::MostVisitedURL
url(GURL("http://foo.com/"),
301 base::ASCIIToUTF16("Foo"));
304 provider_
->Start(input
, false, true);
305 EXPECT_TRUE(provider_
->matches().empty());
306 // Stop() doesn't always get called.
308 std::string
search_url("https://www.google.com/?q=flowers");
309 AutocompleteInput
srp_input(
310 base::ASCIIToUTF16(search_url
),
311 base::string16::npos
,
314 metrics::OmniboxEventProto::
315 SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT
,
320 ChromeAutocompleteSchemeClassifier(&profile_
));
322 provider_
->Start(srp_input
, false, true);
323 EXPECT_TRUE(provider_
->matches().empty());
324 // Most visited results arriving after a new request has been started.
325 scoped_refptr
<history::TopSites
> top_sites
=
326 TopSitesFactory::GetForProfile(&profile_
);
327 static_cast<FakeEmptyTopSites
*>(top_sites
.get())->mv_callback
.Run(urls
);
328 EXPECT_TRUE(provider_
->matches().empty());
331 TEST_F(ZeroSuggestProviderTest
, TestPsuggestZeroSuggestCachingFirstRun
) {
332 CreatePersonalizedFieldTrial();
334 // Ensure the cache is empty.
335 PrefService
* prefs
= profile_
.GetPrefs();
336 prefs
->SetString(prefs::kZeroSuggestCachedResults
, std::string());
338 std::string
url("http://www.cnn.com/");
339 AutocompleteInput
input(base::ASCIIToUTF16(url
), base::string16::npos
,
340 std::string(), GURL(url
),
341 metrics::OmniboxEventProto::INVALID_SPEC
, true, false,
343 ChromeAutocompleteSchemeClassifier(&profile_
));
345 provider_
->Start(input
, false, true);
347 EXPECT_TRUE(prefs
->GetString(prefs::kZeroSuggestCachedResults
).empty());
348 EXPECT_TRUE(provider_
->matches().empty());
350 net::TestURLFetcher
* fetcher
= test_factory_
.GetFetcherByID(1);
351 ASSERT_TRUE(fetcher
);
352 fetcher
->set_response_code(200);
353 std::string
json_response("[\"\",[\"search1\", \"search2\", \"search3\"],"
354 "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
355 "\"google:verbatimrelevance\":1300}]");
356 fetcher
->SetResponseString(json_response
);
357 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
359 base::RunLoop().RunUntilIdle();
361 EXPECT_EQ(4U, provider_
->matches().size()); // 3 results + verbatim
362 EXPECT_EQ(json_response
, prefs
->GetString(prefs::kZeroSuggestCachedResults
));
365 TEST_F(ZeroSuggestProviderTest
, TestPsuggestZeroSuggestHasCachedResults
) {
366 CreatePersonalizedFieldTrial();
368 std::string
url("http://www.cnn.com/");
369 AutocompleteInput
input(base::ASCIIToUTF16(url
), base::string16::npos
,
370 std::string(), GURL(url
),
371 metrics::OmniboxEventProto::INVALID_SPEC
, true, false,
373 ChromeAutocompleteSchemeClassifier(&profile_
));
375 // Set up the pref to cache the response from the previous run.
376 std::string
json_response("[\"\",[\"search1\", \"search2\", \"search3\"],"
377 "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
378 "\"google:verbatimrelevance\":1300}]");
379 PrefService
* prefs
= profile_
.GetPrefs();
380 prefs
->SetString(prefs::kZeroSuggestCachedResults
, json_response
);
382 provider_
->Start(input
, false, true);
384 // Expect that matches get populated synchronously out of the cache.
385 ASSERT_EQ(4U, provider_
->matches().size());
386 EXPECT_EQ(base::ASCIIToUTF16("search1"), provider_
->matches()[1].contents
);
387 EXPECT_EQ(base::ASCIIToUTF16("search2"), provider_
->matches()[2].contents
);
388 EXPECT_EQ(base::ASCIIToUTF16("search3"), provider_
->matches()[3].contents
);
390 net::TestURLFetcher
* fetcher
= test_factory_
.GetFetcherByID(1);
391 ASSERT_TRUE(fetcher
);
392 fetcher
->set_response_code(200);
393 std::string
json_response2("[\"\",[\"search4\", \"search5\", \"search6\"],"
394 "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
395 "\"google:verbatimrelevance\":1300}]");
396 fetcher
->SetResponseString(json_response2
);
397 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
399 base::RunLoop().RunUntilIdle();
401 // Expect the same 4 results after the response has been handled.
402 ASSERT_EQ(4U, provider_
->matches().size());
403 EXPECT_EQ(base::ASCIIToUTF16("search1"), provider_
->matches()[1].contents
);
404 EXPECT_EQ(base::ASCIIToUTF16("search2"), provider_
->matches()[2].contents
);
405 EXPECT_EQ(base::ASCIIToUTF16("search3"), provider_
->matches()[3].contents
);
407 // Expect the new results have been stored.
408 EXPECT_EQ(json_response2
,
409 prefs
->GetString(prefs::kZeroSuggestCachedResults
));
412 TEST_F(ZeroSuggestProviderTest
, TestPsuggestZeroSuggestReceivedEmptyResults
) {
413 CreatePersonalizedFieldTrial();
415 std::string
url("http://www.cnn.com/");
416 AutocompleteInput
input(base::ASCIIToUTF16(url
), base::string16::npos
,
417 std::string(), GURL(url
),
418 metrics::OmniboxEventProto::INVALID_SPEC
, true, false,
420 ChromeAutocompleteSchemeClassifier(&profile_
));
422 // Set up the pref to cache the response from the previous run.
423 std::string
json_response("[\"\",[\"search1\", \"search2\", \"search3\"],"
424 "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
425 "\"google:verbatimrelevance\":1300}]");
426 PrefService
* prefs
= profile_
.GetPrefs();
427 prefs
->SetString(prefs::kZeroSuggestCachedResults
, json_response
);
429 provider_
->Start(input
, false, true);
431 // Expect that matches get populated synchronously out of the cache.
432 ASSERT_EQ(4U, provider_
->matches().size());
433 EXPECT_EQ(base::ASCIIToUTF16("search1"), provider_
->matches()[1].contents
);
434 EXPECT_EQ(base::ASCIIToUTF16("search2"), provider_
->matches()[2].contents
);
435 EXPECT_EQ(base::ASCIIToUTF16("search3"), provider_
->matches()[3].contents
);
437 net::TestURLFetcher
* fetcher
= test_factory_
.GetFetcherByID(1);
438 ASSERT_TRUE(fetcher
);
439 fetcher
->set_response_code(200);
440 std::string
empty_response("[\"\",[],[],[],{}]");
441 fetcher
->SetResponseString(empty_response
);
442 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
444 base::RunLoop().RunUntilIdle();
446 // Expect that the matches have been cleared.
447 ASSERT_TRUE(provider_
->matches().empty());
449 // Expect the new results have been stored.
450 EXPECT_EQ(empty_response
,
451 prefs
->GetString(prefs::kZeroSuggestCachedResults
));