Revert of Remove OneClickSigninHelper since it is no longer used. (patchset #5 id...
[chromium-blink-merge.git] / chrome / browser / autocomplete / search_provider_unittest.cc
blob1a0c89719c414a1326848ee67742aef90e3d8d5d
1 // Copyright 2012 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/search_provider.h"
7 #include <string>
9 #include "base/command_line.h"
10 #include "base/metrics/field_trial.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/run_loop.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/time/time.h"
18 #include "build/build_config.h"
19 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
20 #include "chrome/browser/autocomplete/autocomplete_controller.h"
21 #include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
22 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
23 #include "chrome/browser/autocomplete/history_url_provider.h"
24 #include "chrome/browser/history/history_service.h"
25 #include "chrome/browser/history/history_service_factory.h"
26 #include "chrome/browser/search_engines/template_url_service_factory.h"
27 #include "chrome/browser/signin/signin_manager_factory.h"
28 #include "chrome/browser/sync/profile_sync_service.h"
29 #include "chrome/browser/sync/profile_sync_service_factory.h"
30 #include "chrome/common/chrome_switches.h"
31 #include "chrome/common/pref_names.h"
32 #include "chrome/test/base/testing_browser_process.h"
33 #include "chrome/test/base/testing_profile.h"
34 #include "components/google/core/browser/google_switches.h"
35 #include "components/metrics/proto/omnibox_event.pb.h"
36 #include "components/omnibox/autocomplete_input.h"
37 #include "components/omnibox/autocomplete_match.h"
38 #include "components/omnibox/autocomplete_provider.h"
39 #include "components/omnibox/autocomplete_provider_listener.h"
40 #include "components/omnibox/omnibox_field_trial.h"
41 #include "components/omnibox/omnibox_switches.h"
42 #include "components/omnibox/suggestion_answer.h"
43 #include "components/search_engines/search_engine_type.h"
44 #include "components/search_engines/search_engines_switches.h"
45 #include "components/search_engines/search_terms_data.h"
46 #include "components/search_engines/template_url.h"
47 #include "components/search_engines/template_url_service.h"
48 #include "components/signin/core/browser/signin_manager.h"
49 #include "components/sync_driver/pref_names.h"
50 #include "components/variations/entropy_provider.h"
51 #include "components/variations/variations_associated_data.h"
52 #include "content/public/test/test_browser_thread_bundle.h"
53 #include "net/url_request/test_url_fetcher_factory.h"
54 #include "net/url_request/url_request_status.h"
55 #include "testing/gtest/include/gtest/gtest.h"
57 using base::ASCIIToUTF16;
59 namespace {
61 // Returns the first match in |matches| with |allowed_to_be_default_match|
62 // set to true.
63 ACMatches::const_iterator FindDefaultMatch(const ACMatches& matches) {
64 ACMatches::const_iterator it = matches.begin();
65 while ((it != matches.end()) && !it->allowed_to_be_default_match)
66 ++it;
67 return it;
70 class SuggestionDeletionHandler;
71 class SearchProviderForTest : public SearchProvider {
72 public:
73 SearchProviderForTest(AutocompleteProviderListener* listener,
74 TemplateURLService* template_url_service,
75 Profile* profile);
76 bool is_success() { return is_success_; }
78 protected:
79 ~SearchProviderForTest() override;
81 private:
82 void RecordDeletionResult(bool success) override;
83 bool is_success_;
84 DISALLOW_COPY_AND_ASSIGN(SearchProviderForTest);
87 SearchProviderForTest::SearchProviderForTest(
88 AutocompleteProviderListener* listener,
89 TemplateURLService* template_url_service,
90 Profile* profile)
91 : SearchProvider(listener, template_url_service,
92 scoped_ptr<AutocompleteProviderClient>(
93 new ChromeAutocompleteProviderClient(profile))),
94 is_success_(false) {
97 SearchProviderForTest::~SearchProviderForTest() {
100 void SearchProviderForTest::RecordDeletionResult(bool success) {
101 is_success_ = success;
104 } // namespace
106 // SearchProviderTest ---------------------------------------------------------
108 // The following environment is configured for these tests:
109 // . The TemplateURL default_t_url_ is set as the default provider.
110 // . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This
111 // TemplateURL has a valid suggest and search URL.
112 // . The URL created by using the search term term1_ with default_t_url_ is
113 // added to history.
114 // . The URL created by using the search term keyword_term_ with keyword_t_url_
115 // is added to history.
116 // . test_factory_ is set as the URLFetcherFactory.
117 class SearchProviderTest : public testing::Test,
118 public AutocompleteProviderListener {
119 public:
120 struct ResultInfo {
121 ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES),
122 allowed_to_be_default_match(false) {
124 ResultInfo(GURL gurl,
125 AutocompleteMatch::Type result_type,
126 bool allowed_to_be_default_match,
127 base::string16 fill_into_edit)
128 : gurl(gurl),
129 result_type(result_type),
130 allowed_to_be_default_match(allowed_to_be_default_match),
131 fill_into_edit(fill_into_edit) {
134 const GURL gurl;
135 const AutocompleteMatch::Type result_type;
136 const bool allowed_to_be_default_match;
137 const base::string16 fill_into_edit;
140 struct TestData {
141 const base::string16 input;
142 const size_t num_results;
143 const ResultInfo output[3];
146 struct ExpectedMatch {
147 std::string contents;
148 bool allowed_to_be_default_match;
151 SearchProviderTest()
152 : default_t_url_(NULL),
153 term1_(ASCIIToUTF16("term1")),
154 keyword_t_url_(NULL),
155 keyword_term_(ASCIIToUTF16("keyword")),
156 run_loop_(NULL) {
157 ResetFieldTrialList();
160 // See description above class for what this registers.
161 void SetUp() override;
162 void TearDown() override;
164 void RunTest(TestData* cases, int num_cases, bool prefer_keyword);
166 protected:
167 // Needed for AutocompleteFieldTrial::ActivateStaticTrials();
168 scoped_ptr<base::FieldTrialList> field_trial_list_;
170 // Default values used for testing.
171 static const std::string kNotApplicable;
172 static const ExpectedMatch kEmptyExpectedMatch;
174 // Adds a search for |term|, using the engine |t_url| to the history, and
175 // returns the URL for that search.
176 GURL AddSearchToHistory(TemplateURL* t_url, base::string16 term, int visit_count);
178 // Looks for a match in |provider_| with |contents| equal to |contents|.
179 // Sets |match| to it if found. Returns whether |match| was set.
180 bool FindMatchWithContents(const base::string16& contents,
181 AutocompleteMatch* match);
183 // Looks for a match in |provider_| with destination |url|. Sets |match| to
184 // it if found. Returns whether |match| was set.
185 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match);
187 // AutocompleteProviderListener:
188 // If we're waiting for the provider to finish, this exits the message loop.
189 void OnProviderUpdate(bool updated_matches) override;
191 // Runs a nested message loop until provider_ is done. The message loop is
192 // exited by way of OnProviderUpdate.
193 void RunTillProviderDone();
195 // Invokes Start on provider_, then runs all pending tasks.
196 void QueryForInput(const base::string16& text,
197 bool prevent_inline_autocomplete,
198 bool prefer_keyword);
200 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is
201 // non-NULL, sets it to the "what you typed" entry for |text|.
202 void QueryForInputAndSetWYTMatch(const base::string16& text,
203 AutocompleteMatch* wyt_match);
205 // Calls QueryForInput(), sets the JSON responses for the default and keyword
206 // fetchers, and waits until the responses have been returned and the matches
207 // returned. Use empty responses for each fetcher that shouldn't be set up /
208 // configured.
209 void QueryForInputAndWaitForFetcherResponses(
210 const base::string16& text,
211 const bool prefer_keyword,
212 const std::string& default_fetcher_response,
213 const std::string& keyword_fetcher_response);
215 // Notifies the URLFetcher for the suggest query corresponding to the default
216 // search provider that it's done.
217 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE.
218 void FinishDefaultSuggestQuery();
220 // Runs SearchProvider on |input|, for which the suggest server replies
221 // with |json|, and expects that the resulting matches' contents equals
222 // that in |matches|. An empty entry in |matches| means no match should
223 // be returned in that position. Reports any errors with a message that
224 // includes |error_description|.
225 void ForcedQueryTestHelper(const std::string& input,
226 const std::string& json,
227 const std::string matches[3],
228 const std::string& error_description);
230 // Verifies that |matches| and |expected_matches| agree on the first
231 // |num_expected_matches|, displaying an error message that includes
232 // |description| for any disagreement.
233 void CheckMatches(const std::string& description,
234 const size_t num_expected_matches,
235 const ExpectedMatch expected_matches[],
236 const ACMatches& matches);
238 void ResetFieldTrialList();
240 // Enable or disable the specified Omnibox field trial rule.
241 base::FieldTrial* CreateFieldTrial(const char* field_trial_rule,
242 bool enabled);
244 void ClearAllResults();
246 // See description above class for details of these fields.
247 TemplateURL* default_t_url_;
248 const base::string16 term1_;
249 GURL term1_url_;
250 TemplateURL* keyword_t_url_;
251 const base::string16 keyword_term_;
252 GURL keyword_url_;
254 content::TestBrowserThreadBundle thread_bundle_;
256 // URLFetcherFactory implementation registered.
257 net::TestURLFetcherFactory test_factory_;
259 // Profile we use.
260 TestingProfile profile_;
262 // The provider.
263 scoped_refptr<SearchProviderForTest> provider_;
265 // If non-NULL, OnProviderUpdate quits the current |run_loop_|.
266 base::RunLoop* run_loop_;
268 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
271 // static
272 const std::string SearchProviderTest::kNotApplicable = "Not Applicable";
273 const SearchProviderTest::ExpectedMatch
274 SearchProviderTest::kEmptyExpectedMatch = { kNotApplicable, false };
276 void SearchProviderTest::SetUp() {
277 // Make sure that fetchers are automatically ungregistered upon destruction.
278 test_factory_.set_remove_fetcher_on_delete(true);
280 // We need both the history service and template url model loaded.
281 ASSERT_TRUE(profile_.CreateHistoryService(true, false));
282 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
283 &profile_, &TemplateURLServiceFactory::BuildInstanceFor);
285 TemplateURLService* turl_model =
286 TemplateURLServiceFactory::GetForProfile(&profile_);
288 turl_model->Load();
290 // Reset the default TemplateURL.
291 TemplateURLData data;
292 data.short_name = ASCIIToUTF16("t");
293 data.SetURL("http://defaultturl/{searchTerms}");
294 data.suggestions_url = "http://defaultturl2/{searchTerms}";
295 data.instant_url = "http://does/not/exist?strk=1";
296 data.search_terms_replacement_key = "strk";
297 default_t_url_ = new TemplateURL(data);
298 turl_model->Add(default_t_url_);
299 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
300 TemplateURLID default_provider_id = default_t_url_->id();
301 ASSERT_NE(0, default_provider_id);
303 // Add url1, with search term term1_.
304 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1);
306 // Create another TemplateURL.
307 data.short_name = ASCIIToUTF16("k");
308 data.SetKeyword(ASCIIToUTF16("k"));
309 data.SetURL("http://keyword/{searchTerms}");
310 data.suggestions_url = "http://suggest_keyword/{searchTerms}";
311 keyword_t_url_ = new TemplateURL(data);
312 turl_model->Add(keyword_t_url_);
313 ASSERT_NE(0, keyword_t_url_->id());
315 // Add a page and search term for keyword_t_url_.
316 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1);
318 // Keywords are updated by the InMemoryHistoryBackend only after the message
319 // has been processed on the history thread. Block until history processes all
320 // requests to ensure the InMemoryDatabase is the state we expect it.
321 profile_.BlockUntilHistoryProcessesPendingRequests();
323 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
324 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor);
326 provider_ = new SearchProviderForTest(this, turl_model, &profile_);
327 OmniboxFieldTrial::kDefaultMinimumTimeBetweenSuggestQueriesMs = 0;
330 void SearchProviderTest::TearDown() {
331 base::RunLoop().RunUntilIdle();
333 // Shutdown the provider before the profile.
334 provider_ = NULL;
337 void SearchProviderTest::RunTest(TestData* cases,
338 int num_cases,
339 bool prefer_keyword) {
340 ACMatches matches;
341 for (int i = 0; i < num_cases; ++i) {
342 AutocompleteInput input(cases[i].input, base::string16::npos,
343 std::string(), GURL(),
344 metrics::OmniboxEventProto::INVALID_SPEC, false,
345 prefer_keyword, true, true,
346 ChromeAutocompleteSchemeClassifier(&profile_));
347 provider_->Start(input, false, false);
348 matches = provider_->matches();
349 SCOPED_TRACE(
350 ASCIIToUTF16("Input was: ") +
351 cases[i].input +
352 ASCIIToUTF16("; prefer_keyword was: ") +
353 (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false")));
354 EXPECT_EQ(cases[i].num_results, matches.size());
355 if (matches.size() == cases[i].num_results) {
356 for (size_t j = 0; j < cases[i].num_results; ++j) {
357 EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url);
358 EXPECT_EQ(cases[i].output[j].result_type, matches[j].type);
359 EXPECT_EQ(cases[i].output[j].fill_into_edit,
360 matches[j].fill_into_edit);
361 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
362 matches[j].allowed_to_be_default_match);
368 void SearchProviderTest::OnProviderUpdate(bool updated_matches) {
369 if (run_loop_ && provider_->done()) {
370 run_loop_->Quit();
371 run_loop_ = NULL;
375 void SearchProviderTest::RunTillProviderDone() {
376 if (provider_->done())
377 return;
379 base::RunLoop run_loop;
380 run_loop_ = &run_loop;
381 run_loop.Run();
384 void SearchProviderTest::QueryForInput(const base::string16& text,
385 bool prevent_inline_autocomplete,
386 bool prefer_keyword) {
387 // Start a query.
388 AutocompleteInput input(text, base::string16::npos, std::string(), GURL(),
389 metrics::OmniboxEventProto::INVALID_SPEC,
390 prevent_inline_autocomplete, prefer_keyword, true,
391 true, ChromeAutocompleteSchemeClassifier(&profile_));
392 provider_->Start(input, false, false);
394 // RunUntilIdle so that the task scheduled by SearchProvider to create the
395 // URLFetchers runs.
396 base::RunLoop().RunUntilIdle();
399 void SearchProviderTest::QueryForInputAndSetWYTMatch(
400 const base::string16& text,
401 AutocompleteMatch* wyt_match) {
402 QueryForInput(text, false, false);
403 profile_.BlockUntilHistoryProcessesPendingRequests();
404 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
405 if (!wyt_match)
406 return;
407 ASSERT_GE(provider_->matches().size(), 1u);
408 EXPECT_TRUE(FindMatchWithDestination(
409 GURL(default_t_url_->url_ref().ReplaceSearchTerms(
410 TemplateURLRef::SearchTermsArgs(base::CollapseWhitespace(
411 text, false)),
412 TemplateURLServiceFactory::GetForProfile(
413 &profile_)->search_terms_data())),
414 wyt_match));
417 void SearchProviderTest::QueryForInputAndWaitForFetcherResponses(
418 const base::string16& text,
419 const bool prefer_keyword,
420 const std::string& default_fetcher_response,
421 const std::string& keyword_fetcher_response) {
422 QueryForInput(text, false, prefer_keyword);
423 if (!default_fetcher_response.empty()) {
424 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
425 SearchProvider::kDefaultProviderURLFetcherID);
426 ASSERT_TRUE(fetcher);
427 fetcher->set_response_code(200);
428 fetcher->SetResponseString(default_fetcher_response);
429 fetcher->delegate()->OnURLFetchComplete(fetcher);
431 if (!keyword_fetcher_response.empty()) {
432 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID(
433 SearchProvider::kKeywordProviderURLFetcherID);
434 ASSERT_TRUE(keyword_fetcher);
435 keyword_fetcher->set_response_code(200);
436 keyword_fetcher->SetResponseString(keyword_fetcher_response);
437 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
439 RunTillProviderDone();
442 GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url,
443 base::string16 term,
444 int visit_count) {
445 HistoryService* history = HistoryServiceFactory::GetForProfile(
446 &profile_, ServiceAccessType::EXPLICIT_ACCESS);
447 GURL search(t_url->url_ref().ReplaceSearchTerms(
448 TemplateURLRef::SearchTermsArgs(term),
449 TemplateURLServiceFactory::GetForProfile(
450 &profile_)->search_terms_data()));
451 static base::Time last_added_time;
452 last_added_time = std::max(base::Time::Now(),
453 last_added_time + base::TimeDelta::FromMicroseconds(1));
454 history->AddPageWithDetails(search, base::string16(), visit_count, visit_count,
455 last_added_time, false, history::SOURCE_BROWSED);
456 history->SetKeywordSearchTermsForURL(search, t_url->id(), term);
457 return search;
460 bool SearchProviderTest::FindMatchWithContents(const base::string16& contents,
461 AutocompleteMatch* match) {
462 for (ACMatches::const_iterator i = provider_->matches().begin();
463 i != provider_->matches().end(); ++i) {
464 if (i->contents == contents) {
465 *match = *i;
466 return true;
469 return false;
472 bool SearchProviderTest::FindMatchWithDestination(const GURL& url,
473 AutocompleteMatch* match) {
474 for (ACMatches::const_iterator i = provider_->matches().begin();
475 i != provider_->matches().end(); ++i) {
476 if (i->destination_url == url) {
477 *match = *i;
478 return true;
481 return false;
484 void SearchProviderTest::FinishDefaultSuggestQuery() {
485 net::TestURLFetcher* default_fetcher =
486 test_factory_.GetFetcherByID(
487 SearchProvider::kDefaultProviderURLFetcherID);
488 ASSERT_TRUE(default_fetcher);
490 // Tell the SearchProvider the default suggest query is done.
491 default_fetcher->set_response_code(200);
492 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
495 void SearchProviderTest::ForcedQueryTestHelper(
496 const std::string& input,
497 const std::string& json,
498 const std::string expected_matches[3],
499 const std::string& error_description) {
500 // Send the query twice in order to have a synchronous pass after the first
501 // response is received. This is necessary because SearchProvider doesn't
502 // allow an asynchronous response to change the default match.
503 for (size_t i = 0; i < 2; ++i) {
504 QueryForInputAndWaitForFetcherResponses(
505 ASCIIToUTF16(input), false, json, std::string());
508 const ACMatches& matches = provider_->matches();
509 ASSERT_LE(matches.size(), 3u);
510 size_t i = 0;
511 // Ensure that the returned matches equal the expectations.
512 for (; i < matches.size(); ++i) {
513 EXPECT_EQ(ASCIIToUTF16(expected_matches[i]), matches[i].contents) <<
514 error_description;
516 // Ensure that no expected matches are missing.
517 for (; i < 3u; ++i) {
518 EXPECT_EQ(std::string(), expected_matches[i]) <<
519 "Case #" << i << ": " << error_description;
523 void SearchProviderTest::CheckMatches(const std::string& description,
524 const size_t num_expected_matches,
525 const ExpectedMatch expected_matches[],
526 const ACMatches& matches) {
527 ASSERT_FALSE(matches.empty());
528 ASSERT_LE(matches.size(), num_expected_matches);
529 size_t i = 0;
530 SCOPED_TRACE(description);
531 // Ensure that the returned matches equal the expectations.
532 for (; i < matches.size(); ++i) {
533 SCOPED_TRACE(" Case # " + base::IntToString(i));
534 EXPECT_EQ(ASCIIToUTF16(expected_matches[i].contents), matches[i].contents);
535 EXPECT_EQ(expected_matches[i].allowed_to_be_default_match,
536 matches[i].allowed_to_be_default_match);
538 // Ensure that no expected matches are missing.
539 for (; i < num_expected_matches; ++i) {
540 SCOPED_TRACE(" Case # " + base::IntToString(i));
541 EXPECT_EQ(kNotApplicable, expected_matches[i].contents);
545 void SearchProviderTest::ResetFieldTrialList() {
546 // Destroy the existing FieldTrialList before creating a new one to avoid
547 // a DCHECK.
548 field_trial_list_.reset();
549 field_trial_list_.reset(new base::FieldTrialList(
550 new metrics::SHA1EntropyProvider("foo")));
551 variations::testing::ClearAllVariationParams();
552 base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
553 "AutocompleteDynamicTrial_0", "DefaultGroup");
554 trial->group();
556 base::FieldTrial* SearchProviderTest::CreateFieldTrial(
557 const char* field_trial_rule,
558 bool enabled) {
559 std::map<std::string, std::string> params;
560 params[std::string(field_trial_rule)] = enabled ?
561 "true" : "false";
562 variations::AssociateVariationParams(
563 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params);
564 return base::FieldTrialList::CreateFieldTrial(
565 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
568 void SearchProviderTest::ClearAllResults() {
569 provider_->ClearAllResults();
572 // Actual Tests ---------------------------------------------------------------
574 // Make sure we query history for the default provider and a URLFetcher is
575 // created for the default provider suggest results.
576 TEST_F(SearchProviderTest, QueryDefaultProvider) {
577 base::string16 term = term1_.substr(0, term1_.length() - 1);
578 QueryForInput(term, false, false);
580 // Make sure the default providers suggest service was queried.
581 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
582 SearchProvider::kDefaultProviderURLFetcherID);
583 ASSERT_TRUE(fetcher);
585 // And the URL matches what we expected.
586 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
587 TemplateURLRef::SearchTermsArgs(term),
588 TemplateURLServiceFactory::GetForProfile(
589 &profile_)->search_terms_data()));
590 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url);
592 // Tell the SearchProvider the suggest query is done.
593 fetcher->set_response_code(200);
594 fetcher->delegate()->OnURLFetchComplete(fetcher);
595 fetcher = NULL;
597 // Run till the history results complete.
598 RunTillProviderDone();
600 // The SearchProvider is done. Make sure it has a result for the history
601 // term term1.
602 AutocompleteMatch term1_match;
603 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match));
604 // Term1 should not have a description, it's set later.
605 EXPECT_TRUE(term1_match.description.empty());
607 AutocompleteMatch wyt_match;
608 EXPECT_TRUE(FindMatchWithDestination(
609 GURL(default_t_url_->url_ref().ReplaceSearchTerms(
610 TemplateURLRef::SearchTermsArgs(term),
611 TemplateURLServiceFactory::GetForProfile(
612 &profile_)->search_terms_data())),
613 &wyt_match));
614 EXPECT_TRUE(wyt_match.description.empty());
616 // The match for term1 should be more relevant than the what you typed match.
617 EXPECT_GT(term1_match.relevance, wyt_match.relevance);
618 // This longer match should be inlineable.
619 EXPECT_TRUE(term1_match.allowed_to_be_default_match);
620 // The what you typed match should be too, of course.
621 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
624 TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
625 base::string16 term = term1_.substr(0, term1_.length() - 1);
626 QueryForInput(term, true, false);
628 ASSERT_FALSE(provider_->matches().empty());
629 ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
630 provider_->matches()[0].type);
631 EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match);
634 // Issues a query that matches the registered keyword and makes sure history
635 // is queried as well as URLFetchers getting created.
636 TEST_F(SearchProviderTest, QueryKeywordProvider) {
637 base::string16 term = keyword_term_.substr(0, keyword_term_.length() - 1);
638 QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term,
639 false,
640 false);
642 // Make sure the default providers suggest service was queried.
643 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID(
644 SearchProvider::kDefaultProviderURLFetcherID);
645 ASSERT_TRUE(default_fetcher);
647 // Tell the SearchProvider the default suggest query is done.
648 default_fetcher->set_response_code(200);
649 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
650 default_fetcher = NULL;
652 // Make sure the keyword providers suggest service was queried.
653 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID(
654 SearchProvider::kKeywordProviderURLFetcherID);
655 ASSERT_TRUE(keyword_fetcher);
657 // And the URL matches what we expected.
658 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms(
659 TemplateURLRef::SearchTermsArgs(term),
660 TemplateURLServiceFactory::GetForProfile(
661 &profile_)->search_terms_data()));
662 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url);
664 // Tell the SearchProvider the keyword suggest query is done.
665 keyword_fetcher->set_response_code(200);
666 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
667 keyword_fetcher = NULL;
669 // Run till the history results complete.
670 RunTillProviderDone();
672 // The SearchProvider is done. Make sure it has a result for the history
673 // term keyword.
674 AutocompleteMatch match;
675 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match));
677 // The match should have an associated keyword.
678 EXPECT_FALSE(match.keyword.empty());
680 // The fill into edit should contain the keyword.
681 EXPECT_EQ(keyword_t_url_->keyword() + base::char16(' ') + keyword_term_,
682 match.fill_into_edit);
685 TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) {
686 // None of the following input strings should be sent to the suggest server,
687 // because they may contain private data.
688 const char* inputs[] = {
689 "username:password",
690 "http://username:password",
691 "https://username:password",
692 "username:password@hostname",
693 "http://username:password@hostname/",
694 "file://filename",
695 "data://data",
696 "unknownscheme:anything",
697 "http://hostname/?query=q",
698 "http://hostname/path#ref",
699 "http://hostname/path #ref",
700 "https://hostname/path",
703 for (size_t i = 0; i < arraysize(inputs); ++i) {
704 QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
705 // Make sure the default provider's suggest service was not queried.
706 ASSERT_TRUE(test_factory_.GetFetcherByID(
707 SearchProvider::kDefaultProviderURLFetcherID) == NULL);
708 // Run till the history results complete.
709 RunTillProviderDone();
713 TEST_F(SearchProviderTest, SendNonPrivateDataToSuggest) {
714 // All of the following input strings should be sent to the suggest server,
715 // because they should not get caught by the private data checks.
716 const char* inputs[] = {
717 "query",
718 "query with spaces",
719 "http://hostname",
720 "http://hostname/path",
721 "http://hostname #ref",
722 "www.hostname.com #ref",
723 "https://hostname",
724 "#hashtag",
725 "foo https://hostname/path"
728 profile_.BlockUntilHistoryProcessesPendingRequests();
729 for (size_t i = 0; i < arraysize(inputs); ++i) {
730 QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
731 // Make sure the default provider's suggest service was queried.
732 ASSERT_TRUE(test_factory_.GetFetcherByID(
733 SearchProvider::kDefaultProviderURLFetcherID) != NULL);
737 TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
738 GURL url = AddSearchToHistory(default_t_url_,
739 ASCIIToUTF16("docs.google.com"), 1);
741 // Add the term as a url.
742 HistoryServiceFactory::GetForProfile(&profile_,
743 ServiceAccessType::EXPLICIT_ACCESS)
744 ->AddPageWithDetails(GURL("http://docs.google.com"), base::string16(), 1,
745 1, base::Time::Now(), false,
746 history::SOURCE_BROWSED);
747 profile_.BlockUntilHistoryProcessesPendingRequests();
749 AutocompleteMatch wyt_match;
750 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"),
751 &wyt_match));
753 // There should be two matches, one for what you typed, the other for
754 // 'docs.google.com'. The search term should have a lower priority than the
755 // what you typed match.
756 ASSERT_EQ(2u, provider_->matches().size());
757 AutocompleteMatch term_match;
758 EXPECT_TRUE(FindMatchWithDestination(url, &term_match));
759 EXPECT_GT(wyt_match.relevance, term_match.relevance);
760 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
761 EXPECT_TRUE(term_match.allowed_to_be_default_match);
764 TEST_F(SearchProviderTest, DontGiveNavsuggestionsInForcedQueryMode) {
765 const std::string kEmptyMatch;
766 struct {
767 const std::string json;
768 const std::string matches_in_default_mode[3];
769 const std::string matches_in_forced_query_mode[3];
770 } cases[] = {
771 // Without suggested relevance scores.
772 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
773 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]}]",
774 { "a", "a1.com", "a2" },
775 { "a", "a2", kEmptyMatch } },
777 // With suggested relevance scores in a situation where navsuggest would
778 // go second.
779 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
780 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
781 "\"google:suggestrelevance\":[1250, 1200]}]",
782 { "a", "a1.com", "a2" },
783 { "a", "a2", kEmptyMatch } },
785 // With suggested relevance scores in a situation where navsuggest
786 // would go first.
787 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
788 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
789 "\"google:suggestrelevance\":[1350, 1250]}]",
790 { "a1.com", "a", "a2" },
791 { "a", "a2", kEmptyMatch } },
793 // With suggested relevance scores in a situation where navsuggest
794 // would go first only because verbatim has been demoted.
795 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
796 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
797 "\"google:suggestrelevance\":[1450, 1400],"
798 "\"google:verbatimrelevance\":1350}]",
799 { "a1.com", "a2", "a" },
800 { "a2", "a", kEmptyMatch } },
803 for (size_t i = 0; i < arraysize(cases); ++i) {
804 ForcedQueryTestHelper("a", cases[i].json, cases[i].matches_in_default_mode,
805 "regular input with json=" + cases[i].json);
806 ForcedQueryTestHelper("?a", cases[i].json,
807 cases[i].matches_in_forced_query_mode,
808 "forced query input with json=" + cases[i].json);
812 // A multiword search with one visit should not autocomplete until multiple
813 // words are typed.
814 TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) {
815 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"),
816 1));
817 profile_.BlockUntilHistoryProcessesPendingRequests();
819 AutocompleteMatch wyt_match;
820 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"),
821 &wyt_match));
822 ASSERT_EQ(2u, provider_->matches().size());
823 AutocompleteMatch term_match;
824 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
825 EXPECT_GT(wyt_match.relevance, term_match.relevance);
826 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
827 EXPECT_TRUE(term_match.allowed_to_be_default_match);
829 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"),
830 &wyt_match));
831 ASSERT_EQ(2u, provider_->matches().size());
832 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
833 EXPECT_GT(term_match.relevance, wyt_match.relevance);
834 EXPECT_TRUE(term_match.allowed_to_be_default_match);
835 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
838 // A multiword search with more than one visit should autocomplete immediately.
839 TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) {
840 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
841 2));
842 profile_.BlockUntilHistoryProcessesPendingRequests();
844 AutocompleteMatch wyt_match;
845 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"),
846 &wyt_match));
847 ASSERT_EQ(2u, provider_->matches().size());
848 AutocompleteMatch term_match;
849 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
850 EXPECT_GT(term_match.relevance, wyt_match.relevance);
851 EXPECT_TRUE(term_match.allowed_to_be_default_match);
852 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
855 // Autocompletion should work at a word boundary after a space, and should
856 // offer a suggestion for the trimmed search query.
857 TEST_F(SearchProviderTest, AutocompleteAfterSpace) {
858 AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches "), 2);
859 GURL suggested_url(default_t_url_->url_ref().ReplaceSearchTerms(
860 TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("two searches")),
861 TemplateURLServiceFactory::GetForProfile(
862 &profile_)->search_terms_data()));
863 profile_.BlockUntilHistoryProcessesPendingRequests();
865 AutocompleteMatch wyt_match;
866 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "),
867 &wyt_match));
868 ASSERT_EQ(2u, provider_->matches().size());
869 AutocompleteMatch term_match;
870 EXPECT_TRUE(FindMatchWithDestination(suggested_url, &term_match));
871 EXPECT_GT(term_match.relevance, wyt_match.relevance);
872 EXPECT_TRUE(term_match.allowed_to_be_default_match);
873 EXPECT_EQ(ASCIIToUTF16("searches"), term_match.inline_autocompletion);
874 EXPECT_EQ(ASCIIToUTF16("two searches"), term_match.fill_into_edit);
875 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
878 // Newer multiword searches should score more highly than older ones.
879 TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) {
880 GURL term_url_a(AddSearchToHistory(default_t_url_,
881 ASCIIToUTF16("three searches aaa"), 1));
882 GURL term_url_b(AddSearchToHistory(default_t_url_,
883 ASCIIToUTF16("three searches bbb"), 1));
884 profile_.BlockUntilHistoryProcessesPendingRequests();
886 AutocompleteMatch wyt_match;
887 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"),
888 &wyt_match));
889 ASSERT_EQ(3u, provider_->matches().size());
890 AutocompleteMatch term_match_a;
891 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
892 AutocompleteMatch term_match_b;
893 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
894 EXPECT_GT(term_match_b.relevance, term_match_a.relevance);
895 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
896 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
897 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
898 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
901 // If ScoreHistoryResults doesn't properly clear its output vector it can skip
902 // scoring the actual results and just return results from a previous run.
903 TEST_F(SearchProviderTest, ResetResultsBetweenRuns) {
904 GURL term_url_a(AddSearchToHistory(default_t_url_,
905 ASCIIToUTF16("games"), 1));
906 GURL term_url_b(AddSearchToHistory(default_t_url_,
907 ASCIIToUTF16("gangnam style"), 1));
908 GURL term_url_c(AddSearchToHistory(default_t_url_,
909 ASCIIToUTF16("gundam"), 1));
910 profile_.BlockUntilHistoryProcessesPendingRequests();
912 AutocompleteMatch wyt_match;
913 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"),
914 &wyt_match));
915 ASSERT_EQ(1u, provider_->matches().size());
917 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("g"),
918 &wyt_match));
919 ASSERT_EQ(4u, provider_->matches().size());
921 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("ga"),
922 &wyt_match));
923 ASSERT_EQ(3u, provider_->matches().size());
925 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("gan"),
926 &wyt_match));
927 ASSERT_EQ(2u, provider_->matches().size());
929 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("gans"),
930 &wyt_match));
931 ASSERT_EQ(1u, provider_->matches().size());
934 // This test is identical to the previous one except that it enables the Answers
935 // in Suggest field trial which triggers a different code path in
936 // SearchProvider. When Answers launches, this can be removed.
937 TEST_F(SearchProviderTest, ResetResultsBetweenRunsAnswersInSuggestEnabled) {
938 CreateFieldTrial(OmniboxFieldTrial::kAnswersInSuggestRule, true);
940 GURL term_url_a(AddSearchToHistory(default_t_url_,
941 ASCIIToUTF16("games"), 1));
942 GURL term_url_b(AddSearchToHistory(default_t_url_,
943 ASCIIToUTF16("gangnam style"), 1));
944 GURL term_url_c(AddSearchToHistory(default_t_url_,
945 ASCIIToUTF16("gundam"), 1));
946 profile_.BlockUntilHistoryProcessesPendingRequests();
948 AutocompleteMatch wyt_match;
949 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"),
950 &wyt_match));
951 ASSERT_EQ(1u, provider_->matches().size());
953 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("g"),
954 &wyt_match));
955 ASSERT_EQ(4u, provider_->matches().size());
957 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("ga"),
958 &wyt_match));
959 ASSERT_EQ(3u, provider_->matches().size());
961 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("gan"),
962 &wyt_match));
963 ASSERT_EQ(2u, provider_->matches().size());
965 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("gans"),
966 &wyt_match));
967 ASSERT_EQ(1u, provider_->matches().size());
970 // An autocompleted multiword search should not be replaced by a different
971 // autocompletion while the user is still typing a valid prefix unless the
972 // user has typed the prefix as a query before.
973 TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) {
974 GURL term_url_a(AddSearchToHistory(default_t_url_,
975 ASCIIToUTF16("four searches aaa"), 3));
976 GURL term_url_b(AddSearchToHistory(default_t_url_,
977 ASCIIToUTF16("four searches bbb"), 1));
978 GURL term_url_c(AddSearchToHistory(default_t_url_,
979 ASCIIToUTF16("four searches"), 1));
980 profile_.BlockUntilHistoryProcessesPendingRequests();
982 AutocompleteMatch wyt_match;
983 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"),
984 &wyt_match));
985 ASSERT_EQ(4u, provider_->matches().size());
986 AutocompleteMatch term_match_a;
987 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
988 AutocompleteMatch term_match_b;
989 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
990 AutocompleteMatch term_match_c;
991 EXPECT_TRUE(FindMatchWithDestination(term_url_c, &term_match_c));
992 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
993 // We don't care about the relative order of b and c.
994 EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
995 EXPECT_GT(wyt_match.relevance, term_match_c.relevance);
996 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
997 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
998 EXPECT_TRUE(term_match_c.allowed_to_be_default_match);
999 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
1001 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"),
1002 &wyt_match));
1003 ASSERT_EQ(4u, provider_->matches().size());
1004 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
1005 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
1006 EXPECT_TRUE(FindMatchWithDestination(term_url_c, &term_match_c));
1007 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
1008 EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
1009 EXPECT_GT(wyt_match.relevance, term_match_c.relevance);
1010 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
1011 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
1012 EXPECT_TRUE(term_match_c.allowed_to_be_default_match);
1013 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
1015 // For the exact previously-issued query, the what-you-typed match should win.
1016 ASSERT_NO_FATAL_FAILURE(
1017 QueryForInputAndSetWYTMatch(ASCIIToUTF16("four searches"), &wyt_match));
1018 ASSERT_EQ(3u, provider_->matches().size());
1019 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
1020 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
1021 EXPECT_GT(wyt_match.relevance, term_match_a.relevance);
1022 EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
1023 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
1024 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
1025 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
1028 // Non-completable multiword searches should not crowd out single-word searches.
1029 TEST_F(SearchProviderTest, DontCrowdOutSingleWords) {
1030 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1));
1031 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1);
1032 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1);
1033 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1);
1034 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1);
1035 profile_.BlockUntilHistoryProcessesPendingRequests();
1037 AutocompleteMatch wyt_match;
1038 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"),
1039 &wyt_match));
1040 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size());
1041 AutocompleteMatch term_match;
1042 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
1043 EXPECT_GT(term_match.relevance, wyt_match.relevance);
1044 EXPECT_TRUE(term_match.allowed_to_be_default_match);
1045 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
1048 // Inline autocomplete matches regardless of case differences from the input.
1049 TEST_F(SearchProviderTest, InlineMixedCaseMatches) {
1050 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1));
1051 profile_.BlockUntilHistoryProcessesPendingRequests();
1053 AutocompleteMatch wyt_match;
1054 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"),
1055 &wyt_match));
1056 ASSERT_EQ(2u, provider_->matches().size());
1057 AutocompleteMatch term_match;
1058 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
1059 EXPECT_GT(term_match.relevance, wyt_match.relevance);
1060 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit);
1061 EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion);
1062 EXPECT_TRUE(term_match.allowed_to_be_default_match);
1065 // Verifies AutocompleteControllers return results (including keyword
1066 // results) in the right order and set descriptions for them correctly.
1067 TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) {
1068 // Add an entry that corresponds to a keyword search with 'term2'.
1069 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1);
1070 profile_.BlockUntilHistoryProcessesPendingRequests();
1072 AutocompleteController controller(&profile_,
1073 TemplateURLServiceFactory::GetForProfile(&profile_),
1074 NULL, AutocompleteProvider::TYPE_SEARCH);
1075 controller.Start(AutocompleteInput(
1076 ASCIIToUTF16("k t"), base::string16::npos, std::string(), GURL(),
1077 metrics::OmniboxEventProto::INVALID_SPEC, false, false, true, true,
1078 ChromeAutocompleteSchemeClassifier(&profile_)));
1079 const AutocompleteResult& result = controller.result();
1081 // There should be three matches, one for the keyword history, one for
1082 // keyword provider's what-you-typed, and one for the default provider's
1083 // what you typed, in that order.
1084 ASSERT_EQ(3u, result.size());
1085 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type);
1086 EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1087 result.match_at(1).type);
1088 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1089 result.match_at(2).type);
1090 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance);
1091 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance);
1092 EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match);
1093 EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match);
1094 EXPECT_FALSE(result.match_at(2).allowed_to_be_default_match);
1096 // The two keyword results should come with the keyword we expect.
1097 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword);
1098 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword);
1099 // The default provider has a different keyword. (We don't explicitly
1100 // set it during this test, so all we do is assert that it's different.)
1101 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword);
1103 // The top result will always have a description. The third result,
1104 // coming from a different provider than the first two, should also.
1105 // Whether the second result has one doesn't matter much. (If it was
1106 // missing, people would infer that it's the same search provider as
1107 // the one above it.)
1108 EXPECT_FALSE(result.match_at(0).description.empty());
1109 EXPECT_FALSE(result.match_at(2).description.empty());
1110 EXPECT_NE(result.match_at(0).description, result.match_at(2).description);
1113 TEST_F(SearchProviderTest, KeywordVerbatim) {
1114 TestData cases[] = {
1115 // Test a simple keyword input.
1116 { ASCIIToUTF16("k foo"), 2,
1117 { ResultInfo(GURL("http://keyword/foo"),
1118 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1119 true,
1120 ASCIIToUTF16("k foo")),
1121 ResultInfo(GURL("http://defaultturl/k%20foo"),
1122 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1123 false,
1124 ASCIIToUTF16("k foo") ) } },
1126 // Make sure extra whitespace after the keyword doesn't change the
1127 // keyword verbatim query. Also verify that interior consecutive
1128 // whitespace gets trimmed.
1129 { ASCIIToUTF16("k foo"), 2,
1130 { ResultInfo(GURL("http://keyword/foo"),
1131 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1132 true,
1133 ASCIIToUTF16("k foo")),
1134 ResultInfo(GURL("http://defaultturl/k%20foo"),
1135 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1136 false,
1137 ASCIIToUTF16("k foo")) } },
1138 // Leading whitespace should be stripped before SearchProvider gets the
1139 // input; hence there are no tests here about how it handles those inputs.
1141 // Verify that interior consecutive whitespace gets trimmed in either case.
1142 { ASCIIToUTF16("k foo bar"), 2,
1143 { ResultInfo(GURL("http://keyword/foo%20bar"),
1144 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1145 true,
1146 ASCIIToUTF16("k foo bar")),
1147 ResultInfo(GURL("http://defaultturl/k%20foo%20bar"),
1148 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1149 false,
1150 ASCIIToUTF16("k foo bar")) } },
1152 // Verify that trailing whitespace gets trimmed.
1153 { ASCIIToUTF16("k foo bar "), 2,
1154 { ResultInfo(GURL("http://keyword/foo%20bar"),
1155 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1156 true,
1157 ASCIIToUTF16("k foo bar")),
1158 ResultInfo(GURL("http://defaultturl/k%20foo%20bar"),
1159 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1160 false,
1161 ASCIIToUTF16("k foo bar")) } },
1163 // Keywords can be prefixed by certain things that should get ignored
1164 // when constructing the keyword match.
1165 { ASCIIToUTF16("www.k foo"), 2,
1166 { ResultInfo(GURL("http://keyword/foo"),
1167 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1168 true,
1169 ASCIIToUTF16("k foo")),
1170 ResultInfo(GURL("http://defaultturl/www.k%20foo"),
1171 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1172 false,
1173 ASCIIToUTF16("www.k foo")) } },
1174 { ASCIIToUTF16("http://k foo"), 2,
1175 { ResultInfo(GURL("http://keyword/foo"),
1176 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1177 true,
1178 ASCIIToUTF16("k foo")),
1179 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"),
1180 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1181 false,
1182 ASCIIToUTF16("http://k foo")) } },
1183 { ASCIIToUTF16("http://www.k foo"), 2,
1184 { ResultInfo(GURL("http://keyword/foo"),
1185 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1186 true,
1187 ASCIIToUTF16("k foo")),
1188 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"),
1189 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1190 false,
1191 ASCIIToUTF16("http://www.k foo")) } },
1193 // A keyword with no remaining input shouldn't get a keyword
1194 // verbatim match.
1195 { ASCIIToUTF16("k"), 1,
1196 { ResultInfo(GURL("http://defaultturl/k"),
1197 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1198 true,
1199 ASCIIToUTF16("k")) } },
1200 // Ditto. Trailing whitespace shouldn't make a difference.
1201 { ASCIIToUTF16("k "), 1,
1202 { ResultInfo(GURL("http://defaultturl/k"),
1203 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1204 true,
1205 ASCIIToUTF16("k")) } }
1207 // The fact that verbatim queries to keyword are handled by KeywordProvider
1208 // not SearchProvider is tested in
1209 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc.
1212 // Test not in keyword mode.
1213 RunTest(cases, arraysize(cases), false);
1215 // Test in keyword mode. (Both modes should give the same result.)
1216 RunTest(cases, arraysize(cases), true);
1219 // Ensures command-line flags are reflected in the URLs the search provider
1220 // generates.
1221 TEST_F(SearchProviderTest, CommandLineOverrides) {
1222 TemplateURLService* turl_model =
1223 TemplateURLServiceFactory::GetForProfile(&profile_);
1225 TemplateURLData data;
1226 data.short_name = ASCIIToUTF16("default");
1227 data.SetKeyword(data.short_name);
1228 data.SetURL("{google:baseURL}{searchTerms}");
1229 default_t_url_ = new TemplateURL(data);
1230 turl_model->Add(default_t_url_);
1231 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
1233 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
1234 switches::kGoogleBaseURL, "http://www.bar.com/");
1235 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
1236 switches::kExtraSearchQueryParams, "a=b");
1238 TestData cases[] = {
1239 { ASCIIToUTF16("k a"), 2,
1240 { ResultInfo(GURL("http://keyword/a"),
1241 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1242 true,
1243 ASCIIToUTF16("k a")),
1244 ResultInfo(GURL("http://www.bar.com/k%20a?a=b"),
1245 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1246 false,
1247 ASCIIToUTF16("k a")) } },
1250 RunTest(cases, arraysize(cases), false);
1253 // Verifies Navsuggest results don't set a TemplateURL, which Instant relies on.
1254 // Also verifies that just the *first* navigational result is listed as a match
1255 // if suggested relevance scores were not sent.
1256 TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) {
1257 QueryForInputAndWaitForFetcherResponses(
1258 ASCIIToUTF16("a.c"), false,
1259 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[],"
1260 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1261 std::string());
1263 // Make sure the only match is 'a.com' and it doesn't have a template_url.
1264 AutocompleteMatch nav_match;
1265 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match));
1266 EXPECT_TRUE(nav_match.keyword.empty());
1267 EXPECT_FALSE(nav_match.allowed_to_be_default_match);
1268 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match));
1271 // Verifies that the most relevant suggest results are added properly.
1272 TEST_F(SearchProviderTest, SuggestRelevance) {
1273 QueryForInputAndWaitForFetcherResponses(
1274 ASCIIToUTF16("a"), false, "[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]",
1275 std::string());
1277 // Check the expected verbatim and (first 3) suggestions' relative relevances.
1278 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4;
1279 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
1280 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1));
1281 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2));
1282 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3));
1283 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4));
1284 EXPECT_GT(verbatim.relevance, match_a1.relevance);
1285 EXPECT_GT(match_a1.relevance, match_a2.relevance);
1286 EXPECT_GT(match_a2.relevance, match_a3.relevance);
1287 EXPECT_TRUE(verbatim.allowed_to_be_default_match);
1288 EXPECT_FALSE(match_a1.allowed_to_be_default_match);
1289 EXPECT_FALSE(match_a2.allowed_to_be_default_match);
1290 EXPECT_FALSE(match_a3.allowed_to_be_default_match);
1293 // Verifies that the default provider abandons suggested relevance scores
1294 // when in keyword mode. This should happen regardless of whether the
1295 // keyword provider returns suggested relevance scores.
1296 TEST_F(SearchProviderTest, DefaultProviderNoSuggestRelevanceInKeywordMode) {
1297 struct {
1298 const std::string default_provider_json;
1299 const std::string keyword_provider_json;
1300 const std::string matches[5];
1301 } cases[] = {
1302 // First, try an input where the keyword provider does not deliver
1303 // suggested relevance scores.
1304 { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
1305 "{\"google:verbatimrelevance\":9700,"
1306 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1307 "\"google:suggestrelevance\":[9900, 9800]}]",
1308 "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]}]",
1309 { "a", "akeyword-query", "k a", "adefault.com", "k adefault-query" } },
1311 // Now try with keyword provider suggested relevance scores.
1312 { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
1313 "{\"google:verbatimrelevance\":9700,"
1314 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1315 "\"google:suggestrelevance\":[9900, 9800]}]",
1316 "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"],"
1317 "\"google:verbatimrelevance\":9500,"
1318 "\"google:suggestrelevance\":[9600]}]",
1319 { "akeyword-query", "a", "k a", "adefault.com", "k adefault-query" } }
1322 for (size_t i = 0; i < arraysize(cases); ++i) {
1323 // Send the query twice in order to have a synchronous pass after the first
1324 // response is received. This is necessary because SearchProvider doesn't
1325 // allow an asynchronous response to change the default match.
1326 for (size_t j = 0; j < 2; ++j) {
1327 QueryForInputAndWaitForFetcherResponses(
1328 ASCIIToUTF16("k a"), true, cases[i].default_provider_json,
1329 cases[i].keyword_provider_json);
1332 SCOPED_TRACE(
1333 "for input with default_provider_json=" +
1334 cases[i].default_provider_json + " and keyword_provider_json=" +
1335 cases[i].keyword_provider_json);
1336 const ACMatches& matches = provider_->matches();
1337 ASSERT_LE(matches.size(), arraysize(cases[i].matches));
1338 size_t j = 0;
1339 // Ensure that the returned matches equal the expectations.
1340 for (; j < matches.size(); ++j)
1341 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), matches[j].contents);
1342 // Ensure that no expected matches are missing.
1343 for (; j < arraysize(cases[i].matches); ++j)
1344 EXPECT_EQ(std::string(), cases[i].matches[j]);
1348 // Verifies that suggest results with relevance scores are added
1349 // properly when using the default fetcher. When adding a new test
1350 // case to this test, please consider adding it to the tests in
1351 // KeywordFetcherSuggestRelevance below.
1352 TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
1353 struct {
1354 const std::string json;
1355 const ExpectedMatch matches[6];
1356 const std::string inline_autocompletion;
1357 } cases[] = {
1358 // Ensure that suggestrelevance scores reorder matches.
1359 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1360 { { "a", true }, { "c", false }, { "b", false }, kEmptyExpectedMatch,
1361 kEmptyExpectedMatch, kEmptyExpectedMatch },
1362 std::string() },
1363 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1364 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1365 "\"google:suggestrelevance\":[1, 2]}]",
1366 { { "a", true }, { "c.com", false }, { "b.com", false },
1367 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1368 std::string() },
1370 // Without suggested relevance scores, we should only allow one
1371 // navsuggest result to be be displayed.
1372 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1373 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1374 { { "a", true }, { "b.com", false }, kEmptyExpectedMatch,
1375 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1376 std::string() },
1378 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1379 // Negative values will have no effect; the calculated value will be used.
1380 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1381 "\"google:suggestrelevance\":[9998]}]",
1382 { { "a", true}, { "a1", false }, kEmptyExpectedMatch,
1383 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1384 std::string() },
1385 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1386 "\"google:suggestrelevance\":[9999]}]",
1387 { { "a1", true }, { "a", true }, kEmptyExpectedMatch,
1388 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1389 "1" },
1390 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1391 "\"google:suggestrelevance\":[9999]}]",
1392 { { "a1", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1393 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1394 "1" },
1395 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1396 "\"google:suggestrelevance\":[9999]}]",
1397 { { "a1", true }, { "a", true }, kEmptyExpectedMatch,
1398 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1399 "1" },
1400 { "[\"a\",[\"http://a.com\"],[],[],"
1401 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1402 "\"google:verbatimrelevance\":9999,"
1403 "\"google:suggestrelevance\":[9998]}]",
1404 { { "a", true }, { "a.com", false }, kEmptyExpectedMatch,
1405 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1406 std::string() },
1407 { "[\"a\",[\"http://a.com\"],[],[],"
1408 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1409 "\"google:verbatimrelevance\":9998,"
1410 "\"google:suggestrelevance\":[9999]}]",
1411 { { "a.com", true }, { "a", true }, kEmptyExpectedMatch,
1412 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1413 ".com" },
1414 { "[\"a\",[\"http://a.com\"],[],[],"
1415 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1416 "\"google:verbatimrelevance\":0,"
1417 "\"google:suggestrelevance\":[9999]}]",
1418 { { "a.com", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1419 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1420 ".com" },
1421 { "[\"a\",[\"http://a.com\"],[],[],"
1422 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1423 "\"google:verbatimrelevance\":-1,"
1424 "\"google:suggestrelevance\":[9999]}]",
1425 { { "a.com", true }, { "a", true }, kEmptyExpectedMatch,
1426 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1427 ".com" },
1429 // Ensure that both types of relevance scores reorder matches together.
1430 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1431 "\"google:verbatimrelevance\":9998}]",
1432 { { "a1", true }, { "a", true }, { "a2", false }, kEmptyExpectedMatch,
1433 kEmptyExpectedMatch, kEmptyExpectedMatch },
1434 "1" },
1436 // Check that an inlineable result appears first regardless of its score.
1437 // Also, if the result set lacks a single inlineable result, abandon the
1438 // request to suppress verbatim (verbatim_relevance=0), which will then
1439 // cause verbatim to appear (first).
1440 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1441 { { "a", true }, { "b", false }, kEmptyExpectedMatch,
1442 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1443 std::string() },
1444 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1445 "\"google:verbatimrelevance\":0}]",
1446 { { "a", true }, { "b", false }, kEmptyExpectedMatch,
1447 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1448 std::string() },
1449 { "[\"a\",[\"http://b.com\"],[],[],"
1450 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1451 "\"google:suggestrelevance\":[9999]}]",
1452 { { "a", true }, { "b.com", false }, kEmptyExpectedMatch,
1453 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1454 std::string() },
1455 { "[\"a\",[\"http://b.com\"],[],[],"
1456 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1457 "\"google:suggestrelevance\":[9999],"
1458 "\"google:verbatimrelevance\":0}]",
1459 { { "a", true }, { "b.com", false }, kEmptyExpectedMatch,
1460 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1461 std::string() },
1463 // Allow low-scoring matches.
1464 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1465 { { "a1", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1466 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1467 "1" },
1468 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":10}]",
1469 { { "a1", true }, { "a", true }, kEmptyExpectedMatch,
1470 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1471 "1" },
1472 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[10],"
1473 "\"google:verbatimrelevance\":0}]",
1474 { { "a1", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1475 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1476 "1" },
1477 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10, 20],"
1478 "\"google:verbatimrelevance\":0}]",
1479 { { "a2", true }, { "a1", false }, kEmptyExpectedMatch,
1480 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1481 "2" },
1482 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10, 30],"
1483 "\"google:verbatimrelevance\":20}]",
1484 { { "a2", true }, { "a", true }, { "a1", false }, kEmptyExpectedMatch,
1485 kEmptyExpectedMatch, kEmptyExpectedMatch },
1486 "2" },
1487 { "[\"a\",[\"http://a.com\"],[],[],"
1488 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1489 "\"google:suggestrelevance\":[10],"
1490 "\"google:verbatimrelevance\":0}]",
1491 { { "a.com", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1492 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1493 ".com" },
1494 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1495 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1496 "\"google:suggestrelevance\":[10, 20],"
1497 "\"google:verbatimrelevance\":0}]",
1498 { { "a2.com", true }, { "a1.com", false }, kEmptyExpectedMatch,
1499 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1500 "2.com" },
1502 // Ensure that all suggestions are considered, regardless of order.
1503 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1504 "{\"google:suggestrelevance\":[10, 20, 30, 40, 50, 60, 70]}]",
1505 { { "a", true }, { "h", false }, { "g", false }, { "f", false },
1506 { "e", false }, { "d", false } },
1507 std::string() },
1508 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1509 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1510 "\"http://h.com\"],[],[],"
1511 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1512 "\"NAVIGATION\", \"NAVIGATION\","
1513 "\"NAVIGATION\", \"NAVIGATION\","
1514 "\"NAVIGATION\"],"
1515 "\"google:suggestrelevance\":[10, 20, 30, 40, 50, 60, 70]}]",
1516 { { "a", true }, { "h.com", false }, { "g.com", false },
1517 { "f.com", false }, { "e.com", false }, { "d.com", false } },
1518 std::string() },
1520 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1521 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10]}]",
1522 { { "a", true }, { "a1", false }, { "a2", false }, kEmptyExpectedMatch,
1523 kEmptyExpectedMatch, kEmptyExpectedMatch },
1524 std::string() },
1525 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 10]}]",
1526 { { "a", true }, { "a1", false }, kEmptyExpectedMatch,
1527 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1528 std::string() },
1529 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1530 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1531 "\"google:suggestrelevance\":[10]}]",
1532 { { "a", true }, { "a1.com", false }, kEmptyExpectedMatch,
1533 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1534 std::string() },
1535 { "[\"a\",[\"http://a1.com\"],[],[],"
1536 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1537 "\"google:suggestrelevance\":[9999, 10]}]",
1538 { { "a", true }, { "a1.com", false }, kEmptyExpectedMatch,
1539 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1540 std::string() },
1542 // Ensure that all 'verbatim' results are merged with their maximum score.
1543 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1544 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1545 { { "a2", true }, { "a", true }, { "a1", false }, kEmptyExpectedMatch,
1546 kEmptyExpectedMatch, kEmptyExpectedMatch },
1547 "2" },
1548 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1549 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1550 "\"google:verbatimrelevance\":0}]",
1551 { { "a2", true }, { "a", true }, { "a1", false }, kEmptyExpectedMatch,
1552 kEmptyExpectedMatch, kEmptyExpectedMatch },
1553 "2" },
1555 // Ensure that verbatim is always generated without other suggestions.
1556 // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1557 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1558 { { "a", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1559 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1560 std::string() },
1561 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1562 { { "a", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1563 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1564 std::string() },
1567 for (size_t i = 0; i < arraysize(cases); ++i) {
1568 // Send the query twice in order to have a synchronous pass after the first
1569 // response is received. This is necessary because SearchProvider doesn't
1570 // allow an asynchronous response to change the default match.
1571 for (size_t j = 0; j < 2; ++j) {
1572 QueryForInputAndWaitForFetcherResponses(
1573 ASCIIToUTF16("a"), false, cases[i].json, std::string());
1576 const std::string description = "for input with json=" + cases[i].json;
1577 CheckMatches(description, arraysize(cases[i].matches), cases[i].matches,
1578 provider_->matches());
1582 // Verifies that suggest results with relevance scores are added
1583 // properly when using the keyword fetcher. This is similar to the
1584 // test DefaultFetcherSuggestRelevance above but this uses inputs that
1585 // trigger keyword suggestions (i.e., "k a" rather than "a") and has
1586 // different expectations (because now the results are a mix of
1587 // keyword suggestions and default provider suggestions). When a new
1588 // test is added to this TEST_F, please consider if it would be
1589 // appropriate to add to DefaultFetcherSuggestRelevance as well.
1590 TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
1591 struct KeywordFetcherMatch {
1592 std::string contents;
1593 bool from_keyword;
1594 bool allowed_to_be_default_match;
1596 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
1597 struct {
1598 const std::string json;
1599 const KeywordFetcherMatch matches[6];
1600 const std::string inline_autocompletion;
1601 } cases[] = {
1602 // Ensure that suggest relevance scores reorder matches and that
1603 // the keyword verbatim (lacking a suggested verbatim score) beats
1604 // the default provider verbatim.
1605 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1606 { { "a", true, true },
1607 { "k a", false, false },
1608 { "c", true, false },
1609 { "b", true, false },
1610 kEmptyMatch, kEmptyMatch },
1611 std::string() },
1612 // Again, check that relevance scores reorder matches, just this
1613 // time with navigation matches. This also checks that with
1614 // suggested relevance scores we allow multiple navsuggest results.
1615 // Note that navsuggest results that come from a keyword provider
1616 // are marked as not a keyword result. (They don't go to a
1617 // keyword search engine.)
1618 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
1619 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1620 "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
1621 { { "a", true, true },
1622 { "d", true, false },
1623 { "c.com", false, false },
1624 { "b.com", false, false },
1625 { "k a", false, false },
1626 kEmptyMatch },
1627 std::string() },
1629 // Without suggested relevance scores, we should only allow one
1630 // navsuggest result to be be displayed.
1631 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1632 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1633 { { "a", true, true },
1634 { "b.com", false, false },
1635 { "k a", false, false },
1636 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1637 std::string() },
1639 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1640 // Negative values will have no effect; the calculated value will be used.
1641 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1642 "\"google:suggestrelevance\":[9998]}]",
1643 { { "a", true, true },
1644 { "a1", true, false },
1645 { "k a", false, false },
1646 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1647 std::string() },
1648 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1649 "\"google:suggestrelevance\":[9999]}]",
1650 { { "a1", true, true },
1651 { "a", true, true },
1652 { "k a", false, false },
1653 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1654 "1" },
1655 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1656 "\"google:suggestrelevance\":[9999]}]",
1657 { { "a1", true, true },
1658 { "k a", false, false },
1659 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1660 "1" },
1661 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1662 "\"google:suggestrelevance\":[9999]}]",
1663 { { "a1", true, true },
1664 { "a", true, true },
1665 { "k a", false, false },
1666 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1667 "1" },
1668 { "[\"a\",[\"http://a.com\"],[],[],"
1669 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1670 "\"google:verbatimrelevance\":9999,"
1671 "\"google:suggestrelevance\":[9998]}]",
1672 { { "a", true, true },
1673 { "a.com", false, false },
1674 { "k a", false, false },
1675 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1676 std::string() },
1678 // Ensure that both types of relevance scores reorder matches together.
1679 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1680 "\"google:verbatimrelevance\":9998}]",
1681 { { "a1", true, true },
1682 { "a", true, true },
1683 { "a2", true, false },
1684 { "k a", false, false },
1685 kEmptyMatch, kEmptyMatch },
1686 "1" },
1688 // Check that an inlineable match appears first regardless of its score.
1689 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1690 { { "a", true, true },
1691 { "b", true, false },
1692 { "k a", false, false },
1693 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1694 std::string() },
1695 { "[\"a\",[\"http://b.com\"],[],[],"
1696 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1697 "\"google:suggestrelevance\":[9999]}]",
1698 { { "a", true, true },
1699 { "b.com", false, false },
1700 { "k a", false, false },
1701 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1702 std::string() },
1703 // If there is no inlineable match, restore the keyword verbatim score.
1704 // The keyword verbatim match will then appear first.
1705 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1706 "\"google:verbatimrelevance\":0}]",
1707 { { "a", true, true },
1708 { "b", true, false },
1709 { "k a", false, false },
1710 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1711 std::string() },
1712 { "[\"a\",[\"http://b.com\"],[],[],"
1713 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1714 "\"google:suggestrelevance\":[9999],"
1715 "\"google:verbatimrelevance\":0}]",
1716 { { "a", true, true },
1717 { "b.com", false, false },
1718 { "k a", false, false },
1719 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1720 std::string() },
1722 // The top result does not have to score as highly as calculated
1723 // verbatim. i.e., there are no minimum score restrictions in
1724 // this provider.
1725 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1726 { { "a1", true, true },
1727 { "k a", false, false },
1728 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1729 "1" },
1730 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":10}]",
1731 { { "a1", true, true },
1732 { "k a", false, false },
1733 { "a", true, true },
1734 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1735 "1" },
1736 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[10],"
1737 "\"google:verbatimrelevance\":0}]",
1738 { { "a1", true, true },
1739 { "k a", false, false },
1740 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1741 "1" },
1742 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10, 20],"
1743 "\"google:verbatimrelevance\":0}]",
1744 { { "a2", true, true },
1745 { "k a", false, false },
1746 { "a1", true, false },
1747 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1748 "2" },
1749 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10, 30],"
1750 "\"google:verbatimrelevance\":20}]",
1751 { { "a2", true, true },
1752 { "k a", false, false },
1753 { "a", true, true },
1754 { "a1", true, false },
1755 kEmptyMatch, kEmptyMatch },
1756 "2" },
1758 // Ensure that all suggestions are considered, regardless of order.
1759 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1760 "{\"google:suggestrelevance\":[10, 20, 30, 40, 50, 60, 70]}]",
1761 { { "a", true, true },
1762 { "k a", false, false },
1763 { "h", true, false },
1764 { "g", true, false },
1765 { "f", true, false },
1766 { "e", true, false } },
1767 std::string() },
1768 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1769 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1770 "\"http://h.com\"],[],[],"
1771 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1772 "\"NAVIGATION\", \"NAVIGATION\","
1773 "\"NAVIGATION\", \"NAVIGATION\","
1774 "\"NAVIGATION\"],"
1775 "\"google:suggestrelevance\":[10, 20, 30, 40, 50, 60, 70]}]",
1776 { { "a", true, true },
1777 { "k a", false, false },
1778 { "h.com", false, false },
1779 { "g.com", false, false },
1780 { "f.com", false, false },
1781 { "e.com", false, false } },
1782 std::string() },
1784 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1785 // Note that keyword suggestions by default (not in suggested relevance
1786 // mode) score more highly than the default verbatim.
1787 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1788 { { "a", true, true },
1789 { "a1", true, false },
1790 { "a2", true, false },
1791 { "k a", false, false },
1792 kEmptyMatch, kEmptyMatch },
1793 std::string() },
1794 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1795 { { "a", true, true },
1796 { "a1", true, false },
1797 { "k a", false, false },
1798 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1799 std::string() },
1800 // In this case, ignoring the suggested relevance scores means we keep
1801 // only one navsuggest result.
1802 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1803 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1804 "\"google:suggestrelevance\":[1]}]",
1805 { { "a", true, true },
1806 { "a1.com", false, false },
1807 { "k a", false, false },
1808 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1809 std::string() },
1810 { "[\"a\",[\"http://a1.com\"],[],[],"
1811 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1812 "\"google:suggestrelevance\":[9999, 1]}]",
1813 { { "a", true, true },
1814 { "a1.com", false, false },
1815 { "k a", false, false },
1816 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1817 std::string() },
1819 // Ensure that all 'verbatim' results are merged with their maximum score.
1820 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1821 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1822 { { "a2", true, true },
1823 { "a", true, true },
1824 { "a1", true, false },
1825 { "k a", false, false },
1826 kEmptyMatch, kEmptyMatch },
1827 "2" },
1828 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1829 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1830 "\"google:verbatimrelevance\":0}]",
1831 { { "a2", true, true },
1832 { "a", true, true },
1833 { "a1", true, false },
1834 { "k a", false, false },
1835 kEmptyMatch, kEmptyMatch },
1836 "2" },
1838 // Ensure that verbatim is always generated without other suggestions.
1839 // TODO(mpearson): Ensure the value of verbatimrelevance is respected
1840 // (except when suggested relevances are ignored).
1841 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1842 { { "a", true, true },
1843 { "k a", false, false },
1844 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1845 std::string() },
1846 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1847 { { "a", true, true },
1848 { "k a", false, false },
1849 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1850 std::string() },
1852 // In reorder mode, navsuggestions will not need to be demoted (because
1853 // they are marked as not allowed to be default match and will be
1854 // reordered as necessary).
1855 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1856 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1857 "\"google:verbatimrelevance\":9990,"
1858 "\"google:suggestrelevance\":[9998, 9999]}]",
1859 { { "a", true, true },
1860 { "a2.com", false, false },
1861 { "a1.com", false, false },
1862 { "k a", false, false },
1863 kEmptyMatch, kEmptyMatch },
1864 std::string() },
1865 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1866 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1867 "\"google:verbatimrelevance\":9990,"
1868 "\"google:suggestrelevance\":[9999, 9998]}]",
1869 { { "a", true, true },
1870 { "a1.com", false, false },
1871 { "a2.com", false, false },
1872 { "k a", false, false },
1873 kEmptyMatch, kEmptyMatch },
1874 std::string() },
1875 { "[\"a\",[\"https://a/\"],[],[],"
1876 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1877 "\"google:suggestrelevance\":[9999]}]",
1878 { { "a", true, true },
1879 { "https://a", false, false },
1880 { "k a", false, false },
1881 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1882 std::string() },
1883 // Check when navsuggest scores more than verbatim and there is query
1884 // suggestion but it scores lower.
1885 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1886 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1887 "\"google:verbatimrelevance\":9990,"
1888 "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
1889 { { "a", true, true },
1890 { "a2.com", false, false },
1891 { "a1.com", false, false },
1892 { "a3", true, false },
1893 { "k a", false, false },
1894 kEmptyMatch },
1895 std::string() },
1896 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1897 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1898 "\"google:verbatimrelevance\":9990,"
1899 "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
1900 { { "a", true, true },
1901 { "a1.com", false, false },
1902 { "a2.com", false, false },
1903 { "a3", true, false },
1904 { "k a", false, false },
1905 kEmptyMatch },
1906 std::string() },
1907 // Check when navsuggest scores more than a query suggestion. There is
1908 // a verbatim but it scores lower.
1909 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1910 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1911 "\"google:verbatimrelevance\":9990,"
1912 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1913 { { "a3", true, true },
1914 { "a2.com", false, false },
1915 { "a1.com", false, false },
1916 { "a", true, true },
1917 { "k a", false, false },
1918 kEmptyMatch },
1919 "3" },
1920 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1921 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1922 "\"google:verbatimrelevance\":9990,"
1923 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1924 { { "a3", true, true },
1925 { "a1.com", false, false },
1926 { "a2.com", false, false },
1927 { "a", true, true },
1928 { "k a", false, false },
1929 kEmptyMatch },
1930 "3" },
1931 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1932 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1933 "\"google:verbatimrelevance\":0,"
1934 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1935 { { "a3", true, true },
1936 { "a2.com", false, false },
1937 { "a1.com", false, false },
1938 { "k a", false, false },
1939 kEmptyMatch, kEmptyMatch },
1940 "3" },
1941 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1942 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1943 "\"google:verbatimrelevance\":0,"
1944 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1945 { { "a3", true, true },
1946 { "a1.com", false, false },
1947 { "a2.com", false, false },
1948 { "k a", false, false },
1949 kEmptyMatch, kEmptyMatch },
1950 "3" },
1951 // Check when there is neither verbatim nor a query suggestion that,
1952 // because we can't demote navsuggestions below a query suggestion,
1953 // we restore the keyword verbatim score.
1954 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1955 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1956 "\"google:verbatimrelevance\":0,"
1957 "\"google:suggestrelevance\":[9998, 9999]}]",
1958 { { "a", true, true },
1959 { "a2.com", false, false },
1960 { "a1.com", false, false },
1961 { "k a", false, false },
1962 kEmptyMatch, kEmptyMatch },
1963 std::string() },
1964 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1965 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1966 "\"google:verbatimrelevance\":0,"
1967 "\"google:suggestrelevance\":[9999, 9998]}]",
1968 { { "a", true, true },
1969 { "a1.com", false, false },
1970 { "a2.com", false, false },
1971 { "k a", false, false },
1972 kEmptyMatch, kEmptyMatch },
1973 std::string() },
1974 // More checks that everything works when it's not necessary to demote.
1975 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1976 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1977 "\"google:verbatimrelevance\":9990,"
1978 "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
1979 { { "a3", true, true },
1980 { "a2.com", false, false },
1981 { "a1.com", false, false },
1982 { "a", true, true },
1983 { "k a", false, false },
1984 kEmptyMatch },
1985 "3" },
1986 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1987 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1988 "\"google:verbatimrelevance\":9990,"
1989 "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1990 { { "a3", true, true },
1991 { "a1.com", false, false },
1992 { "a2.com", false, false },
1993 { "a", true, true },
1994 { "k a", false, false },
1995 kEmptyMatch },
1996 "3" },
1999 for (size_t i = 0; i < arraysize(cases); ++i) {
2000 // Send the query twice in order to have a synchronous pass after the first
2001 // response is received. This is necessary because SearchProvider doesn't
2002 // allow an asynchronous response to change the default match.
2003 for (size_t j = 0; j < 2; ++j) {
2004 QueryForInput(ASCIIToUTF16("k a"), false, true);
2006 // Set up a default fetcher with no results.
2007 net::TestURLFetcher* default_fetcher =
2008 test_factory_.GetFetcherByID(
2009 SearchProvider::kDefaultProviderURLFetcherID);
2010 ASSERT_TRUE(default_fetcher);
2011 default_fetcher->set_response_code(200);
2012 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
2013 default_fetcher = NULL;
2015 // Set up a keyword fetcher with provided results.
2016 net::TestURLFetcher* keyword_fetcher =
2017 test_factory_.GetFetcherByID(
2018 SearchProvider::kKeywordProviderURLFetcherID);
2019 ASSERT_TRUE(keyword_fetcher);
2020 keyword_fetcher->set_response_code(200);
2021 keyword_fetcher->SetResponseString(cases[i].json);
2022 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
2023 keyword_fetcher = NULL;
2024 RunTillProviderDone();
2027 SCOPED_TRACE("for input with json=" + cases[i].json);
2028 const ACMatches& matches = provider_->matches();
2029 ASSERT_FALSE(matches.empty());
2030 // Find the first match that's allowed to be the default match and check
2031 // its inline_autocompletion.
2032 ACMatches::const_iterator it = FindDefaultMatch(matches);
2033 ASSERT_NE(matches.end(), it);
2034 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2035 it->inline_autocompletion);
2037 ASSERT_LE(matches.size(), arraysize(cases[i].matches));
2038 size_t j = 0;
2039 // Ensure that the returned matches equal the expectations.
2040 for (; j < matches.size(); ++j) {
2041 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
2042 matches[j].contents);
2043 EXPECT_EQ(cases[i].matches[j].from_keyword,
2044 matches[j].keyword == ASCIIToUTF16("k"));
2045 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
2046 matches[j].allowed_to_be_default_match);
2048 // Ensure that no expected matches are missing.
2049 for (; j < arraysize(cases[i].matches); ++j) {
2050 SCOPED_TRACE(" Case # " + base::IntToString(i));
2051 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents);
2056 TEST_F(SearchProviderTest, DontInlineAutocompleteAsynchronously) {
2057 // This test sends two separate queries, each receiving different JSON
2058 // replies, and checks that at each stage of processing (receiving first
2059 // asynchronous response, handling new keystroke synchronously / sending the
2060 // second request, and receiving the second asynchronous response) we have the
2061 // expected matches. In particular, receiving the second response shouldn't
2062 // cause an unexpected inline autcompletion.
2063 struct {
2064 const std::string first_json;
2065 const ExpectedMatch first_async_matches[4];
2066 const ExpectedMatch sync_matches[4];
2067 const std::string second_json;
2068 const ExpectedMatch second_async_matches[4];
2069 } cases[] = {
2070 // A simple test that verifies we don't inline autocomplete after the
2071 // first asynchronous response, but we do at the next keystroke if the
2072 // response's results were good enough. Furthermore, we should continue
2073 // inline autocompleting after the second asynchronous response if the new
2074 // top suggestion is the same as the old inline autocompleted suggestion.
2075 { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
2076 "{\"google:verbatimrelevance\":9000,"
2077 "\"google:suggestrelevance\":[9002, 9001]}]",
2078 { { "a", true }, { "ab1", false }, { "ab2", false },
2079 kEmptyExpectedMatch },
2080 { { "ab1", true }, { "ab2", true }, { "ab", true },
2081 kEmptyExpectedMatch },
2082 "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
2083 "{\"google:verbatimrelevance\":9000,"
2084 "\"google:suggestrelevance\":[9002, 9001]}]",
2085 { { "ab1", true }, { "ab2", false }, { "ab", true },
2086 kEmptyExpectedMatch } },
2087 // Ditto, just for a navigation suggestion.
2088 { "[\"a\",[\"ab1.com\", \"ab2.com\"],[],[],"
2089 "{\"google:verbatimrelevance\":9000,"
2090 "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2091 "\"google:suggestrelevance\":[9002, 9001]}]",
2092 { { "a", true }, { "ab1.com", false }, { "ab2.com", false },
2093 kEmptyExpectedMatch },
2094 { { "ab1.com", true }, { "ab2.com", true }, { "ab", true },
2095 kEmptyExpectedMatch },
2096 "[\"ab\",[\"ab1.com\", \"ab2.com\"],[],[],"
2097 "{\"google:verbatimrelevance\":9000,"
2098 "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2099 "\"google:suggestrelevance\":[9002, 9001]}]",
2100 { { "ab1.com", true }, { "ab2.com", false }, { "ab", true },
2101 kEmptyExpectedMatch } },
2102 // A more realistic test of the same situation.
2103 { "[\"a\",[\"abcdef\", \"abcdef.com\", \"abc\"],[],[],"
2104 "{\"google:verbatimrelevance\":900,"
2105 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"QUERY\"],"
2106 "\"google:suggestrelevance\":[1250, 1200, 1000]}]",
2107 { { "a", true }, { "abcdef", false }, { "abcdef.com", false },
2108 { "abc", false } },
2109 { { "abcdef", true }, { "abcdef.com", true }, { "abc", true },
2110 { "ab", true } },
2111 "[\"ab\",[\"abcdef\", \"abcdef.com\", \"abc\"],[],[],"
2112 "{\"google:verbatimrelevance\":900,"
2113 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"QUERY\"],"
2114 "\"google:suggestrelevance\":[1250, 1200, 1000]}]",
2115 { { "abcdef", true }, { "abcdef.com", false }, { "abc", false },
2116 { "ab", true } } },
2118 // Without an original inline autcompletion, a new inline autcompletion
2119 // should be rejected.
2120 { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
2121 "{\"google:verbatimrelevance\":9000,"
2122 "\"google:suggestrelevance\":[8000, 7000]}]",
2123 { { "a", true }, { "ab1", false }, { "ab2", false },
2124 kEmptyExpectedMatch },
2125 { { "ab", true }, { "ab1", true }, { "ab2", true },
2126 kEmptyExpectedMatch },
2127 "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
2128 "{\"google:verbatimrelevance\":9000,"
2129 "\"google:suggestrelevance\":[9002, 9001]}]",
2130 { { "ab", true }, { "ab1", false }, { "ab2", false },
2131 kEmptyExpectedMatch } },
2132 // For the same test except with the queries scored in the opposite order
2133 // on the second JSON response, the queries should be ordered by the second
2134 // response's scores, not the first.
2135 { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
2136 "{\"google:verbatimrelevance\":9000,"
2137 "\"google:suggestrelevance\":[8000, 7000]}]",
2138 { { "a", true }, { "ab1", false }, { "ab2", false },
2139 kEmptyExpectedMatch },
2140 { { "ab", true }, { "ab1", true }, { "ab2", true },
2141 kEmptyExpectedMatch },
2142 "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
2143 "{\"google:verbatimrelevance\":9000,"
2144 "\"google:suggestrelevance\":[9001, 9002]}]",
2145 { { "ab", true }, { "ab2", false }, { "ab1", false },
2146 kEmptyExpectedMatch } },
2147 // Now, the same verifications but with the new inline autocompletion as a
2148 // navsuggestion. The new autocompletion should still be rejected.
2149 { "[\"a\",[\"ab1.com\", \"ab2.com\"],[],[],"
2150 "{\"google:verbatimrelevance\":9000,"
2151 "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2152 "\"google:suggestrelevance\":[8000, 7000]}]",
2153 { { "a", true }, { "ab1.com", false }, { "ab2.com", false },
2154 kEmptyExpectedMatch },
2155 { { "ab", true }, { "ab1.com", true }, { "ab2.com", true },
2156 kEmptyExpectedMatch },
2157 "[\"ab\",[\"ab1.com\", \"ab2.com\"],[],[],"
2158 "{\"google:verbatimrelevance\":9000,"
2159 "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2160 "\"google:suggestrelevance\":[9002, 9001]}]",
2161 { { "ab", true }, { "ab1.com", false }, { "ab2.com", false },
2162 kEmptyExpectedMatch } },
2163 { "[\"a\",[\"ab1.com\", \"ab2.com\"],[],[],"
2164 "{\"google:verbatimrelevance\":9000,"
2165 "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2166 "\"google:suggestrelevance\":[8000, 7000]}]",
2167 { { "a", true }, { "ab1.com", false }, { "ab2.com", false },
2168 kEmptyExpectedMatch },
2169 { { "ab", true }, { "ab1.com", true }, { "ab2.com", true },
2170 kEmptyExpectedMatch },
2171 "[\"ab\",[\"ab1.com\", \"ab2.com\"],[],[],"
2172 "{\"google:verbatimrelevance\":9000,"
2173 "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2174 "\"google:suggestrelevance\":[9001, 9002]}]",
2175 { { "ab", true }, { "ab2.com", false }, { "ab1.com", false },
2176 kEmptyExpectedMatch } },
2178 // It's okay to abandon an inline autocompletion asynchronously.
2179 { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
2180 "{\"google:verbatimrelevance\":9000,"
2181 "\"google:suggestrelevance\":[9002, 9001]}]",
2182 { { "a", true }, { "ab1", false }, { "ab2", false },
2183 kEmptyExpectedMatch },
2184 { { "ab1", true }, { "ab2", true }, { "ab", true },
2185 kEmptyExpectedMatch },
2186 "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
2187 "{\"google:verbatimrelevance\":9000,"
2188 "\"google:suggestrelevance\":[8000, 7000]}]",
2189 { { "ab", true }, { "ab1", true }, { "ab2", false },
2190 kEmptyExpectedMatch } },
2192 // Note: it's possible that the suggest server returns a suggestion with
2193 // an inline autocompletion (that as usual we delay in allowing it to
2194 // be displayed as an inline autocompletion until the next keystroke),
2195 // then, in response to the next keystroke, the server returns a different
2196 // suggestion as an inline autocompletion. This is not likely to happen.
2197 // Regardless, if it does, one could imagine three different behaviors:
2198 // - keep the original inline autocompletion until the next keystroke
2199 // (i.e., don't abandon an inline autocompletion asynchronously), then
2200 // use the new suggestion
2201 // - abandon all inline autocompletions upon the server response, then use
2202 // the new suggestion on the next keystroke
2203 // - ignore the new inline autocompletion provided by the server, yet
2204 // possibly keep the original if it scores well in the most recent
2205 // response, then use the new suggestion on the next keystroke
2206 // All of these behaviors are reasonable. The main thing we want to
2207 // ensure is that the second asynchronous response shouldn't cause *a new*
2208 // inline autocompletion to be displayed. We test that here.
2209 // The current implementation does the third bullet, but all of these
2210 // behaviors seem reasonable.
2211 { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
2212 "{\"google:verbatimrelevance\":9000,"
2213 "\"google:suggestrelevance\":[9002, 9001]}]",
2214 { { "a", true }, { "ab1", false }, { "ab2", false },
2215 kEmptyExpectedMatch },
2216 { { "ab1", true }, { "ab2", true }, { "ab", true },
2217 kEmptyExpectedMatch },
2218 "[\"ab\",[\"ab1\", \"ab3\"],[],[],"
2219 "{\"google:verbatimrelevance\":9000,"
2220 "\"google:suggestrelevance\":[9002, 9900]}]",
2221 { { "ab1", true }, { "ab3", false }, { "ab", true },
2222 kEmptyExpectedMatch } },
2223 { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
2224 "{\"google:verbatimrelevance\":9000,"
2225 "\"google:suggestrelevance\":[9002, 9001]}]",
2226 { { "a", true }, { "ab1", false }, { "ab2", false },
2227 kEmptyExpectedMatch },
2228 { { "ab1", true }, { "ab2", true }, { "ab", true },
2229 kEmptyExpectedMatch },
2230 "[\"ab\",[\"ab1\", \"ab3\"],[],[],"
2231 "{\"google:verbatimrelevance\":9000,"
2232 "\"google:suggestrelevance\":[8000, 9500]}]",
2233 { { "ab", true }, { "ab3", false }, { "ab1", true },
2234 kEmptyExpectedMatch } },
2237 for (size_t i = 0; i < arraysize(cases); ++i) {
2238 // First, send the query "a" and receive the JSON response |first_json|.
2239 ClearAllResults();
2240 QueryForInputAndWaitForFetcherResponses(
2241 ASCIIToUTF16("a"), false, cases[i].first_json, std::string());
2243 // Verify that the matches after the asynchronous results are as expected.
2244 std::string description = "first asynchronous response for input with "
2245 "first_json=" + cases[i].first_json;
2246 CheckMatches(description, arraysize(cases[i].first_async_matches),
2247 cases[i].first_async_matches, provider_->matches());
2249 // Then, send the query "ab" and check the synchronous matches.
2250 description = "synchronous response after the first keystroke after input "
2251 "with first_json=" + cases[i].first_json;
2252 QueryForInput(ASCIIToUTF16("ab"), false, false);
2253 CheckMatches(description, arraysize(cases[i].sync_matches),
2254 cases[i].sync_matches, provider_->matches());
2256 // Finally, get the provided JSON response, |second_json|, and verify the
2257 // matches after the second asynchronous response are as expected.
2258 description = "second asynchronous response after input with first_json=" +
2259 cases[i].first_json + " and second_json=" + cases[i].second_json;
2260 net::TestURLFetcher* second_fetcher =
2261 test_factory_.GetFetcherByID(
2262 SearchProvider::kDefaultProviderURLFetcherID);
2263 ASSERT_TRUE(second_fetcher);
2264 second_fetcher->set_response_code(200);
2265 second_fetcher->SetResponseString(cases[i].second_json);
2266 second_fetcher->delegate()->OnURLFetchComplete(second_fetcher);
2267 RunTillProviderDone();
2268 CheckMatches(description, arraysize(cases[i].second_async_matches),
2269 cases[i].second_async_matches, provider_->matches());
2273 TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
2274 // We hardcode the string "term1" below, so ensure that the search term that
2275 // got added to history already is that string.
2276 ASSERT_EQ(ASCIIToUTF16("term1"), term1_);
2277 base::string16 term = term1_.substr(0, term1_.length() - 1);
2279 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2);
2280 profile_.BlockUntilHistoryProcessesPendingRequests();
2282 struct {
2283 const base::string16 input;
2284 const std::string json;
2285 const std::string matches[6];
2286 } cases[] = {
2287 // The history results outscore the default verbatim score. term2 has more
2288 // visits so it outscores term1. The suggestions are still returned since
2289 // they're server-scored.
2290 { term,
2291 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
2292 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
2293 "\"google:suggestrelevance\":[1, 2, 3]}]",
2294 { "term2", "term1", "term", "a3", "a2", "a1" } },
2295 // Because we already have three suggestions by the time we see the history
2296 // results, they don't get returned.
2297 { term,
2298 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
2299 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
2300 "\"google:verbatimrelevance\":1450,"
2301 "\"google:suggestrelevance\":[1440, 1430, 1420]}]",
2302 { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } },
2303 // If we only have two suggestions, we have room for a history result.
2304 { term,
2305 "[\"term\",[\"a1\", \"a2\"],[],[],"
2306 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
2307 "\"google:verbatimrelevance\":1450,"
2308 "\"google:suggestrelevance\":[1430, 1410]}]",
2309 { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } },
2310 // If we have more than three suggestions, they should all be returned as
2311 // long as we have enough total space for them.
2312 { term,
2313 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
2314 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
2315 "\"google:verbatimrelevance\":1450,"
2316 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]",
2317 { "term", "a1", "a2", "a3", "a4", kNotApplicable } },
2318 { term,
2319 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[],"
2320 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\","
2321 "\"QUERY\", \"QUERY\"],"
2322 "\"google:verbatimrelevance\":1450,"
2323 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]",
2324 { "term", "a1", "a2", "a3", "a4", "a5" } },
2325 { term,
2326 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
2327 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
2328 "\"google:verbatimrelevance\":1450,"
2329 "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]",
2330 { "term", "a1", "a2", "term2", "a3", "a4" } }
2333 for (size_t i = 0; i < arraysize(cases); ++i) {
2334 QueryForInputAndWaitForFetcherResponses(
2335 cases[i].input, false, cases[i].json, std::string());
2337 const std::string description = "for input with json=" + cases[i].json;
2338 const ACMatches& matches = provider_->matches();
2340 // Ensure no extra matches are present.
2341 ASSERT_LE(matches.size(), arraysize(cases[i].matches));
2343 size_t j = 0;
2344 // Ensure that the returned matches equal the expectations.
2345 for (; j < matches.size(); ++j)
2346 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]),
2347 matches[j].contents) << description;
2348 // Ensure that no expected matches are missing.
2349 for (; j < arraysize(cases[i].matches); ++j)
2350 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) <<
2351 "Case # " << i << " " << description;
2355 // Verifies suggest relevance behavior for URL input.
2356 TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
2357 struct DefaultFetcherUrlInputMatch {
2358 const std::string match_contents;
2359 AutocompleteMatch::Type match_type;
2360 bool allowed_to_be_default_match;
2362 const DefaultFetcherUrlInputMatch kEmptyMatch =
2363 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
2364 struct {
2365 const std::string input;
2366 const std::string json;
2367 const DefaultFetcherUrlInputMatch output[4];
2368 } cases[] = {
2369 // Ensure NAVIGATION matches are allowed to be listed first for URL input.
2370 // Non-inlineable matches should not be allowed to be the default match.
2371 // Note that the top-scoring inlineable match is moved to the top
2372 // regardless of its score.
2373 { "a.com", "[\"a.com\",[\"http://b.com/\"],[],[],"
2374 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2375 "\"google:suggestrelevance\":[9999]}]",
2376 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2377 { "b.com", AutocompleteMatchType::NAVSUGGEST, false },
2378 kEmptyMatch, kEmptyMatch } },
2379 { "a.com", "[\"a.com\",[\"https://b.com\"],[],[],"
2380 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2381 "\"google:suggestrelevance\":[9999]}]",
2382 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2383 { "https://b.com", AutocompleteMatchType::NAVSUGGEST, false },
2384 kEmptyMatch, kEmptyMatch } },
2385 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
2386 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2387 "\"google:suggestrelevance\":[9999]}]",
2388 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true },
2389 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2390 kEmptyMatch, kEmptyMatch } },
2391 { "a.com", "[\"a.com\",[\"https://a.com\"],[],[],"
2392 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2393 "\"google:suggestrelevance\":[9999]}]",
2394 { { "https://a.com", AutocompleteMatchType::NAVSUGGEST, true },
2395 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2396 kEmptyMatch, kEmptyMatch } },
2398 // Ensure topmost inlineable SUGGEST matches are NOT allowed for URL
2399 // input. SearchProvider disregards search and verbatim suggested
2400 // relevances.
2401 { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2402 "{\"google:suggestrelevance\":[9999]}]",
2403 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2404 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2405 kEmptyMatch, kEmptyMatch } },
2406 { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2407 "{\"google:suggestrelevance\":[9999]}]",
2408 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2409 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2410 kEmptyMatch, kEmptyMatch } },
2412 // Ensure the fallback mechanism allows inlineable NAVIGATION matches.
2413 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2414 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2415 "\"google:suggestrelevance\":[9999, 9998]}]",
2416 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2417 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2418 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2419 kEmptyMatch } },
2420 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2421 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2422 "\"google:suggestrelevance\":[9998, 9997],"
2423 "\"google:verbatimrelevance\":9999}]",
2424 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2425 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2426 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2427 kEmptyMatch } },
2429 // Ensure non-inlineable SUGGEST matches are allowed for URL input
2430 // assuming the best inlineable match is not a query (i.e., is a
2431 // NAVSUGGEST). The best inlineable match will be at the top of the
2432 // list regardless of its score.
2433 { "a.com", "[\"a.com\",[\"info\"],[],[],"
2434 "{\"google:suggestrelevance\":[9999]}]",
2435 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2436 { "info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2437 kEmptyMatch, kEmptyMatch } },
2438 { "a.com", "[\"a.com\",[\"info\"],[],[],"
2439 "{\"google:suggestrelevance\":[9999]}]",
2440 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2441 { "info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2442 kEmptyMatch, kEmptyMatch } },
2445 for (size_t i = 0; i < arraysize(cases); ++i) {
2446 // Send the query twice in order to have a synchronous pass after the first
2447 // response is received. This is necessary because SearchProvider doesn't
2448 // allow an asynchronous response to change the default match.
2449 for (size_t j = 0; j < 2; ++j) {
2450 QueryForInputAndWaitForFetcherResponses(
2451 ASCIIToUTF16(cases[i].input), false, cases[i].json, std::string());
2454 SCOPED_TRACE("input=" + cases[i].input + " json=" + cases[i].json);
2455 size_t j = 0;
2456 const ACMatches& matches = provider_->matches();
2457 ASSERT_LE(matches.size(), arraysize(cases[i].output));
2458 // Ensure that the returned matches equal the expectations.
2459 for (; j < matches.size(); ++j) {
2460 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2461 matches[j].contents);
2462 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2463 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2464 matches[j].allowed_to_be_default_match);
2466 // Ensure that no expected matches are missing.
2467 for (; j < arraysize(cases[i].output); ++j) {
2468 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2469 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2470 cases[i].output[j].match_type);
2471 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2476 // A basic test that verifies the field trial triggered parsing logic.
2477 TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) {
2478 QueryForInputAndWaitForFetcherResponses(
2479 ASCIIToUTF16("foo"), false,
2480 "[\"foo\",[\"foo bar\"],[\"\"],[],"
2481 "{\"google:suggesttype\":[\"QUERY\"],"
2482 "\"google:fieldtrialtriggered\":true}]",
2483 std::string());
2486 // Check for the match and field trial triggered bits.
2487 AutocompleteMatch match;
2488 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match));
2489 ProvidersInfo providers_info;
2490 provider_->AddProviderInfo(&providers_info);
2491 ASSERT_EQ(1U, providers_info.size());
2492 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2493 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size());
2496 // Reset the session and check that bits are reset.
2497 provider_->ResetSession();
2498 ProvidersInfo providers_info;
2499 provider_->AddProviderInfo(&providers_info);
2500 ASSERT_EQ(1U, providers_info.size());
2501 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2502 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size());
2506 // Verifies inline autocompletion of navigational results.
2507 TEST_F(SearchProviderTest, NavigationInline) {
2508 struct {
2509 const std::string input;
2510 const std::string url;
2511 // Test the expected fill_into_edit, which may drop "http://".
2512 // Some cases do not trim "http://" to match from the start of the scheme.
2513 const std::string fill_into_edit;
2514 const std::string inline_autocompletion;
2515 const bool allowed_to_be_default_match_in_regular_mode;
2516 const bool allowed_to_be_default_match_in_prevent_inline_mode;
2517 } cases[] = {
2518 // Do not inline matches that do not contain the input; trim http as needed.
2519 { "x", "http://www.abc.com",
2520 "www.abc.com", std::string(), false, false },
2521 { "https:", "http://www.abc.com",
2522 "www.abc.com", std::string(), false, false },
2523 { "http://www.abc.com/a", "http://www.abc.com",
2524 "http://www.abc.com", std::string(), false,
2525 false },
2527 // Do not inline matches with invalid input prefixes; trim http as needed.
2528 { "ttp", "http://www.abc.com",
2529 "www.abc.com", std::string(), false, false },
2530 { "://w", "http://www.abc.com",
2531 "www.abc.com", std::string(), false, false },
2532 { "ww.", "http://www.abc.com",
2533 "www.abc.com", std::string(), false, false },
2534 { ".ab", "http://www.abc.com",
2535 "www.abc.com", std::string(), false, false },
2536 { "bc", "http://www.abc.com",
2537 "www.abc.com", std::string(), false, false },
2538 { ".com", "http://www.abc.com",
2539 "www.abc.com", std::string(), false, false },
2541 // Do not inline matches that omit input domain labels; trim http as needed.
2542 { "www.a", "http://a.com",
2543 "a.com", std::string(), false, false },
2544 { "http://www.a", "http://a.com",
2545 "http://a.com", std::string(), false, false },
2546 { "www.a", "ftp://a.com",
2547 "ftp://a.com", std::string(), false, false },
2548 { "ftp://www.a", "ftp://a.com",
2549 "ftp://a.com", std::string(), false, false },
2551 // Input matching but with nothing to inline will not yield an offset, but
2552 // will be allowed to be default.
2553 { "abc.com", "http://www.abc.com",
2554 "www.abc.com", std::string(), true, true },
2555 { "abc.com/", "http://www.abc.com",
2556 "www.abc.com", std::string(), true, true },
2557 { "http://www.abc.com", "http://www.abc.com",
2558 "http://www.abc.com", std::string(), true, true },
2559 { "http://www.abc.com/", "http://www.abc.com",
2560 "http://www.abc.com", std::string(), true, true },
2562 // Inputs with trailing whitespace should inline when possible.
2563 { "abc.com ", "http://www.abc.com",
2564 "www.abc.com", std::string(), true, true },
2565 { "abc.com/ ", "http://www.abc.com",
2566 "www.abc.com", std::string(), true, true },
2567 { "abc.com ", "http://www.abc.com/bar",
2568 "www.abc.com/bar", "/bar", false, false },
2570 // A suggestion that's equivalent to what the input gets fixed up to
2571 // should be inlined.
2572 { "abc.com:", "http://abc.com/",
2573 "abc.com", std::string(), true, true },
2574 { "abc.com:", "http://www.abc.com",
2575 "www.abc.com", std::string(), true, true },
2577 // Inline matches when the input is a leading substring of the scheme.
2578 { "h", "http://www.abc.com",
2579 "http://www.abc.com", "ttp://www.abc.com", true, false },
2580 { "http", "http://www.abc.com",
2581 "http://www.abc.com", "://www.abc.com", true, false },
2583 // Inline matches when the input is a leading substring of the full URL.
2584 { "http:", "http://www.abc.com",
2585 "http://www.abc.com", "//www.abc.com", true, false },
2586 { "http://w", "http://www.abc.com",
2587 "http://www.abc.com", "ww.abc.com", true, false },
2588 { "http://www.", "http://www.abc.com",
2589 "http://www.abc.com", "abc.com", true, false },
2590 { "http://www.ab", "http://www.abc.com",
2591 "http://www.abc.com", "c.com", true, false },
2592 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
2593 "http://www.abc.com/path/file.htm?q=x#foo",
2594 "ath/file.htm?q=x#foo",
2595 true, false },
2596 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo",
2597 "http://abc.com/path/file.htm?q=x#foo",
2598 "ath/file.htm?q=x#foo",
2599 true, false},
2601 // Inline matches with valid URLPrefixes; only trim "http://".
2602 { "w", "http://www.abc.com",
2603 "www.abc.com", "ww.abc.com", true, false },
2604 { "www.a", "http://www.abc.com",
2605 "www.abc.com", "bc.com", true, false },
2606 { "abc", "http://www.abc.com",
2607 "www.abc.com", ".com", true, false },
2608 { "abc.c", "http://www.abc.com",
2609 "www.abc.com", "om", true, false },
2610 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
2611 "www.abc.com/path/file.htm?q=x#foo",
2612 "ath/file.htm?q=x#foo",
2613 true, false },
2614 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo",
2615 "abc.com/path/file.htm?q=x#foo",
2616 "ath/file.htm?q=x#foo",
2617 true, false },
2619 // Inline matches using the maximal URLPrefix components.
2620 { "h", "http://help.com",
2621 "help.com", "elp.com", true, false },
2622 { "http", "http://http.com",
2623 "http.com", ".com", true, false },
2624 { "h", "http://www.help.com",
2625 "www.help.com", "elp.com", true, false },
2626 { "http", "http://www.http.com",
2627 "www.http.com", ".com", true, false },
2628 { "w", "http://www.www.com",
2629 "www.www.com", "ww.com", true, false },
2631 // Test similar behavior for the ftp and https schemes.
2632 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
2633 "ftp://www.abc.com/path/file.htm?q=x#foo",
2634 "c.com/path/file.htm?q=x#foo", true, false },
2635 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
2636 "ftp://www.abc.com/path/file.htm?q=x#foo",
2637 "c.com/path/file.htm?q=x#foo", true, false },
2638 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
2639 "ftp://www.abc.com/path/file.htm?q=x#foo",
2640 "c.com/path/file.htm?q=x#foo", true, false },
2641 { "ab", "ftp://abc.com/path/file.htm?q=x#foo",
2642 "ftp://abc.com/path/file.htm?q=x#foo",
2643 "c.com/path/file.htm?q=x#foo", true, false },
2644 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo",
2645 "https://www.abc.com/path/file.htm?q=x#foo",
2646 "c.com/path/file.htm?q=x#foo",
2647 true, false },
2648 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo",
2649 "https://www.abc.com/path/file.htm?q=x#foo",
2650 "c.com/path/file.htm?q=x#foo", true, false },
2651 { "ab", "https://www.abc.com/path/file.htm?q=x#foo",
2652 "https://www.abc.com/path/file.htm?q=x#foo",
2653 "c.com/path/file.htm?q=x#foo", true, false },
2654 { "ab", "https://abc.com/path/file.htm?q=x#foo",
2655 "https://abc.com/path/file.htm?q=x#foo",
2656 "c.com/path/file.htm?q=x#foo", true, false },
2658 // Forced query input should inline and retain the "?" prefix.
2659 { "?http://www.ab", "http://www.abc.com",
2660 "?http://www.abc.com", "c.com", true, false },
2661 { "?www.ab", "http://www.abc.com",
2662 "?www.abc.com", "c.com", true, false },
2663 { "?ab", "http://www.abc.com",
2664 "?www.abc.com", "c.com", true, false },
2665 { "?abc.com", "http://www.abc.com",
2666 "?www.abc.com", std::string(), true, true },
2669 for (size_t i = 0; i < arraysize(cases); ++i) {
2670 // First test regular mode.
2671 QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2672 SearchSuggestionParser::NavigationResult result(
2673 ChromeAutocompleteSchemeClassifier(&profile_), GURL(cases[i].url),
2674 AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
2675 false, 0, false, ASCIIToUTF16(cases[i].input), std::string());
2676 result.set_received_after_last_keystroke(false);
2677 AutocompleteMatch match(provider_->NavigationToMatch(result));
2678 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2679 match.inline_autocompletion);
2680 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit);
2681 EXPECT_EQ(cases[i].allowed_to_be_default_match_in_regular_mode,
2682 match.allowed_to_be_default_match);
2684 // Then test prevent-inline-autocomplete mode.
2685 QueryForInput(ASCIIToUTF16(cases[i].input), true, false);
2686 SearchSuggestionParser::NavigationResult result_prevent_inline(
2687 ChromeAutocompleteSchemeClassifier(&profile_), GURL(cases[i].url),
2688 AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
2689 false, 0, false, ASCIIToUTF16(cases[i].input), std::string());
2690 result_prevent_inline.set_received_after_last_keystroke(false);
2691 AutocompleteMatch match_prevent_inline(
2692 provider_->NavigationToMatch(result_prevent_inline));
2693 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2694 match_prevent_inline.inline_autocompletion);
2695 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit),
2696 match_prevent_inline.fill_into_edit);
2697 EXPECT_EQ(cases[i].allowed_to_be_default_match_in_prevent_inline_mode,
2698 match_prevent_inline.allowed_to_be_default_match);
2702 // Verifies that "http://" is not trimmed for input that is a leading substring.
2703 TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
2704 const base::string16 input(ASCIIToUTF16("ht"));
2705 const base::string16 url(ASCIIToUTF16("http://a.com"));
2706 SearchSuggestionParser::NavigationResult result(
2707 ChromeAutocompleteSchemeClassifier(&profile_), GURL(url),
2708 AutocompleteMatchType::NAVSUGGEST,
2709 base::string16(), std::string(), false, 0, false, input, std::string());
2710 result.set_received_after_last_keystroke(false);
2712 // Check the offset and strings when inline autocompletion is allowed.
2713 QueryForInput(input, false, false);
2714 AutocompleteMatch match_inline(provider_->NavigationToMatch(result));
2715 EXPECT_EQ(url, match_inline.fill_into_edit);
2716 EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion);
2717 EXPECT_TRUE(match_inline.allowed_to_be_default_match);
2718 EXPECT_EQ(url, match_inline.contents);
2720 // Check the same strings when inline autocompletion is prevented.
2721 QueryForInput(input, true, false);
2722 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result));
2723 EXPECT_EQ(url, match_prevent.fill_into_edit);
2724 EXPECT_FALSE(match_prevent.allowed_to_be_default_match);
2725 EXPECT_EQ(url, match_prevent.contents);
2728 // Verifies that input "w" marks a more significant domain label than "www.".
2729 TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
2730 QueryForInput(ASCIIToUTF16("w"), false, false);
2731 SearchSuggestionParser::NavigationResult result(
2732 ChromeAutocompleteSchemeClassifier(&profile_),
2733 GURL("http://www.wow.com"), AutocompleteMatchType::NAVSUGGEST,
2734 base::string16(), std::string(), false, 0, false, ASCIIToUTF16("w"),
2735 std::string());
2736 result.set_received_after_last_keystroke(false);
2737 AutocompleteMatch match(provider_->NavigationToMatch(result));
2738 EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion);
2739 EXPECT_TRUE(match.allowed_to_be_default_match);
2740 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit);
2741 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents);
2743 // Ensure that the match for input "w" is marked on "wow" and not "www".
2744 ASSERT_EQ(3U, match.contents_class.size());
2745 EXPECT_EQ(0U, match.contents_class[0].offset);
2746 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2747 match.contents_class[0].style);
2748 EXPECT_EQ(4U, match.contents_class[1].offset);
2749 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL |
2750 AutocompleteMatch::ACMatchClassification::MATCH,
2751 match.contents_class[1].style);
2752 EXPECT_EQ(5U, match.contents_class[2].offset);
2753 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2754 match.contents_class[2].style);
2757 #if !defined(OS_WIN)
2758 // Verify entity suggestion parsing.
2759 TEST_F(SearchProviderTest, ParseEntitySuggestion) {
2760 struct Match {
2761 std::string contents;
2762 std::string description;
2763 std::string query_params;
2764 std::string fill_into_edit;
2765 AutocompleteMatchType::Type type;
2767 const Match kEmptyMatch = {
2768 kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable,
2769 AutocompleteMatchType::NUM_TYPES};
2771 struct {
2772 const std::string input_text;
2773 const std::string response_json;
2774 const Match matches[5];
2775 } cases[] = {
2776 // A query and an entity suggestion with different search terms.
2777 { "x",
2778 "[\"x\",[\"xy\", \"yy\"],[\"\",\"\"],[],"
2779 " {\"google:suggestdetail\":[{},"
2780 " {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
2781 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
2782 { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2783 { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
2784 { "xy", "A", "p=v", "yy",
2785 AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
2786 kEmptyMatch,
2787 kEmptyMatch
2790 // A query and an entity suggestion with same search terms.
2791 { "x",
2792 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
2793 " {\"google:suggestdetail\":[{},"
2794 " {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
2795 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
2796 { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2797 { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
2798 { "xy", "A", "p=v", "xy",
2799 AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
2800 kEmptyMatch,
2801 kEmptyMatch
2805 for (size_t i = 0; i < arraysize(cases); ++i) {
2806 QueryForInputAndWaitForFetcherResponses(
2807 ASCIIToUTF16(cases[i].input_text), false, cases[i].response_json,
2808 std::string());
2810 const ACMatches& matches = provider_->matches();
2811 ASSERT_FALSE(matches.empty());
2813 SCOPED_TRACE("for input with json = " + cases[i].response_json);
2815 ASSERT_LE(matches.size(), arraysize(cases[i].matches));
2816 size_t j = 0;
2817 // Ensure that the returned matches equal the expectations.
2818 for (; j < matches.size(); ++j) {
2819 const Match& match = cases[i].matches[j];
2820 SCOPED_TRACE(" and match index: " + base::IntToString(j));
2821 EXPECT_EQ(match.contents,
2822 base::UTF16ToUTF8(matches[j].contents));
2823 EXPECT_EQ(match.description,
2824 base::UTF16ToUTF8(matches[j].description));
2825 EXPECT_EQ(match.query_params,
2826 matches[j].search_terms_args->suggest_query_params);
2827 EXPECT_EQ(match.fill_into_edit,
2828 base::UTF16ToUTF8(matches[j].fill_into_edit));
2829 EXPECT_EQ(match.type, matches[j].type);
2831 // Ensure that no expected matches are missing.
2832 for (; j < arraysize(cases[i].matches); ++j) {
2833 SCOPED_TRACE(" and match index: " + base::IntToString(j));
2834 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
2835 EXPECT_EQ(cases[i].matches[j].description, kNotApplicable);
2836 EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
2837 EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
2838 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
2842 #endif // !defined(OS_WIN)
2845 // A basic test that verifies the prefetch metadata parsing logic.
2846 TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
2847 struct Match {
2848 std::string contents;
2849 bool allowed_to_be_prefetched;
2850 AutocompleteMatchType::Type type;
2851 bool from_keyword;
2853 const Match kEmptyMatch = { kNotApplicable,
2854 false,
2855 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2856 false };
2858 struct {
2859 const std::string input_text;
2860 bool prefer_keyword_provider_results;
2861 const std::string default_provider_response_json;
2862 const std::string keyword_provider_response_json;
2863 const Match matches[5];
2864 } cases[] = {
2865 // Default provider response does not have prefetch details. Ensure that the
2866 // suggestions are not marked as prefetch query.
2867 { "a",
2868 false,
2869 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2870 std::string(),
2871 { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2872 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2873 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2874 kEmptyMatch,
2875 kEmptyMatch
2878 // Ensure that default provider suggest response prefetch details are
2879 // parsed and recorded in AutocompleteMatch.
2880 { "ab",
2881 false,
2882 "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[],"
2883 "{\"google:clientdata\":{\"phi\": 0},"
2884 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"],"
2885 "\"google:suggestrelevance\":[999, 12, 1]}]",
2886 std::string(),
2887 { { "ab", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2888 { "abc", true, AutocompleteMatchType::SEARCH_SUGGEST, false },
2889 { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2890 { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2891 kEmptyMatch
2894 // Default provider suggest response has prefetch details.
2895 // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for
2896 // the same query string. Ensure that the prefetch details from
2897 // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match.
2898 { "ab",
2899 false,
2900 "[\"ab\",[\"ab\", \"http://ab.com\"],[],[],"
2901 "{\"google:clientdata\":{\"phi\": 0},"
2902 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2903 "\"google:suggestrelevance\":[99, 98]}]",
2904 std::string(),
2905 { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2906 {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2907 kEmptyMatch,
2908 kEmptyMatch,
2909 kEmptyMatch
2912 // Default provider response has prefetch details. We prefer keyword
2913 // provider results. Ensure that prefetch bit for a suggestion from the
2914 // default search provider does not get copied onto a higher-scoring match
2915 // for the same query string from the keyword provider.
2916 { "k a",
2917 true,
2918 "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0},"
2919 "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
2920 "\"google:suggestrelevance\":[9, 12]}]",
2921 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2922 { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true},
2923 { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2924 { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2925 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true },
2926 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true }
2931 for (size_t i = 0; i < arraysize(cases); ++i) {
2932 QueryForInputAndWaitForFetcherResponses(
2933 ASCIIToUTF16(cases[i].input_text),
2934 cases[i].prefer_keyword_provider_results,
2935 cases[i].default_provider_response_json,
2936 cases[i].prefer_keyword_provider_results ?
2937 cases[i].keyword_provider_response_json : std::string());
2939 const std::string description =
2940 "for input with json =" + cases[i].default_provider_response_json;
2941 const ACMatches& matches = provider_->matches();
2942 // The top match must inline and score as highly as calculated verbatim.
2943 ASSERT_FALSE(matches.empty());
2944 EXPECT_GE(matches[0].relevance, 1300);
2946 ASSERT_LE(matches.size(), arraysize(cases[i].matches));
2947 // Ensure that the returned matches equal the expectations.
2948 for (size_t j = 0; j < matches.size(); ++j) {
2949 SCOPED_TRACE(description);
2950 EXPECT_EQ(cases[i].matches[j].contents,
2951 base::UTF16ToUTF8(matches[j].contents));
2952 EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
2953 SearchProvider::ShouldPrefetch(matches[j]));
2954 EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
2955 EXPECT_EQ(cases[i].matches[j].from_keyword,
2956 matches[j].keyword == ASCIIToUTF16("k"));
2961 TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_InvalidResponse) {
2962 ClearAllResults();
2964 std::string input_str("abc");
2965 QueryForInputAndWaitForFetcherResponses(
2966 ASCIIToUTF16(input_str), false, "this is a bad non-json response",
2967 std::string());
2969 const ACMatches& matches = provider_->matches();
2971 // Should have exactly one "search what you typed" match
2972 ASSERT_TRUE(matches.size() == 1);
2973 EXPECT_EQ(input_str, base::UTF16ToUTF8(matches[0].contents));
2974 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2975 matches[0].type);
2978 // A basic test that verifies that the XSSI guarded JSON response is parsed
2979 // correctly.
2980 TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_ValidResponses) {
2981 struct Match {
2982 std::string contents;
2983 AutocompleteMatchType::Type type;
2985 const Match kEmptyMatch = {
2986 kNotApplicable, AutocompleteMatchType::NUM_TYPES
2989 struct {
2990 const std::string input_text;
2991 const std::string default_provider_response_json;
2992 const Match matches[4];
2993 } cases[] = {
2994 // No XSSI guard.
2995 { "a",
2996 "[\"a\",[\"b\", \"c\"],[],[],"
2997 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
2998 "\"google:suggestrelevance\":[1, 2]}]",
2999 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3000 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3001 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3002 kEmptyMatch,
3005 // Standard XSSI guard - )]}'\n.
3006 { "a",
3007 ")]}'\n[\"a\",[\"b\", \"c\"],[],[],"
3008 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3009 "\"google:suggestrelevance\":[1, 2]}]",
3010 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3011 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3012 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3013 kEmptyMatch,
3016 // Modified XSSI guard - contains "[".
3017 { "a",
3018 ")]}'\n[)\"[\"a\",[\"b\", \"c\"],[],[],"
3019 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3020 "\"google:suggestrelevance\":[1, 2]}]",
3021 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3022 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3023 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3024 kEmptyMatch,
3029 for (size_t i = 0; i < arraysize(cases); ++i) {
3030 ClearAllResults();
3031 QueryForInputAndWaitForFetcherResponses(
3032 ASCIIToUTF16(cases[i].input_text), false,
3033 cases[i].default_provider_response_json, std::string());
3035 const ACMatches& matches = provider_->matches();
3036 // The top match must inline and score as highly as calculated verbatim.
3037 ASSERT_FALSE(matches.empty());
3038 EXPECT_GE(matches[0].relevance, 1300);
3040 SCOPED_TRACE("for case: " + base::IntToString(i));
3041 ASSERT_LE(matches.size(), arraysize(cases[i].matches));
3042 size_t j = 0;
3043 // Ensure that the returned matches equal the expectations.
3044 for (; j < matches.size(); ++j) {
3045 SCOPED_TRACE("and match: " + base::IntToString(j));
3046 EXPECT_EQ(cases[i].matches[j].contents,
3047 base::UTF16ToUTF8(matches[j].contents));
3048 EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
3050 for (; j < arraysize(cases[i].matches); ++j) {
3051 SCOPED_TRACE("and match: " + base::IntToString(j));
3052 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
3053 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
3058 // Test that deletion url gets set on an AutocompleteMatch when available for a
3059 // personalized query or a personalized URL.
3060 TEST_F(SearchProviderTest, ParseDeletionUrl) {
3061 struct Match {
3062 std::string contents;
3063 std::string deletion_url;
3064 AutocompleteMatchType::Type type;
3067 const Match kEmptyMatch = {
3068 kNotApplicable, std::string(), AutocompleteMatchType::NUM_TYPES
3071 const char* url[] = {
3072 "http://defaultturl/complete/deleteitems"
3073 "?delq=ab&client=chrome&deltok=xsrf124",
3074 "http://defaultturl/complete/deleteitems"
3075 "?delq=www.amazon.com&client=chrome&deltok=xsrf123",
3078 struct {
3079 const std::string input_text;
3080 const std::string response_json;
3081 const Match matches[5];
3082 } cases[] = {
3083 // A deletion URL on a personalized query should be reflected in the
3084 // resulting AutocompleteMatch.
3085 { "a",
3086 "[\"a\",[\"ab\", \"ac\",\"www.amazon.com\"],[],[],"
3087 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
3088 "\"PERSONALIZED_NAVIGATION\"],"
3089 "\"google:suggestrelevance\":[3, 2, 1],"
3090 "\"google:suggestdetail\":[{\"du\":"
3091 "\"/complete/deleteitems?delq=ab&client=chrome"
3092 "&deltok=xsrf124\"}, {}, {\"du\":"
3093 "\"/complete/deleteitems?delq=www.amazon.com&"
3094 "client=chrome&deltok=xsrf123\"}]}]",
3095 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3096 { "ab", url[0], AutocompleteMatchType::SEARCH_SUGGEST },
3097 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
3098 { "www.amazon.com", url[1],
3099 AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
3100 kEmptyMatch,
3103 // Personalized queries or a personalized URL without deletion URLs
3104 // shouldn't cause errors.
3105 { "a",
3106 "[\"a\",[\"ab\", \"ac\"],[],[],"
3107 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
3108 "\"PERSONALIZED_NAVIGATION\"],"
3109 "\"google:suggestrelevance\":[1, 2],"
3110 "\"google:suggestdetail\":[{}, {}]}]",
3111 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3112 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
3113 { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
3114 { "www.amazon.com", "",
3115 AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
3116 kEmptyMatch,
3119 // Personalized queries or a personalized URL without
3120 // google:suggestdetail shouldn't cause errors.
3121 { "a",
3122 "[\"a\",[\"ab\", \"ac\"],[],[],"
3123 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
3124 "\"PERSONALIZED_NAVIGATION\"],"
3125 "\"google:suggestrelevance\":[1, 2]}]",
3126 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3127 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
3128 { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
3129 { "www.amazon.com", "",
3130 AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
3131 kEmptyMatch,
3136 for (size_t i = 0; i < arraysize(cases); ++i) {
3137 QueryForInputAndWaitForFetcherResponses(
3138 ASCIIToUTF16(cases[i].input_text), false, cases[i].response_json,
3139 std::string());
3141 const ACMatches& matches = provider_->matches();
3142 ASSERT_FALSE(matches.empty());
3144 SCOPED_TRACE("for input with json = " + cases[i].response_json);
3146 for (size_t j = 0; j < matches.size(); ++j) {
3147 const Match& match = cases[i].matches[j];
3148 SCOPED_TRACE(" and match index: " + base::IntToString(j));
3149 EXPECT_EQ(match.contents, base::UTF16ToUTF8(matches[j].contents));
3150 EXPECT_EQ(match.deletion_url, matches[j].GetAdditionalInfo(
3151 "deletion_url"));
3156 TEST_F(SearchProviderTest, ReflectsBookmarkBarState) {
3157 profile_.GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, false);
3158 base::string16 term = term1_.substr(0, term1_.length() - 1);
3159 QueryForInput(term, true, false);
3160 ASSERT_FALSE(provider_->matches().empty());
3161 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3162 provider_->matches()[0].type);
3163 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
3164 EXPECT_FALSE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
3166 profile_.GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
3167 term = term1_.substr(0, term1_.length() - 1);
3168 QueryForInput(term, true, false);
3169 ASSERT_FALSE(provider_->matches().empty());
3170 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3171 provider_->matches()[0].type);
3172 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
3173 EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
3176 TEST_F(SearchProviderTest, CanSendURL) {
3177 TemplateURLData template_url_data;
3178 template_url_data.short_name = ASCIIToUTF16("t");
3179 template_url_data.SetURL("http://www.google.com/{searchTerms}");
3180 template_url_data.suggestions_url = "http://www.google.com/{searchTerms}";
3181 template_url_data.instant_url = "http://does/not/exist?strk=1";
3182 template_url_data.search_terms_replacement_key = "strk";
3183 template_url_data.id = SEARCH_ENGINE_GOOGLE;
3184 TemplateURL google_template_url(template_url_data);
3186 // Create field trial.
3187 CreateFieldTrial(OmniboxFieldTrial::kZeroSuggestRule, true);
3189 ChromeAutocompleteProviderClient client(&profile_);
3191 // Not signed in.
3192 EXPECT_FALSE(SearchProvider::CanSendURL(
3193 GURL("http://www.google.com/search"),
3194 GURL("https://www.google.com/complete/search"), &google_template_url,
3195 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3196 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(&profile_);
3197 signin->SetAuthenticatedUsername("test");
3199 // All conditions should be met.
3200 EXPECT_TRUE(SearchProvider::CanSendURL(
3201 GURL("http://www.google.com/search"),
3202 GURL("https://www.google.com/complete/search"), &google_template_url,
3203 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3205 // Not in field trial.
3206 ResetFieldTrialList();
3207 CreateFieldTrial(OmniboxFieldTrial::kZeroSuggestRule, false);
3208 EXPECT_FALSE(SearchProvider::CanSendURL(
3209 GURL("http://www.google.com/search"),
3210 GURL("https://www.google.com/complete/search"), &google_template_url,
3211 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3212 ResetFieldTrialList();
3213 CreateFieldTrial(OmniboxFieldTrial::kZeroSuggestRule, true);
3215 // Invalid page URL.
3216 EXPECT_FALSE(SearchProvider::CanSendURL(
3217 GURL("badpageurl"),
3218 GURL("https://www.google.com/complete/search"), &google_template_url,
3219 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3221 // Invalid page classification.
3222 EXPECT_FALSE(SearchProvider::CanSendURL(
3223 GURL("http://www.google.com/search"),
3224 GURL("https://www.google.com/complete/search"), &google_template_url,
3225 metrics::OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS,
3226 SearchTermsData(), &client));
3228 // Invalid page classification.
3229 EXPECT_FALSE(SearchProvider::CanSendURL(
3230 GURL("http://www.google.com/search"),
3231 GURL("https://www.google.com/complete/search"), &google_template_url,
3232 metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
3233 SearchTermsData(), &client));
3235 // HTTPS page URL on same domain as provider.
3236 EXPECT_TRUE(SearchProvider::CanSendURL(
3237 GURL("https://www.google.com/search"),
3238 GURL("https://www.google.com/complete/search"),
3239 &google_template_url, metrics::OmniboxEventProto::OTHER,
3240 SearchTermsData(), &client));
3242 // Non-HTTP[S] page URL on same domain as provider.
3243 EXPECT_FALSE(SearchProvider::CanSendURL(
3244 GURL("ftp://www.google.com/search"),
3245 GURL("https://www.google.com/complete/search"), &google_template_url,
3246 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3248 // Non-HTTP page URL on different domain.
3249 EXPECT_FALSE(SearchProvider::CanSendURL(
3250 GURL("https://www.notgoogle.com/search"),
3251 GURL("https://www.google.com/complete/search"), &google_template_url,
3252 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3254 // Non-HTTPS provider.
3255 EXPECT_FALSE(SearchProvider::CanSendURL(
3256 GURL("http://www.google.com/search"),
3257 GURL("http://www.google.com/complete/search"), &google_template_url,
3258 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3260 // Suggest disabled.
3261 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, false);
3262 EXPECT_FALSE(SearchProvider::CanSendURL(
3263 GURL("http://www.google.com/search"),
3264 GURL("https://www.google.com/complete/search"), &google_template_url,
3265 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3266 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true);
3268 // Incognito.
3269 ChromeAutocompleteProviderClient client_incognito(
3270 profile_.GetOffTheRecordProfile());
3271 EXPECT_FALSE(SearchProvider::CanSendURL(
3272 GURL("http://www.google.com/search"),
3273 GURL("https://www.google.com/complete/search"), &google_template_url,
3274 metrics::OmniboxEventProto::OTHER, SearchTermsData(),
3275 &client_incognito));
3277 // Tab sync not enabled.
3278 profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncKeepEverythingSynced,
3279 false);
3280 profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, false);
3281 EXPECT_FALSE(SearchProvider::CanSendURL(
3282 GURL("http://www.google.com/search"),
3283 GURL("https://www.google.com/complete/search"), &google_template_url,
3284 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3285 profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, true);
3287 // Tab sync is encrypted.
3288 ProfileSyncService* service =
3289 ProfileSyncServiceFactory::GetInstance()->GetForProfile(&profile_);
3290 syncer::ModelTypeSet encrypted_types = service->GetEncryptedDataTypes();
3291 encrypted_types.Put(syncer::SESSIONS);
3292 service->OnEncryptedTypesChanged(encrypted_types, false);
3293 EXPECT_FALSE(SearchProvider::CanSendURL(
3294 GURL("http://www.google.com/search"),
3295 GURL("https://www.google.com/complete/search"), &google_template_url,
3296 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3297 encrypted_types.Remove(syncer::SESSIONS);
3298 service->OnEncryptedTypesChanged(encrypted_types, false);
3300 // Check that there were no side effects from previous tests.
3301 EXPECT_TRUE(SearchProvider::CanSendURL(
3302 GURL("http://www.google.com/search"),
3303 GURL("https://www.google.com/complete/search"), &google_template_url,
3304 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3307 TEST_F(SearchProviderTest, TestDeleteMatch) {
3308 AutocompleteMatch match(
3309 provider_.get(), 0, true, AutocompleteMatchType::SEARCH_SUGGEST);
3310 match.RecordAdditionalInfo(
3311 SearchProvider::kDeletionUrlKey,
3312 "https://www.google.com/complete/deleteitem?q=foo");
3314 // Test a successful deletion request.
3315 provider_->matches_.push_back(match);
3316 provider_->DeleteMatch(match);
3317 EXPECT_FALSE(provider_->deletion_handlers_.empty());
3318 EXPECT_TRUE(provider_->matches_.empty());
3319 // Set up a default fetcher with provided results.
3320 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3321 SearchProvider::kDeletionURLFetcherID);
3322 ASSERT_TRUE(fetcher);
3323 fetcher->set_response_code(200);
3324 fetcher->delegate()->OnURLFetchComplete(fetcher);
3325 EXPECT_TRUE(provider_->deletion_handlers_.empty());
3326 EXPECT_TRUE(provider_->is_success());
3328 // Test a failing deletion request.
3329 provider_->matches_.push_back(match);
3330 provider_->DeleteMatch(match);
3331 EXPECT_FALSE(provider_->deletion_handlers_.empty());
3332 // Set up a default fetcher with provided results.
3333 fetcher = test_factory_.GetFetcherByID(
3334 SearchProvider::kDeletionURLFetcherID);
3335 ASSERT_TRUE(fetcher);
3336 fetcher->set_response_code(500);
3337 fetcher->delegate()->OnURLFetchComplete(fetcher);
3338 EXPECT_TRUE(provider_->deletion_handlers_.empty());
3339 EXPECT_FALSE(provider_->is_success());
3342 TEST_F(SearchProviderTest, TestDeleteHistoryQueryMatch) {
3343 GURL term_url(
3344 AddSearchToHistory(default_t_url_, ASCIIToUTF16("flash games"), 1));
3345 profile_.BlockUntilHistoryProcessesPendingRequests();
3347 AutocompleteMatch games;
3348 QueryForInput(ASCIIToUTF16("fla"), false, false);
3349 profile_.BlockUntilHistoryProcessesPendingRequests();
3350 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
3351 ASSERT_TRUE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games));
3353 size_t matches_before = provider_->matches().size();
3354 provider_->DeleteMatch(games);
3355 EXPECT_EQ(matches_before - 1, provider_->matches().size());
3357 // Process history deletions.
3358 profile_.BlockUntilHistoryProcessesPendingRequests();
3360 // Check that the match is gone.
3361 QueryForInput(ASCIIToUTF16("fla"), false, false);
3362 profile_.BlockUntilHistoryProcessesPendingRequests();
3363 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
3364 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games));
3367 // Verifies that duplicates are preserved in AddMatchToMap().
3368 TEST_F(SearchProviderTest, CheckDuplicateMatchesSaved) {
3369 AddSearchToHistory(default_t_url_, ASCIIToUTF16("a"), 1);
3370 AddSearchToHistory(default_t_url_, ASCIIToUTF16("alpha"), 1);
3371 AddSearchToHistory(default_t_url_, ASCIIToUTF16("avid"), 1);
3373 profile_.BlockUntilHistoryProcessesPendingRequests();
3374 QueryForInputAndWaitForFetcherResponses(
3375 ASCIIToUTF16("a"), false,
3376 "[\"a\",[\"a\", \"alpha\", \"avid\", \"apricot\"],[],[],"
3377 "{\"google:suggestrelevance\":[1450, 1200, 1150, 1100],"
3378 "\"google:verbatimrelevance\":1350}]",
3379 std::string());
3381 AutocompleteMatch verbatim, match_alpha, match_apricot, match_avid;
3382 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
3383 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("alpha"), &match_alpha));
3384 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("apricot"), &match_apricot));
3385 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("avid"), &match_avid));
3387 // Verbatim match duplicates are added such that each one has a higher
3388 // relevance than the previous one.
3389 EXPECT_EQ(2U, verbatim.duplicate_matches.size());
3391 // Other match duplicates are added in descending relevance order.
3392 EXPECT_EQ(1U, match_alpha.duplicate_matches.size());
3393 EXPECT_EQ(1U, match_avid.duplicate_matches.size());
3395 EXPECT_EQ(0U, match_apricot.duplicate_matches.size());
3398 TEST_F(SearchProviderTest, SuggestQueryUsesToken) {
3399 TemplateURLService* turl_model =
3400 TemplateURLServiceFactory::GetForProfile(&profile_);
3402 TemplateURLData data;
3403 data.short_name = ASCIIToUTF16("default");
3404 data.SetKeyword(data.short_name);
3405 data.SetURL("http://example/{searchTerms}{google:sessionToken}");
3406 data.suggestions_url =
3407 "http://suggest/?q={searchTerms}&{google:sessionToken}";
3408 default_t_url_ = new TemplateURL(data);
3409 turl_model->Add(default_t_url_);
3410 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
3412 base::string16 term = term1_.substr(0, term1_.length() - 1);
3413 QueryForInput(term, false, false);
3415 // Make sure the default provider's suggest service was queried.
3416 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3417 SearchProvider::kDefaultProviderURLFetcherID);
3418 ASSERT_TRUE(fetcher);
3420 // And the URL matches what we expected.
3421 TemplateURLRef::SearchTermsArgs search_terms_args(term);
3422 search_terms_args.session_token = provider_->current_token_;
3423 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
3424 search_terms_args, turl_model->search_terms_data()));
3425 EXPECT_EQ(fetcher->GetOriginalURL().spec(), expected_url.spec());
3427 // Complete running the fetcher to clean up.
3428 fetcher->set_response_code(200);
3429 fetcher->delegate()->OnURLFetchComplete(fetcher);
3430 RunTillProviderDone();
3433 TEST_F(SearchProviderTest, SessionToken) {
3434 // Subsequent calls always get the same token.
3435 std::string token = provider_->GetSessionToken();
3436 std::string token2 = provider_->GetSessionToken();
3437 EXPECT_EQ(token, token2);
3438 EXPECT_FALSE(token.empty());
3440 // Calls do not regenerate a token.
3441 provider_->current_token_ = "PRE-EXISTING TOKEN";
3442 token = provider_->GetSessionToken();
3443 EXPECT_EQ(token, "PRE-EXISTING TOKEN");
3445 // ... unless the token has expired.
3446 provider_->current_token_.clear();
3447 const base::TimeDelta kSmallDelta = base::TimeDelta::FromMilliseconds(1);
3448 provider_->token_expiration_time_ = base::TimeTicks::Now() - kSmallDelta;
3449 token = provider_->GetSessionToken();
3450 EXPECT_FALSE(token.empty());
3451 EXPECT_EQ(token, provider_->current_token_);
3453 // The expiration time is always updated.
3454 provider_->GetSessionToken();
3455 base::TimeTicks expiration_time_1 = provider_->token_expiration_time_;
3456 base::PlatformThread::Sleep(kSmallDelta);
3457 provider_->GetSessionToken();
3458 base::TimeTicks expiration_time_2 = provider_->token_expiration_time_;
3459 EXPECT_GT(expiration_time_2, expiration_time_1);
3460 EXPECT_GE(expiration_time_2, expiration_time_1 + kSmallDelta);
3463 TEST_F(SearchProviderTest, AnswersCache) {
3464 AutocompleteResult result;
3465 ACMatches matches;
3466 AutocompleteMatch match1;
3467 match1.answer_contents = base::ASCIIToUTF16("m1");
3468 match1.answer_type = base::ASCIIToUTF16("2334");
3469 match1.fill_into_edit = base::ASCIIToUTF16("weather los angeles");
3471 AutocompleteMatch non_answer_match1;
3472 non_answer_match1.fill_into_edit = base::ASCIIToUTF16("weather laguna beach");
3474 // Test that an answer in the first slot populates the cache.
3475 matches.push_back(match1);
3476 matches.push_back(non_answer_match1);
3477 result.AppendMatches(matches);
3478 provider_->RegisterDisplayedAnswers(result);
3479 ASSERT_FALSE(provider_->answers_cache_.empty());
3481 // Without scored results, no answers will be retrieved.
3482 AnswersQueryData answer = provider_->FindAnswersPrefetchData();
3483 EXPECT_TRUE(answer.full_query_text.empty());
3484 EXPECT_TRUE(answer.query_type.empty());
3486 // Inject a scored result, which will trigger answer retrieval.
3487 base::string16 query = base::ASCIIToUTF16("weather los angeles");
3488 SearchSuggestionParser::SuggestResult suggest_result(
3489 query, AutocompleteMatchType::SEARCH_HISTORY, query, base::string16(),
3490 base::string16(), base::string16(), base::string16(), nullptr,
3491 std::string(), std::string(), false, 1200, false, false, query);
3492 QueryForInput(ASCIIToUTF16("weather l"), false, false);
3493 provider_->transformed_default_history_results_.push_back(suggest_result);
3494 answer = provider_->FindAnswersPrefetchData();
3495 EXPECT_EQ(base::ASCIIToUTF16("weather los angeles"), answer.full_query_text);
3496 EXPECT_EQ(base::ASCIIToUTF16("2334"), answer.query_type);
3499 TEST_F(SearchProviderTest, RemoveExtraAnswers) {
3500 SuggestionAnswer answer1;
3501 answer1.set_type(42);
3502 SuggestionAnswer answer2;
3503 answer2.set_type(1983);
3504 SuggestionAnswer answer3;
3505 answer3.set_type(423);
3507 ACMatches matches;
3508 AutocompleteMatch match1, match2, match3, match4, match5;
3509 match1.answer = SuggestionAnswer::copy(&answer1);
3510 match1.answer_contents = base::ASCIIToUTF16("the answer");
3511 match1.answer_type = base::ASCIIToUTF16("42");
3512 match3.answer = SuggestionAnswer::copy(&answer2);
3513 match3.answer_contents = base::ASCIIToUTF16("not to play");
3514 match3.answer_type = base::ASCIIToUTF16("1983");
3515 match5.answer = SuggestionAnswer::copy(&answer3);
3516 match5.answer_contents = base::ASCIIToUTF16("a man");
3517 match5.answer_type = base::ASCIIToUTF16("423");
3519 matches.push_back(match1);
3520 matches.push_back(match2);
3521 matches.push_back(match3);
3522 matches.push_back(match4);
3523 matches.push_back(match5);
3525 SearchProvider::RemoveExtraAnswers(&matches);
3526 EXPECT_EQ(base::ASCIIToUTF16("the answer"), matches[0].answer_contents);
3527 EXPECT_EQ(base::ASCIIToUTF16("42"), matches[0].answer_type);
3528 EXPECT_TRUE(answer1.Equals(*matches[0].answer));
3529 EXPECT_TRUE(matches[1].answer_contents.empty());
3530 EXPECT_TRUE(matches[1].answer_type.empty());
3531 EXPECT_FALSE(matches[1].answer);
3532 EXPECT_TRUE(matches[2].answer_contents.empty());
3533 EXPECT_TRUE(matches[2].answer_type.empty());
3534 EXPECT_FALSE(matches[2].answer);
3535 EXPECT_TRUE(matches[3].answer_contents.empty());
3536 EXPECT_TRUE(matches[3].answer_type.empty());
3537 EXPECT_FALSE(matches[3].answer);
3538 EXPECT_TRUE(matches[4].answer_contents.empty());
3539 EXPECT_TRUE(matches[4].answer_type.empty());
3540 EXPECT_FALSE(matches[4].answer);
3543 TEST_F(SearchProviderTest, DoesNotProvideOnFocus) {
3544 AutocompleteInput input(base::ASCIIToUTF16("f"), base::string16::npos,
3545 std::string(), GURL(),
3546 metrics::OmniboxEventProto::INVALID_SPEC, false,
3547 true, true, true,
3548 ChromeAutocompleteSchemeClassifier(&profile_));
3549 provider_->Start(input, false, true);
3550 EXPECT_TRUE(provider_->matches().empty());