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 "components/omnibox/browser/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_provider_client.h"
13 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
14 #include "chrome/browser/history/top_sites_factory.h"
15 #include "chrome/browser/search_engines/template_url_service_factory.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "components/history/core/browser/top_sites.h"
19 #include "components/metrics/proto/omnibox_event.pb.h"
20 #include "components/omnibox/browser/autocomplete_provider_listener.h"
21 #include "components/omnibox/browser/omnibox_field_trial.h"
22 #include "components/omnibox/browser/omnibox_pref_names.h"
23 #include "components/search_engines/template_url.h"
24 #include "components/search_engines/template_url_service.h"
25 #include "components/variations/entropy_provider.h"
26 #include "components/variations/variations_associated_data.h"
27 #include "content/public/test/test_browser_thread_bundle.h"
28 #include "net/url_request/test_url_fetcher_factory.h"
29 #include "testing/gtest/include/gtest/gtest.h"
32 class FakeEmptyTopSites
: public history::TopSites
{
38 bool SetPageThumbnail(const GURL
& url
, const gfx::Image
& thumbnail
,
39 const ThumbnailScore
& score
) override
{
42 bool SetPageThumbnailToJPEGBytes(const GURL
& url
,
43 const base::RefCountedMemory
* memory
,
44 const ThumbnailScore
& score
) override
{
47 void GetMostVisitedURLs(const GetMostVisitedURLsCallback
& callback
,
48 bool include_forced_urls
) override
;
49 bool GetPageThumbnail(const GURL
& url
, bool prefix_match
,
50 scoped_refptr
<base::RefCountedMemory
>* bytes
) override
{
53 bool GetPageThumbnailScore(const GURL
& url
, ThumbnailScore
* score
) override
{
56 bool GetTemporaryPageThumbnailScore(const GURL
& url
, ThumbnailScore
* score
)
60 void SyncWithHistory() override
{}
61 bool HasBlacklistedItems() const override
{
64 void AddBlacklistedURL(const GURL
& url
) override
{}
65 void RemoveBlacklistedURL(const GURL
& url
) override
{}
66 bool IsBlacklisted(const GURL
& url
) override
{
69 void ClearBlacklistedURLs() override
{}
70 base::CancelableTaskTracker::TaskId
StartQueryForMostVisited() override
{
73 bool IsKnownURL(const GURL
& url
) override
{
76 const std::string
& GetCanonicalURLString(const GURL
& url
) const override
{
78 return *(new std::string());
80 bool IsNonForcedFull() override
{
83 bool IsForcedFull() override
{
86 bool loaded() const override
{
89 history::PrepopulatedPageList
GetPrepopulatedPages() override
{
90 return history::PrepopulatedPageList();
92 bool AddForcedURL(const GURL
& url
, const base::Time
& time
) override
{
95 void OnNavigationCommitted(const GURL
& url
) override
{}
97 // RefcountedKeyedService:
98 void ShutdownOnUIThread() override
{}
100 // A test-specific field for controlling when most visited callback is run
101 // after top sites have been requested.
102 GetMostVisitedURLsCallback mv_callback
;
105 ~FakeEmptyTopSites() override
{}
108 void FakeEmptyTopSites::GetMostVisitedURLs(
109 const GetMostVisitedURLsCallback
& callback
,
110 bool include_forced_urls
) {
111 mv_callback
= callback
;
114 scoped_refptr
<RefcountedKeyedService
> BuildFakeEmptyTopSites(
115 content::BrowserContext
* profile
) {
116 scoped_refptr
<history::TopSites
> top_sites
= new FakeEmptyTopSites();
123 class ZeroSuggestProviderTest
: public testing::Test
,
124 public AutocompleteProviderListener
{
126 ZeroSuggestProviderTest();
128 void SetUp() override
;
129 void TearDown() override
;
132 // AutocompleteProviderListener:
133 void OnProviderUpdate(bool updated_matches
) override
;
135 void ResetFieldTrialList();
137 void CreatePersonalizedFieldTrial();
138 void CreateMostVisitedFieldTrial();
140 // Set up threads for testing; this needs to be instantiated before
142 content::TestBrowserThreadBundle thread_bundle_
;
144 // Needed for OmniboxFieldTrial::ActivateStaticTrials().
145 scoped_ptr
<base::FieldTrialList
> field_trial_list_
;
147 net::TestURLFetcherFactory test_factory_
;
148 TestingProfile profile_
;
149 scoped_ptr
<ChromeAutocompleteProviderClient
> client_
;
150 scoped_refptr
<ZeroSuggestProvider
> provider_
;
151 TemplateURL
* default_t_url_
;
154 ZeroSuggestProviderTest::ZeroSuggestProviderTest() {
155 ResetFieldTrialList();
158 void ZeroSuggestProviderTest::SetUp() {
159 // Make sure that fetchers are automatically ungregistered upon destruction.
160 test_factory_
.set_remove_fetcher_on_delete(true);
161 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
162 &profile_
, &TemplateURLServiceFactory::BuildInstanceFor
);
163 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
164 &profile_
, &AutocompleteClassifierFactory::BuildInstanceFor
);
166 client_
.reset(new ChromeAutocompleteProviderClient(&profile_
));
168 TemplateURLService
* turl_model
=
169 TemplateURLServiceFactory::GetForProfile(&profile_
);
172 TemplateURLData data
;
173 data
.SetShortName(base::ASCIIToUTF16("t"));
174 data
.SetURL("https://www.google.com/?q={searchTerms}");
175 data
.suggestions_url
= "https://www.google.com/complete/?q={searchTerms}";
176 data
.instant_url
= "https://does/not/exist?strk=1";
177 data
.search_terms_replacement_key
= "strk";
178 default_t_url_
= new TemplateURL(data
);
179 turl_model
->Add(default_t_url_
);
180 turl_model
->SetUserSelectedDefaultSearchProvider(default_t_url_
);
182 TopSitesFactory
* top_sites_factory
= TopSitesFactory::GetInstance();
183 top_sites_factory
->SetTestingFactory(&profile_
, BuildFakeEmptyTopSites
);
184 provider_
= ZeroSuggestProvider::Create(client_
.get(), this);
187 void ZeroSuggestProviderTest::TearDown() {
188 // Shutdown the provider before the profile.
192 void ZeroSuggestProviderTest::OnProviderUpdate(bool updated_matches
) {
195 void ZeroSuggestProviderTest::ResetFieldTrialList() {
196 // Destroy the existing FieldTrialList before creating a new one to avoid
198 field_trial_list_
.reset();
199 field_trial_list_
.reset(new base::FieldTrialList(
200 new metrics::SHA1EntropyProvider("foo")));
201 variations::testing::ClearAllVariationParams();
204 void ZeroSuggestProviderTest::CreatePersonalizedFieldTrial() {
205 std::map
<std::string
, std::string
> params
;
206 params
[std::string(OmniboxFieldTrial::kZeroSuggestRule
)] = "true";
207 params
[std::string(OmniboxFieldTrial::kZeroSuggestVariantRule
)] =
209 variations::AssociateVariationParams(
210 OmniboxFieldTrial::kBundledExperimentFieldTrialName
, "A", params
);
211 base::FieldTrialList::CreateFieldTrial(
212 OmniboxFieldTrial::kBundledExperimentFieldTrialName
, "A");
215 void ZeroSuggestProviderTest::CreateMostVisitedFieldTrial() {
216 std::map
<std::string
, std::string
> params
;
217 params
[std::string(OmniboxFieldTrial::kZeroSuggestRule
)] = "true";
218 params
[std::string(OmniboxFieldTrial::kZeroSuggestVariantRule
)] =
219 "MostVisitedWithoutSERP";
220 variations::AssociateVariationParams(
221 OmniboxFieldTrial::kBundledExperimentFieldTrialName
, "A", params
);
222 base::FieldTrialList::CreateFieldTrial(
223 OmniboxFieldTrial::kBundledExperimentFieldTrialName
, "A");
226 TEST_F(ZeroSuggestProviderTest
, TestDoesNotReturnMatchesForPrefix
) {
227 CreatePersonalizedFieldTrial();
229 std::string
url("http://www.cnn.com/");
230 AutocompleteInput
input(
231 base::ASCIIToUTF16(url
), base::string16::npos
, std::string(), GURL(url
),
232 metrics::OmniboxEventProto::INVALID_SPEC
, true, false, true, true, false,
233 ChromeAutocompleteSchemeClassifier(&profile_
));
235 // Set up the pref to cache the response from the previous run.
236 std::string
json_response("[\"\",[\"search1\", \"search2\", \"search3\"],"
237 "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
238 "\"google:verbatimrelevance\":1300}]");
239 PrefService
* prefs
= profile_
.GetPrefs();
240 prefs
->SetString(omnibox::kZeroSuggestCachedResults
, json_response
);
242 provider_
->Start(input
, false);
244 // Expect that matches don't get populated out of cache because we are not
245 // in zero suggest mode.
246 EXPECT_TRUE(provider_
->matches().empty());
248 // Expect that fetcher did not get created.
249 net::TestURLFetcher
* fetcher
= test_factory_
.GetFetcherByID(1);
250 EXPECT_FALSE(fetcher
);
253 TEST_F(ZeroSuggestProviderTest
, TestMostVisitedCallback
) {
254 CreateMostVisitedFieldTrial();
256 std::string
current_url("http://www.foxnews.com/");
257 std::string
input_url("http://www.cnn.com/");
258 AutocompleteInput
input(
259 base::ASCIIToUTF16(input_url
), base::string16::npos
, std::string(),
260 GURL(current_url
), metrics::OmniboxEventProto::OTHER
, false, false, true,
261 true, true, ChromeAutocompleteSchemeClassifier(&profile_
));
262 history::MostVisitedURLList urls
;
263 history::MostVisitedURL
url(GURL("http://foo.com/"),
264 base::ASCIIToUTF16("Foo"));
267 provider_
->Start(input
, false);
268 EXPECT_TRUE(provider_
->matches().empty());
269 scoped_refptr
<history::TopSites
> top_sites
=
270 TopSitesFactory::GetForProfile(&profile_
);
271 static_cast<FakeEmptyTopSites
*>(top_sites
.get())->mv_callback
.Run(urls
);
272 // Should have verbatim match + most visited url match.
273 EXPECT_EQ(2U, provider_
->matches().size());
274 provider_
->Stop(false, false);
276 provider_
->Start(input
, false);
277 provider_
->Stop(false, false);
278 EXPECT_TRUE(provider_
->matches().empty());
279 // Most visited results arriving after Stop() has been called, ensure they
280 // are not displayed.
281 static_cast<FakeEmptyTopSites
*>(top_sites
.get())->mv_callback
.Run(urls
);
282 EXPECT_TRUE(provider_
->matches().empty());
285 TEST_F(ZeroSuggestProviderTest
, TestMostVisitedNavigateToSearchPage
) {
286 CreateMostVisitedFieldTrial();
288 std::string
current_url("http://www.foxnews.com/");
289 std::string
input_url("http://www.cnn.com/");
290 AutocompleteInput
input(
291 base::ASCIIToUTF16(input_url
), base::string16::npos
, std::string(),
292 GURL(current_url
), metrics::OmniboxEventProto::OTHER
, false, false, true,
293 true, true, ChromeAutocompleteSchemeClassifier(&profile_
));
294 history::MostVisitedURLList urls
;
295 history::MostVisitedURL
url(GURL("http://foo.com/"),
296 base::ASCIIToUTF16("Foo"));
299 provider_
->Start(input
, false);
300 EXPECT_TRUE(provider_
->matches().empty());
301 // Stop() doesn't always get called.
303 std::string
search_url("https://www.google.com/?q=flowers");
304 AutocompleteInput
srp_input(
305 base::ASCIIToUTF16(search_url
), base::string16::npos
, std::string(),
306 GURL(search_url
), metrics::OmniboxEventProto::
307 SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT
,
308 false, false, true, true, true,
309 ChromeAutocompleteSchemeClassifier(&profile_
));
311 provider_
->Start(srp_input
, false);
312 EXPECT_TRUE(provider_
->matches().empty());
313 // Most visited results arriving after a new request has been started.
314 scoped_refptr
<history::TopSites
> top_sites
=
315 TopSitesFactory::GetForProfile(&profile_
);
316 static_cast<FakeEmptyTopSites
*>(top_sites
.get())->mv_callback
.Run(urls
);
317 EXPECT_TRUE(provider_
->matches().empty());
320 TEST_F(ZeroSuggestProviderTest
, TestPsuggestZeroSuggestCachingFirstRun
) {
321 CreatePersonalizedFieldTrial();
323 // Ensure the cache is empty.
324 PrefService
* prefs
= profile_
.GetPrefs();
325 prefs
->SetString(omnibox::kZeroSuggestCachedResults
, std::string());
327 std::string
url("http://www.cnn.com/");
328 AutocompleteInput
input(
329 base::ASCIIToUTF16(url
), base::string16::npos
, std::string(), GURL(url
),
330 metrics::OmniboxEventProto::INVALID_SPEC
, true, false, true, true, true,
331 ChromeAutocompleteSchemeClassifier(&profile_
));
333 provider_
->Start(input
, false);
335 EXPECT_TRUE(prefs
->GetString(omnibox::kZeroSuggestCachedResults
).empty());
336 EXPECT_TRUE(provider_
->matches().empty());
338 net::TestURLFetcher
* fetcher
= test_factory_
.GetFetcherByID(1);
339 ASSERT_TRUE(fetcher
);
340 fetcher
->set_response_code(200);
341 std::string
json_response("[\"\",[\"search1\", \"search2\", \"search3\"],"
342 "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
343 "\"google:verbatimrelevance\":1300}]");
344 fetcher
->SetResponseString(json_response
);
345 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
347 base::RunLoop().RunUntilIdle();
349 EXPECT_EQ(4U, provider_
->matches().size()); // 3 results + verbatim
350 EXPECT_EQ(json_response
,
351 prefs
->GetString(omnibox::kZeroSuggestCachedResults
));
354 TEST_F(ZeroSuggestProviderTest
, TestPsuggestZeroSuggestHasCachedResults
) {
355 CreatePersonalizedFieldTrial();
357 std::string
url("http://www.cnn.com/");
358 AutocompleteInput
input(
359 base::ASCIIToUTF16(url
), base::string16::npos
, std::string(), GURL(url
),
360 metrics::OmniboxEventProto::INVALID_SPEC
, true, false, true, true, true,
361 ChromeAutocompleteSchemeClassifier(&profile_
));
363 // Set up the pref to cache the response from the previous run.
364 std::string
json_response("[\"\",[\"search1\", \"search2\", \"search3\"],"
365 "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
366 "\"google:verbatimrelevance\":1300}]");
367 PrefService
* prefs
= profile_
.GetPrefs();
368 prefs
->SetString(omnibox::kZeroSuggestCachedResults
, json_response
);
370 provider_
->Start(input
, false);
372 // Expect that matches get populated synchronously out of the cache.
373 ASSERT_EQ(4U, provider_
->matches().size());
374 EXPECT_EQ(base::ASCIIToUTF16("search1"), provider_
->matches()[1].contents
);
375 EXPECT_EQ(base::ASCIIToUTF16("search2"), provider_
->matches()[2].contents
);
376 EXPECT_EQ(base::ASCIIToUTF16("search3"), provider_
->matches()[3].contents
);
378 net::TestURLFetcher
* fetcher
= test_factory_
.GetFetcherByID(1);
379 ASSERT_TRUE(fetcher
);
380 fetcher
->set_response_code(200);
381 std::string
json_response2("[\"\",[\"search4\", \"search5\", \"search6\"],"
382 "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
383 "\"google:verbatimrelevance\":1300}]");
384 fetcher
->SetResponseString(json_response2
);
385 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
387 base::RunLoop().RunUntilIdle();
389 // Expect the same 4 results after the response has been handled.
390 ASSERT_EQ(4U, provider_
->matches().size());
391 EXPECT_EQ(base::ASCIIToUTF16("search1"), provider_
->matches()[1].contents
);
392 EXPECT_EQ(base::ASCIIToUTF16("search2"), provider_
->matches()[2].contents
);
393 EXPECT_EQ(base::ASCIIToUTF16("search3"), provider_
->matches()[3].contents
);
395 // Expect the new results have been stored.
396 EXPECT_EQ(json_response2
,
397 prefs
->GetString(omnibox::kZeroSuggestCachedResults
));
400 TEST_F(ZeroSuggestProviderTest
, TestPsuggestZeroSuggestReceivedEmptyResults
) {
401 CreatePersonalizedFieldTrial();
403 std::string
url("http://www.cnn.com/");
404 AutocompleteInput
input(
405 base::ASCIIToUTF16(url
), base::string16::npos
, std::string(), GURL(url
),
406 metrics::OmniboxEventProto::INVALID_SPEC
, true, false, true, true, true,
407 ChromeAutocompleteSchemeClassifier(&profile_
));
409 // Set up the pref to cache the response from the previous run.
410 std::string
json_response("[\"\",[\"search1\", \"search2\", \"search3\"],"
411 "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
412 "\"google:verbatimrelevance\":1300}]");
413 PrefService
* prefs
= profile_
.GetPrefs();
414 prefs
->SetString(omnibox::kZeroSuggestCachedResults
, json_response
);
416 provider_
->Start(input
, false);
418 // Expect that matches get populated synchronously out of the cache.
419 ASSERT_EQ(4U, provider_
->matches().size());
420 EXPECT_EQ(base::ASCIIToUTF16("search1"), provider_
->matches()[1].contents
);
421 EXPECT_EQ(base::ASCIIToUTF16("search2"), provider_
->matches()[2].contents
);
422 EXPECT_EQ(base::ASCIIToUTF16("search3"), provider_
->matches()[3].contents
);
424 net::TestURLFetcher
* fetcher
= test_factory_
.GetFetcherByID(1);
425 ASSERT_TRUE(fetcher
);
426 fetcher
->set_response_code(200);
427 std::string
empty_response("[\"\",[],[],[],{}]");
428 fetcher
->SetResponseString(empty_response
);
429 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
431 base::RunLoop().RunUntilIdle();
433 // Expect that the matches have been cleared.
434 ASSERT_TRUE(provider_
->matches().empty());
436 // Expect the new results have been stored.
437 EXPECT_EQ(empty_response
,
438 prefs
->GetString(omnibox::kZeroSuggestCachedResults
));