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 "chrome/browser/autocomplete/search_provider.h"
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/autocomplete_input.h"
22 #include "chrome/browser/autocomplete/autocomplete_match.h"
23 #include "chrome/browser/autocomplete/autocomplete_provider.h"
24 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h"
25 #include "chrome/browser/autocomplete/history_url_provider.h"
26 #include "chrome/browser/history/history_service.h"
27 #include "chrome/browser/history/history_service_factory.h"
28 #include "chrome/browser/omnibox/omnibox_field_trial.h"
29 #include "chrome/browser/search_engines/search_engine_type.h"
30 #include "chrome/browser/search_engines/template_url.h"
31 #include "chrome/browser/search_engines/template_url_service.h"
32 #include "chrome/browser/search_engines/template_url_service_factory.h"
33 #include "chrome/browser/signin/signin_manager.h"
34 #include "chrome/browser/signin/signin_manager_factory.h"
35 #include "chrome/browser/sync/profile_sync_service.h"
36 #include "chrome/browser/sync/profile_sync_service_factory.h"
37 #include "chrome/common/chrome_switches.h"
38 #include "chrome/common/metrics/variations/variations_util.h"
39 #include "chrome/common/pref_names.h"
40 #include "chrome/test/base/testing_browser_process.h"
41 #include "chrome/test/base/testing_profile.h"
42 #include "components/variations/entropy_provider.h"
43 #include "content/public/test/test_browser_thread_bundle.h"
44 #include "net/url_request/test_url_fetcher_factory.h"
45 #include "net/url_request/url_request_status.h"
46 #include "testing/gtest/include/gtest/gtest.h"
48 using base::ASCIIToUTF16;
52 // Returns the first match in |matches| with |allowed_to_be_default_match|
54 ACMatches::const_iterator FindDefaultMatch(const ACMatches& matches) {
55 ACMatches::const_iterator it = matches.begin();
56 while ((it != matches.end()) && !it->allowed_to_be_default_match)
61 class SuggestionDeletionHandler;
62 class SearchProviderForTest : public SearchProvider {
64 SearchProviderForTest(
65 AutocompleteProviderListener* listener,
67 bool is_success() { return is_success_; };
70 virtual ~SearchProviderForTest();
73 virtual void RecordDeletionResult(bool success) OVERRIDE;
75 DISALLOW_COPY_AND_ASSIGN(SearchProviderForTest);
78 SearchProviderForTest::SearchProviderForTest(
79 AutocompleteProviderListener* listener,
81 : SearchProvider(listener, profile), is_success_(false) {
84 SearchProviderForTest::~SearchProviderForTest() {
87 void SearchProviderForTest::RecordDeletionResult(bool success) {
88 is_success_ = success;
93 // SearchProviderTest ---------------------------------------------------------
95 // The following environment is configured for these tests:
96 // . The TemplateURL default_t_url_ is set as the default provider.
97 // . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This
98 // TemplateURL has a valid suggest and search URL.
99 // . The URL created by using the search term term1_ with default_t_url_ is
101 // . The URL created by using the search term keyword_term_ with keyword_t_url_
102 // is added to history.
103 // . test_factory_ is set as the URLFetcherFactory.
104 class SearchProviderTest : public testing::Test,
105 public AutocompleteProviderListener {
108 ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES),
109 allowed_to_be_default_match(false) {
111 ResultInfo(GURL gurl,
112 AutocompleteMatch::Type result_type,
113 bool allowed_to_be_default_match,
114 base::string16 fill_into_edit)
116 result_type(result_type),
117 allowed_to_be_default_match(allowed_to_be_default_match),
118 fill_into_edit(fill_into_edit) {
122 const AutocompleteMatch::Type result_type;
123 const bool allowed_to_be_default_match;
124 const base::string16 fill_into_edit;
128 const base::string16 input;
129 const size_t num_results;
130 const ResultInfo output[3];
134 : default_t_url_(NULL),
135 term1_(ASCIIToUTF16("term1")),
136 keyword_t_url_(NULL),
137 keyword_term_(ASCIIToUTF16("keyword")),
139 ResetFieldTrialList();
142 // See description above class for what this registers.
143 virtual void SetUp() OVERRIDE;
144 virtual void TearDown() OVERRIDE;
146 void RunTest(TestData* cases, int num_cases, bool prefer_keyword);
149 // Needed for AutocompleteFieldTrial::ActivateStaticTrials();
150 scoped_ptr<base::FieldTrialList> field_trial_list_;
152 // Default value used for testing.
153 static const std::string kNotApplicable;
155 // Adds a search for |term|, using the engine |t_url| to the history, and
156 // returns the URL for that search.
157 GURL AddSearchToHistory(TemplateURL* t_url, base::string16 term, int visit_count);
159 // Looks for a match in |provider_| with |contents| equal to |contents|.
160 // Sets |match| to it if found. Returns whether |match| was set.
161 bool FindMatchWithContents(const base::string16& contents,
162 AutocompleteMatch* match);
164 // Looks for a match in |provider_| with destination |url|. Sets |match| to
165 // it if found. Returns whether |match| was set.
166 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match);
168 // AutocompleteProviderListener:
169 // If we're waiting for the provider to finish, this exits the message loop.
170 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE;
172 // Runs a nested message loop until provider_ is done. The message loop is
173 // exited by way of OnProviderUpdate.
174 void RunTillProviderDone();
176 // Invokes Start on provider_, then runs all pending tasks.
177 void QueryForInput(const base::string16& text,
178 bool prevent_inline_autocomplete,
179 bool prefer_keyword);
181 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is
182 // non-NULL, sets it to the "what you typed" entry for |text|.
183 void QueryForInputAndSetWYTMatch(const base::string16& text,
184 AutocompleteMatch* wyt_match);
186 // Notifies the URLFetcher for the suggest query corresponding to the default
187 // search provider that it's done.
188 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE.
189 void FinishDefaultSuggestQuery();
191 // Runs SearchProvider on |input|, for which the suggest server replies
192 // with |json|, and expects that the resulting matches' contents equals
193 // that in |matches|. An empty entry in |matches| means no match should
194 // be returned in that position. Reports any errors with a message that
195 // includes |error_description|.
196 void ForcedQueryTestHelper(const std::string& input,
197 const std::string& json,
198 const std::string matches[3],
199 const std::string& error_description);
201 void ResetFieldTrialList();
203 void ClearAllResults();
205 // See description above class for details of these fields.
206 TemplateURL* default_t_url_;
207 const base::string16 term1_;
209 TemplateURL* keyword_t_url_;
210 const base::string16 keyword_term_;
213 content::TestBrowserThreadBundle thread_bundle_;
215 // URLFetcherFactory implementation registered.
216 net::TestURLFetcherFactory test_factory_;
219 TestingProfile profile_;
222 scoped_refptr<SearchProviderForTest> provider_;
224 // If non-NULL, OnProviderUpdate quits the current |run_loop_|.
225 base::RunLoop* run_loop_;
227 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
231 const std::string SearchProviderTest::kNotApplicable = "Not Applicable";
233 void SearchProviderTest::SetUp() {
234 // Make sure that fetchers are automatically ungregistered upon destruction.
235 test_factory_.set_remove_fetcher_on_delete(true);
237 // We need both the history service and template url model loaded.
238 ASSERT_TRUE(profile_.CreateHistoryService(true, false));
239 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
240 &profile_, &TemplateURLServiceFactory::BuildInstanceFor);
242 TemplateURLService* turl_model =
243 TemplateURLServiceFactory::GetForProfile(&profile_);
247 // Reset the default TemplateURL.
248 TemplateURLData data;
249 data.short_name = ASCIIToUTF16("t");
250 data.SetURL("http://defaultturl/{searchTerms}");
251 data.suggestions_url = "http://defaultturl2/{searchTerms}";
252 data.instant_url = "http://does/not/exist?strk=1";
253 data.search_terms_replacement_key = "strk";
254 default_t_url_ = new TemplateURL(&profile_, data);
255 turl_model->Add(default_t_url_);
256 turl_model->SetDefaultSearchProvider(default_t_url_);
257 TemplateURLID default_provider_id = default_t_url_->id();
258 ASSERT_NE(0, default_provider_id);
260 // Add url1, with search term term1_.
261 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1);
263 // Create another TemplateURL.
264 data.short_name = ASCIIToUTF16("k");
265 data.SetKeyword(ASCIIToUTF16("k"));
266 data.SetURL("http://keyword/{searchTerms}");
267 data.suggestions_url = "http://suggest_keyword/{searchTerms}";
268 keyword_t_url_ = new TemplateURL(&profile_, data);
269 turl_model->Add(keyword_t_url_);
270 ASSERT_NE(0, keyword_t_url_->id());
272 // Add a page and search term for keyword_t_url_.
273 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1);
275 // Keywords are updated by the InMemoryHistoryBackend only after the message
276 // has been processed on the history thread. Block until history processes all
277 // requests to ensure the InMemoryDatabase is the state we expect it.
278 profile_.BlockUntilHistoryProcessesPendingRequests();
280 provider_ = new SearchProviderForTest(this, &profile_);
281 provider_->kMinimumTimeBetweenSuggestQueriesMs = 0;
284 void SearchProviderTest::TearDown() {
285 base::RunLoop().RunUntilIdle();
287 // Shutdown the provider before the profile.
291 void SearchProviderTest::RunTest(TestData* cases,
293 bool prefer_keyword) {
295 for (int i = 0; i < num_cases; ++i) {
296 AutocompleteInput input(cases[i].input, base::string16::npos,
297 base::string16(), GURL(),
298 AutocompleteInput::INVALID_SPEC, false,
299 prefer_keyword, true,
300 AutocompleteInput::ALL_MATCHES);
301 provider_->Start(input, false);
302 matches = provider_->matches();
303 base::string16 diagnostic_details =
304 ASCIIToUTF16("Input was: ") +
306 ASCIIToUTF16("; prefer_keyword was: ") +
307 (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false"));
308 EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details;
309 if (matches.size() == cases[i].num_results) {
310 for (size_t j = 0; j < cases[i].num_results; ++j) {
311 EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) <<
313 EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) <<
315 EXPECT_EQ(cases[i].output[j].fill_into_edit,
316 matches[j].fill_into_edit) << diagnostic_details;
317 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
318 matches[j].allowed_to_be_default_match) << diagnostic_details;
324 void SearchProviderTest::OnProviderUpdate(bool updated_matches) {
325 if (run_loop_ && provider_->done()) {
331 void SearchProviderTest::RunTillProviderDone() {
332 if (provider_->done())
335 base::RunLoop run_loop;
336 run_loop_ = &run_loop;
340 void SearchProviderTest::QueryForInput(const base::string16& text,
341 bool prevent_inline_autocomplete,
342 bool prefer_keyword) {
344 AutocompleteInput input(text, base::string16::npos, base::string16(), GURL(),
345 AutocompleteInput::INVALID_SPEC,
346 prevent_inline_autocomplete, prefer_keyword, true,
347 AutocompleteInput::ALL_MATCHES);
348 provider_->Start(input, false);
350 // RunUntilIdle so that the task scheduled by SearchProvider to create the
352 base::RunLoop().RunUntilIdle();
355 void SearchProviderTest::QueryForInputAndSetWYTMatch(
356 const base::string16& text,
357 AutocompleteMatch* wyt_match) {
358 QueryForInput(text, false, false);
359 profile_.BlockUntilHistoryProcessesPendingRequests();
360 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
363 ASSERT_GE(provider_->matches().size(), 1u);
364 EXPECT_TRUE(FindMatchWithDestination(GURL(
365 default_t_url_->url_ref().ReplaceSearchTerms(
366 TemplateURLRef::SearchTermsArgs(text))),
370 GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url,
373 HistoryService* history =
374 HistoryServiceFactory::GetForProfile(&profile_,
375 Profile::EXPLICIT_ACCESS);
376 GURL search(t_url->url_ref().ReplaceSearchTerms(
377 TemplateURLRef::SearchTermsArgs(term)));
378 static base::Time last_added_time;
379 last_added_time = std::max(base::Time::Now(),
380 last_added_time + base::TimeDelta::FromMicroseconds(1));
381 history->AddPageWithDetails(search, base::string16(), visit_count, visit_count,
382 last_added_time, false, history::SOURCE_BROWSED);
383 history->SetKeywordSearchTermsForURL(search, t_url->id(), term);
387 bool SearchProviderTest::FindMatchWithContents(const base::string16& contents,
388 AutocompleteMatch* match) {
389 for (ACMatches::const_iterator i = provider_->matches().begin();
390 i != provider_->matches().end(); ++i) {
391 if (i->contents == contents) {
399 bool SearchProviderTest::FindMatchWithDestination(const GURL& url,
400 AutocompleteMatch* match) {
401 for (ACMatches::const_iterator i = provider_->matches().begin();
402 i != provider_->matches().end(); ++i) {
403 if (i->destination_url == url) {
411 void SearchProviderTest::FinishDefaultSuggestQuery() {
412 net::TestURLFetcher* default_fetcher =
413 test_factory_.GetFetcherByID(
414 SearchProvider::kDefaultProviderURLFetcherID);
415 ASSERT_TRUE(default_fetcher);
417 // Tell the SearchProvider the default suggest query is done.
418 default_fetcher->set_response_code(200);
419 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
422 void SearchProviderTest::ForcedQueryTestHelper(
423 const std::string& input,
424 const std::string& json,
425 const std::string expected_matches[3],
426 const std::string& error_description) {
427 QueryForInput(ASCIIToUTF16(input), false, false);
428 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
429 SearchProvider::kDefaultProviderURLFetcherID);
430 ASSERT_TRUE(fetcher);
431 fetcher->set_response_code(200);
432 fetcher->SetResponseString(json);
433 fetcher->delegate()->OnURLFetchComplete(fetcher);
434 RunTillProviderDone();
436 const ACMatches& matches = provider_->matches();
437 ASSERT_LE(matches.size(), 3u);
439 // Ensure that the returned matches equal the expectations.
440 for (; i < matches.size(); ++i) {
441 EXPECT_EQ(ASCIIToUTF16(expected_matches[i]), matches[i].contents) <<
444 // Ensure that no expected matches are missing.
445 for (; i < 3u; ++i) {
446 EXPECT_EQ(std::string(), expected_matches[i]) <<
447 "Case #" << i << ": " << error_description;
451 void SearchProviderTest::ResetFieldTrialList() {
452 // Destroy the existing FieldTrialList before creating a new one to avoid
454 field_trial_list_.reset();
455 field_trial_list_.reset(new base::FieldTrialList(
456 new metrics::SHA1EntropyProvider("foo")));
457 chrome_variations::testing::ClearAllVariationParams();
458 base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
459 "AutocompleteDynamicTrial_0", "DefaultGroup");
463 void SearchProviderTest::ClearAllResults() {
464 provider_->ClearAllResults();
467 // Actual Tests ---------------------------------------------------------------
469 // Make sure we query history for the default provider and a URLFetcher is
470 // created for the default provider suggest results.
471 TEST_F(SearchProviderTest, QueryDefaultProvider) {
472 base::string16 term = term1_.substr(0, term1_.length() - 1);
473 QueryForInput(term, false, false);
475 // Make sure the default providers suggest service was queried.
476 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
477 SearchProvider::kDefaultProviderURLFetcherID);
478 ASSERT_TRUE(fetcher);
480 // And the URL matches what we expected.
481 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
482 TemplateURLRef::SearchTermsArgs(term)));
483 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url);
485 // Tell the SearchProvider the suggest query is done.
486 fetcher->set_response_code(200);
487 fetcher->delegate()->OnURLFetchComplete(fetcher);
490 // Run till the history results complete.
491 RunTillProviderDone();
493 // The SearchProvider is done. Make sure it has a result for the history
495 AutocompleteMatch term1_match;
496 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match));
497 // Term1 should not have a description, it's set later.
498 EXPECT_TRUE(term1_match.description.empty());
500 AutocompleteMatch wyt_match;
501 EXPECT_TRUE(FindMatchWithDestination(
502 GURL(default_t_url_->url_ref().ReplaceSearchTerms(
503 TemplateURLRef::SearchTermsArgs(term))), &wyt_match));
504 EXPECT_TRUE(wyt_match.description.empty());
506 // The match for term1 should be more relevant than the what you typed match.
507 EXPECT_GT(term1_match.relevance, wyt_match.relevance);
508 // This longer match should be inlineable.
509 EXPECT_TRUE(term1_match.allowed_to_be_default_match);
510 // The what you typed match should be too, of course.
511 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
514 TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
515 base::string16 term = term1_.substr(0, term1_.length() - 1);
516 QueryForInput(term, true, false);
518 ASSERT_FALSE(provider_->matches().empty());
519 ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
520 provider_->matches()[0].type);
521 EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match);
524 // Issues a query that matches the registered keyword and makes sure history
525 // is queried as well as URLFetchers getting created.
526 TEST_F(SearchProviderTest, QueryKeywordProvider) {
527 base::string16 term = keyword_term_.substr(0, keyword_term_.length() - 1);
528 QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term,
532 // Make sure the default providers suggest service was queried.
533 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID(
534 SearchProvider::kDefaultProviderURLFetcherID);
535 ASSERT_TRUE(default_fetcher);
537 // Tell the SearchProvider the default suggest query is done.
538 default_fetcher->set_response_code(200);
539 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
540 default_fetcher = NULL;
542 // Make sure the keyword providers suggest service was queried.
543 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID(
544 SearchProvider::kKeywordProviderURLFetcherID);
545 ASSERT_TRUE(keyword_fetcher);
547 // And the URL matches what we expected.
548 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms(
549 TemplateURLRef::SearchTermsArgs(term)));
550 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url);
552 // Tell the SearchProvider the keyword suggest query is done.
553 keyword_fetcher->set_response_code(200);
554 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
555 keyword_fetcher = NULL;
557 // Run till the history results complete.
558 RunTillProviderDone();
560 // The SearchProvider is done. Make sure it has a result for the history
562 AutocompleteMatch match;
563 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match));
565 // The match should have an associated keyword.
566 EXPECT_FALSE(match.keyword.empty());
568 // The fill into edit should contain the keyword.
569 EXPECT_EQ(keyword_t_url_->keyword() + base::char16(' ') + keyword_term_,
570 match.fill_into_edit);
573 TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) {
574 // None of the following input strings should be sent to the suggest server,
575 // because they may contain private data.
576 const char* inputs[] = {
578 "http://username:password",
579 "https://username:password",
580 "username:password@hostname",
581 "http://username:password@hostname/",
584 "unknownscheme:anything",
585 "http://hostname/?query=q",
586 "http://hostname/path#ref",
587 "http://hostname/path #ref",
588 "https://hostname/path",
591 for (size_t i = 0; i < arraysize(inputs); ++i) {
592 QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
593 // Make sure the default provider's suggest service was not queried.
594 ASSERT_TRUE(test_factory_.GetFetcherByID(
595 SearchProvider::kDefaultProviderURLFetcherID) == NULL);
596 // Run till the history results complete.
597 RunTillProviderDone();
601 TEST_F(SearchProviderTest, SendNonPrivateDataToSuggest) {
602 // All of the following input strings should be sent to the suggest server,
603 // because they should not get caught by the private data checks.
604 const char* inputs[] = {
608 "http://hostname/path",
609 "http://hostname #ref",
610 "www.hostname.com #ref",
613 "foo https://hostname/path"
616 profile_.BlockUntilHistoryProcessesPendingRequests();
617 for (size_t i = 0; i < arraysize(inputs); ++i) {
618 QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
619 // Make sure the default provider's suggest service was queried.
620 ASSERT_TRUE(test_factory_.GetFetcherByID(
621 SearchProvider::kDefaultProviderURLFetcherID) != NULL);
625 TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
626 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
627 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor);
628 GURL url = AddSearchToHistory(default_t_url_,
629 ASCIIToUTF16("docs.google.com"), 1);
631 // Add the term as a url.
632 HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)->
633 AddPageWithDetails(GURL("http://docs.google.com"), base::string16(), 1, 1,
634 base::Time::Now(), false, history::SOURCE_BROWSED);
635 profile_.BlockUntilHistoryProcessesPendingRequests();
637 AutocompleteMatch wyt_match;
638 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"),
641 // There should be two matches, one for what you typed, the other for
642 // 'docs.google.com'. The search term should have a lower priority than the
643 // what you typed match.
644 ASSERT_EQ(2u, provider_->matches().size());
645 AutocompleteMatch term_match;
646 EXPECT_TRUE(FindMatchWithDestination(url, &term_match));
647 EXPECT_GT(wyt_match.relevance, term_match.relevance);
648 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
649 EXPECT_TRUE(term_match.allowed_to_be_default_match);
652 TEST_F(SearchProviderTest, DontGiveNavsuggestionsInForcedQueryMode) {
653 const std::string kEmptyMatch;
655 const std::string json;
656 const std::string matches_in_default_mode[3];
657 const std::string matches_in_forced_query_mode[3];
659 // Without suggested relevance scores.
660 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
661 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]}]",
662 { "a", "a1.com", "a2" },
663 { "a", "a2", kEmptyMatch } },
665 // With suggested relevance scores in a situation where navsuggest would
667 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
668 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
669 "\"google:suggestrelevance\":[1250, 1200]}]",
670 { "a", "a1.com", "a2" },
671 { "a", "a2", kEmptyMatch } },
673 // With suggested relevance scores in a situation where navsuggest
675 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
676 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
677 "\"google:suggestrelevance\":[1350, 1250]}]",
678 { "a1.com", "a", "a2" },
679 { "a", "a2", kEmptyMatch } },
681 // With suggested relevance scores in a situation where navsuggest
682 // would go first only because verbatim has been demoted.
683 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
684 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
685 "\"google:suggestrelevance\":[1450, 1400],"
686 "\"google:verbatimrelevance\":1350}]",
687 { "a1.com", "a2", "a" },
688 { "a2", "a", kEmptyMatch } },
691 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
692 ForcedQueryTestHelper("a", cases[i].json, cases[i].matches_in_default_mode,
693 "regular input with json=" + cases[i].json);
694 ForcedQueryTestHelper("?a", cases[i].json,
695 cases[i].matches_in_forced_query_mode,
696 "forced query input with json=" + cases[i].json);
700 // A multiword search with one visit should not autocomplete until multiple
702 TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) {
703 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"),
705 profile_.BlockUntilHistoryProcessesPendingRequests();
707 AutocompleteMatch wyt_match;
708 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"),
710 ASSERT_EQ(2u, provider_->matches().size());
711 AutocompleteMatch term_match;
712 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
713 EXPECT_GT(wyt_match.relevance, term_match.relevance);
714 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
715 EXPECT_TRUE(term_match.allowed_to_be_default_match);
717 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"),
719 ASSERT_EQ(2u, provider_->matches().size());
720 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
721 EXPECT_GT(term_match.relevance, wyt_match.relevance);
722 EXPECT_TRUE(term_match.allowed_to_be_default_match);
723 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
726 // A multiword search with more than one visit should autocomplete immediately.
727 TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) {
728 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
730 profile_.BlockUntilHistoryProcessesPendingRequests();
732 AutocompleteMatch wyt_match;
733 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"),
735 ASSERT_EQ(2u, provider_->matches().size());
736 AutocompleteMatch term_match;
737 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
738 EXPECT_GT(term_match.relevance, wyt_match.relevance);
739 EXPECT_TRUE(term_match.allowed_to_be_default_match);
740 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
743 // Autocompletion should work at a word boundary after a space.
744 TEST_F(SearchProviderTest, AutocompleteAfterSpace) {
745 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
747 profile_.BlockUntilHistoryProcessesPendingRequests();
749 AutocompleteMatch wyt_match;
750 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "),
752 ASSERT_EQ(2u, provider_->matches().size());
753 AutocompleteMatch term_match;
754 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
755 EXPECT_GT(term_match.relevance, wyt_match.relevance);
756 EXPECT_TRUE(term_match.allowed_to_be_default_match);
757 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
760 // Newer multiword searches should score more highly than older ones.
761 TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) {
762 GURL term_url_a(AddSearchToHistory(default_t_url_,
763 ASCIIToUTF16("three searches aaa"), 1));
764 GURL term_url_b(AddSearchToHistory(default_t_url_,
765 ASCIIToUTF16("three searches bbb"), 1));
766 profile_.BlockUntilHistoryProcessesPendingRequests();
768 AutocompleteMatch wyt_match;
769 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"),
771 ASSERT_EQ(3u, provider_->matches().size());
772 AutocompleteMatch term_match_a;
773 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
774 AutocompleteMatch term_match_b;
775 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
776 EXPECT_GT(term_match_b.relevance, term_match_a.relevance);
777 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
778 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
779 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
780 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
783 // An autocompleted multiword search should not be replaced by a different
784 // autocompletion while the user is still typing a valid prefix.
785 TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) {
786 GURL term_url_a(AddSearchToHistory(default_t_url_,
787 ASCIIToUTF16("four searches aaa"), 2));
788 GURL term_url_b(AddSearchToHistory(default_t_url_,
789 ASCIIToUTF16("four searches bbb"), 1));
790 profile_.BlockUntilHistoryProcessesPendingRequests();
792 AutocompleteMatch wyt_match;
793 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"),
795 ASSERT_EQ(3u, provider_->matches().size());
796 AutocompleteMatch term_match_a;
797 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
798 AutocompleteMatch term_match_b;
799 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
800 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
801 EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
802 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
803 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
804 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
806 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"),
808 ASSERT_EQ(3u, provider_->matches().size());
809 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
810 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
811 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
812 EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
813 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
814 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
815 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
818 // Non-completable multiword searches should not crowd out single-word searches.
819 TEST_F(SearchProviderTest, DontCrowdOutSingleWords) {
820 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1));
821 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1);
822 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1);
823 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1);
824 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1);
825 profile_.BlockUntilHistoryProcessesPendingRequests();
827 AutocompleteMatch wyt_match;
828 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"),
830 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size());
831 AutocompleteMatch term_match;
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 // Inline autocomplete matches regardless of case differences from the input.
839 TEST_F(SearchProviderTest, InlineMixedCaseMatches) {
840 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1));
841 profile_.BlockUntilHistoryProcessesPendingRequests();
843 AutocompleteMatch wyt_match;
844 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"),
846 ASSERT_EQ(2u, provider_->matches().size());
847 AutocompleteMatch term_match;
848 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
849 EXPECT_GT(term_match.relevance, wyt_match.relevance);
850 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit);
851 EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion);
852 EXPECT_TRUE(term_match.allowed_to_be_default_match);
855 // Verifies AutocompleteControllers return results (including keyword
856 // results) in the right order and set descriptions for them correctly.
857 TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) {
858 // Add an entry that corresponds to a keyword search with 'term2'.
859 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1);
860 profile_.BlockUntilHistoryProcessesPendingRequests();
862 AutocompleteController controller(&profile_, NULL,
863 AutocompleteProvider::TYPE_SEARCH);
864 controller.Start(AutocompleteInput(
865 ASCIIToUTF16("k t"), base::string16::npos, base::string16(), GURL(),
866 AutocompleteInput::INVALID_SPEC, false, false, true,
867 AutocompleteInput::ALL_MATCHES));
868 const AutocompleteResult& result = controller.result();
870 // There should be three matches, one for the keyword history, one for
871 // keyword provider's what-you-typed, and one for the default provider's
872 // what you typed, in that order.
873 ASSERT_EQ(3u, result.size());
874 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type);
875 EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE,
876 result.match_at(1).type);
877 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
878 result.match_at(2).type);
879 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance);
880 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance);
881 EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match);
882 EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match);
883 EXPECT_FALSE(result.match_at(2).allowed_to_be_default_match);
885 // The two keyword results should come with the keyword we expect.
886 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword);
887 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword);
888 // The default provider has a different keyword. (We don't explicitly
889 // set it during this test, so all we do is assert that it's different.)
890 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword);
892 // The top result will always have a description. The third result,
893 // coming from a different provider than the first two, should also.
894 // Whether the second result has one doesn't matter much. (If it was
895 // missing, people would infer that it's the same search provider as
896 // the one above it.)
897 EXPECT_FALSE(result.match_at(0).description.empty());
898 EXPECT_FALSE(result.match_at(2).description.empty());
899 EXPECT_NE(result.match_at(0).description, result.match_at(2).description);
902 TEST_F(SearchProviderTest, KeywordVerbatim) {
904 // Test a simple keyword input.
905 { ASCIIToUTF16("k foo"), 2,
906 { ResultInfo(GURL("http://keyword/foo"),
907 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
909 ASCIIToUTF16("k foo")),
910 ResultInfo(GURL("http://defaultturl/k%20foo"),
911 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
913 ASCIIToUTF16("k foo") ) } },
915 // Make sure extra whitespace after the keyword doesn't change the
916 // keyword verbatim query.
917 { ASCIIToUTF16("k foo"), 2,
918 { ResultInfo(GURL("http://keyword/foo"),
919 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
921 ASCIIToUTF16("k foo")),
922 ResultInfo(GURL("http://defaultturl/k%20%20%20foo"),
923 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
925 ASCIIToUTF16("k foo")) } },
926 // Leading whitespace should be stripped before SearchProvider gets the
927 // input; hence there are no tests here about how it handles those inputs.
929 // But whitespace elsewhere in the query string should matter to both
931 { ASCIIToUTF16("k foo bar"), 2,
932 { ResultInfo(GURL("http://keyword/foo%20%20bar"),
933 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
935 ASCIIToUTF16("k foo bar")),
936 ResultInfo(GURL("http://defaultturl/k%20%20foo%20%20bar"),
937 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
939 ASCIIToUTF16("k foo bar")) } },
940 // Note in the above test case we don't test trailing whitespace because
941 // SearchProvider still doesn't handle this well. See related bugs:
942 // 102690, 99239, 164635.
944 // Keywords can be prefixed by certain things that should get ignored
945 // when constructing the keyword match.
946 { ASCIIToUTF16("www.k foo"), 2,
947 { ResultInfo(GURL("http://keyword/foo"),
948 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
950 ASCIIToUTF16("k foo")),
951 ResultInfo(GURL("http://defaultturl/www.k%20foo"),
952 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
954 ASCIIToUTF16("www.k foo")) } },
955 { ASCIIToUTF16("http://k foo"), 2,
956 { ResultInfo(GURL("http://keyword/foo"),
957 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
959 ASCIIToUTF16("k foo")),
960 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"),
961 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
963 ASCIIToUTF16("http://k foo")) } },
964 { ASCIIToUTF16("http://www.k foo"), 2,
965 { ResultInfo(GURL("http://keyword/foo"),
966 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
968 ASCIIToUTF16("k foo")),
969 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"),
970 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
972 ASCIIToUTF16("http://www.k foo")) } },
974 // A keyword with no remaining input shouldn't get a keyword
976 { ASCIIToUTF16("k"), 1,
977 { ResultInfo(GURL("http://defaultturl/k"),
978 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
980 ASCIIToUTF16("k")) } },
981 { ASCIIToUTF16("k "), 1,
982 { ResultInfo(GURL("http://defaultturl/k%20"),
983 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
985 ASCIIToUTF16("k ")) } }
987 // The fact that verbatim queries to keyword are handled by KeywordProvider
988 // not SearchProvider is tested in
989 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc.
992 // Test not in keyword mode.
993 RunTest(cases, arraysize(cases), false);
995 // Test in keyword mode. (Both modes should give the same result.)
996 RunTest(cases, arraysize(cases), true);
999 // Ensures command-line flags are reflected in the URLs the search provider
1001 TEST_F(SearchProviderTest, CommandLineOverrides) {
1002 TemplateURLService* turl_model =
1003 TemplateURLServiceFactory::GetForProfile(&profile_);
1005 TemplateURLData data;
1006 data.short_name = ASCIIToUTF16("default");
1007 data.SetKeyword(data.short_name);
1008 data.SetURL("{google:baseURL}{searchTerms}");
1009 default_t_url_ = new TemplateURL(&profile_, data);
1010 turl_model->Add(default_t_url_);
1011 turl_model->SetDefaultSearchProvider(default_t_url_);
1013 CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL,
1014 "http://www.bar.com/");
1015 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
1016 switches::kExtraSearchQueryParams, "a=b");
1018 TestData cases[] = {
1019 { ASCIIToUTF16("k a"), 2,
1020 { ResultInfo(GURL("http://keyword/a"),
1021 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1023 ASCIIToUTF16("k a")),
1024 ResultInfo(GURL("http://www.bar.com/k%20a?a=b"),
1025 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1027 ASCIIToUTF16("k a")) } },
1030 RunTest(cases, arraysize(cases), false);
1033 // Verifies Navsuggest results don't set a TemplateURL, which Instant relies on.
1034 // Also verifies that just the *first* navigational result is listed as a match
1035 // if suggested relevance scores were not sent.
1036 TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) {
1037 QueryForInput(ASCIIToUTF16("a.c"), false, false);
1039 // Make sure the default providers suggest service was queried.
1040 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
1041 SearchProvider::kDefaultProviderURLFetcherID);
1042 ASSERT_TRUE(fetcher);
1044 // Tell the SearchProvider the suggest query is done.
1045 fetcher->set_response_code(200);
1046 fetcher->SetResponseString(
1047 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[],"
1048 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]");
1049 fetcher->delegate()->OnURLFetchComplete(fetcher);
1052 // Run till the history results complete.
1053 RunTillProviderDone();
1055 // Make sure the only match is 'a.com' and it doesn't have a template_url.
1056 AutocompleteMatch nav_match;
1057 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match));
1058 EXPECT_TRUE(nav_match.keyword.empty());
1059 EXPECT_TRUE(nav_match.allowed_to_be_default_match);
1060 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match));
1063 // Verifies that the most relevant suggest results are added properly.
1064 TEST_F(SearchProviderTest, SuggestRelevance) {
1065 QueryForInput(ASCIIToUTF16("a"), false, false);
1067 // Make sure the default provider's suggest service was queried.
1068 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
1069 SearchProvider::kDefaultProviderURLFetcherID);
1070 ASSERT_TRUE(fetcher);
1072 // Tell the SearchProvider the suggest query is done.
1073 fetcher->set_response_code(200);
1074 fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]");
1075 fetcher->delegate()->OnURLFetchComplete(fetcher);
1078 // Run till the history results complete.
1079 RunTillProviderDone();
1081 // Check the expected verbatim and (first 3) suggestions' relative relevances.
1082 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4;
1083 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
1084 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1));
1085 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2));
1086 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3));
1087 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4));
1088 EXPECT_GT(verbatim.relevance, match_a1.relevance);
1089 EXPECT_GT(match_a1.relevance, match_a2.relevance);
1090 EXPECT_GT(match_a2.relevance, match_a3.relevance);
1091 EXPECT_TRUE(verbatim.allowed_to_be_default_match);
1092 EXPECT_TRUE(match_a1.allowed_to_be_default_match);
1093 EXPECT_TRUE(match_a2.allowed_to_be_default_match);
1094 EXPECT_TRUE(match_a3.allowed_to_be_default_match);
1097 // Verifies that the default provider abandons suggested relevance scores
1098 // when in keyword mode. This should happen regardless of whether the
1099 // keyword provider returns suggested relevance scores.
1100 TEST_F(SearchProviderTest, DefaultProviderNoSuggestRelevanceInKeywordMode) {
1102 const std::string default_provider_json;
1103 const std::string keyword_provider_json;
1104 const std::string matches[5];
1106 // First, try an input where the keyword provider does not deliver
1107 // suggested relevance scores.
1108 { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
1109 "{\"google:verbatimrelevance\":9700,"
1110 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1111 "\"google:suggestrelevance\":[9900, 9800]}]",
1112 "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]}]",
1113 { "a", "akeyword-query", "k a", "adefault.com", "k adefault-query" } },
1115 // Now try with keyword provider suggested relevance scores.
1116 { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
1117 "{\"google:verbatimrelevance\":9700,"
1118 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1119 "\"google:suggestrelevance\":[9900, 9800]}]",
1120 "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"],"
1121 "\"google:verbatimrelevance\":9500,"
1122 "\"google:suggestrelevance\":[9600]}]",
1123 { "akeyword-query", "a", "k a", "adefault.com", "k adefault-query" } }
1126 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1127 QueryForInput(ASCIIToUTF16("k a"), false, true);
1128 net::TestURLFetcher* default_fetcher =
1129 test_factory_.GetFetcherByID(
1130 SearchProvider::kDefaultProviderURLFetcherID);
1131 ASSERT_TRUE(default_fetcher);
1132 default_fetcher->set_response_code(200);
1133 default_fetcher->SetResponseString(cases[i].default_provider_json);
1134 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
1135 net::TestURLFetcher* keyword_fetcher =
1136 test_factory_.GetFetcherByID(
1137 SearchProvider::kKeywordProviderURLFetcherID);
1138 ASSERT_TRUE(keyword_fetcher);
1139 keyword_fetcher->set_response_code(200);
1140 keyword_fetcher->SetResponseString(cases[i].keyword_provider_json);
1141 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
1142 RunTillProviderDone();
1144 const std::string description = "for input with default_provider_json=" +
1145 cases[i].default_provider_json + " and keyword_provider_json=" +
1146 cases[i].keyword_provider_json;
1147 const ACMatches& matches = provider_->matches();
1148 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1150 // Ensure that the returned matches equal the expectations.
1151 for (; j < matches.size(); ++j) {
1152 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), matches[j].contents) <<
1155 // Ensure that no expected matches are missing.
1156 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1157 EXPECT_EQ(std::string(), cases[i].matches[j]) << description;
1161 // Verifies that suggest results with relevance scores are added
1162 // properly when using the default fetcher. When adding a new test
1163 // case to this test, please consider adding it to the tests in
1164 // KeywordFetcherSuggestRelevance below.
1165 TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
1166 struct DefaultFetcherMatch {
1167 std::string contents;
1168 bool allowed_to_be_default_match;
1170 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
1172 const std::string json;
1173 const DefaultFetcherMatch matches[6];
1174 const std::string inline_autocompletion;
1176 // Ensure that suggestrelevance scores reorder matches.
1177 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1178 { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch,
1179 kEmptyMatch, kEmptyMatch },
1181 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1182 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1183 "\"google:suggestrelevance\":[1, 2]}]",
1184 { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch,
1185 kEmptyMatch, kEmptyMatch },
1188 // Without suggested relevance scores, we should only allow one
1189 // navsuggest result to be be displayed.
1190 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1191 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1192 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1193 kEmptyMatch, kEmptyMatch },
1196 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1197 // Negative values will have no effect; the calculated value will be used.
1198 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1199 "\"google:suggestrelevance\":[9998]}]",
1200 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1203 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1204 "\"google:suggestrelevance\":[9999]}]",
1205 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1208 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1209 "\"google:suggestrelevance\":[9999]}]",
1210 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1213 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1214 "\"google:suggestrelevance\":[9999]}]",
1215 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1218 { "[\"a\",[\"http://a.com\"],[],[],"
1219 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1220 "\"google:verbatimrelevance\":9999,"
1221 "\"google:suggestrelevance\":[9998]}]",
1222 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1225 { "[\"a\",[\"http://a.com\"],[],[],"
1226 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1227 "\"google:verbatimrelevance\":9998,"
1228 "\"google:suggestrelevance\":[9999]}]",
1229 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1232 { "[\"a\",[\"http://a.com\"],[],[],"
1233 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1234 "\"google:verbatimrelevance\":0,"
1235 "\"google:suggestrelevance\":[9999]}]",
1236 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1239 { "[\"a\",[\"http://a.com\"],[],[],"
1240 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1241 "\"google:verbatimrelevance\":-1,"
1242 "\"google:suggestrelevance\":[9999]}]",
1243 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1247 // Ensure that both types of relevance scores reorder matches together.
1248 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1249 "\"google:verbatimrelevance\":9998}]",
1250 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
1254 // Ensure that only inlinable matches may be ranked as the highest result.
1255 // Ignore all suggested relevance scores if this constraint is violated.
1256 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1257 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1260 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1261 "\"google:verbatimrelevance\":0}]",
1262 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1265 { "[\"a\",[\"http://b.com\"],[],[],"
1266 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1267 "\"google:suggestrelevance\":[9999]}]",
1268 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1269 kEmptyMatch, kEmptyMatch },
1271 { "[\"a\",[\"http://b.com\"],[],[],"
1272 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1273 "\"google:suggestrelevance\":[9999],"
1274 "\"google:verbatimrelevance\":0}]",
1275 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1276 kEmptyMatch, kEmptyMatch },
1278 { "[\"a\",[\"https://a/\"],[],[],"
1279 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1280 "\"google:suggestrelevance\":[9999]}]",
1281 { { "https://a", true }, { "a", true }, kEmptyMatch, kEmptyMatch,
1282 kEmptyMatch, kEmptyMatch },
1285 // Ensure that the top result is ranked as highly as calculated verbatim.
1286 // Ignore the suggested verbatim relevance if this constraint is violated.
1287 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1288 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1291 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1292 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1295 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1296 "\"google:verbatimrelevance\":0}]",
1297 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1300 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1301 "\"google:verbatimrelevance\":0}]",
1302 { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1305 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1306 "\"google:verbatimrelevance\":2}]",
1307 { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1310 { "[\"a\",[\"http://a.com\"],[],[],"
1311 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1312 "\"google:suggestrelevance\":[1],"
1313 "\"google:verbatimrelevance\":0}]",
1314 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1317 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1318 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1319 "\"google:suggestrelevance\":[1, 2],"
1320 "\"google:verbatimrelevance\":0}]",
1321 { { "a", true }, { "a2.com", true }, { "a1.com", true }, kEmptyMatch,
1322 kEmptyMatch, kEmptyMatch },
1325 // Ensure that all suggestions are considered, regardless of order.
1326 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1327 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1328 { { "a", true }, { "h", false }, { "g", false }, { "f", false },
1329 {"e", false }, {"d", false } },
1331 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1332 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1333 "\"http://h.com\"],[],[],"
1334 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1335 "\"NAVIGATION\", \"NAVIGATION\","
1336 "\"NAVIGATION\", \"NAVIGATION\","
1338 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1339 { { "a", true }, { "h.com", false }, { "g.com", false },
1340 { "f.com", false }, {"e.com", false }, {"d.com", false } },
1343 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1344 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1345 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
1348 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1349 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1352 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1353 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1354 "\"google:suggestrelevance\":[1]}]",
1355 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1356 kEmptyMatch, kEmptyMatch },
1358 { "[\"a\",[\"http://a1.com\"],[],[],"
1359 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1360 "\"google:suggestrelevance\":[9999, 1]}]",
1361 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1362 kEmptyMatch, kEmptyMatch },
1365 // Ensure that all 'verbatim' results are merged with their maximum score.
1366 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1367 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1368 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1371 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1372 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1373 "\"google:verbatimrelevance\":0}]",
1374 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1378 // Ensure that verbatim is always generated without other suggestions.
1379 // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1380 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1381 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1384 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1385 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1390 std::map<std::string, std::string> params;
1391 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
1392 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled;
1393 ASSERT_TRUE(chrome_variations::AssociateVariationParams(
1394 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
1395 base::FieldTrialList::CreateFieldTrial(
1396 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
1398 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1399 QueryForInput(ASCIIToUTF16("a"), false, false);
1400 net::TestURLFetcher* fetcher =
1401 test_factory_.GetFetcherByID(
1402 SearchProvider::kDefaultProviderURLFetcherID);
1403 ASSERT_TRUE(fetcher);
1404 fetcher->set_response_code(200);
1405 fetcher->SetResponseString(cases[i].json);
1406 fetcher->delegate()->OnURLFetchComplete(fetcher);
1407 RunTillProviderDone();
1409 const std::string description = "for input with json=" + cases[i].json;
1410 const ACMatches& matches = provider_->matches();
1411 // The top match must inline and score as highly as calculated verbatim.
1412 ASSERT_FALSE(matches.empty());
1413 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1414 matches[0].inline_autocompletion) << description;
1415 EXPECT_GE(matches[0].relevance, 1300) << description;
1417 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1419 // Ensure that the returned matches equal the expectations.
1420 for (; j < matches.size(); ++j) {
1421 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1422 matches[j].contents) << description;
1423 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1424 matches[j].allowed_to_be_default_match) << description;
1426 // Ensure that no expected matches are missing.
1427 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1428 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1429 "Case # " << i << " " << description;
1433 // This test is like DefaultFetcherSuggestRelevance above except it enables
1434 // the field trial that causes the omnibox to be willing to reorder matches
1435 // to guarantee the top result is a legal default match. This field trial
1436 // causes SearchProvider to allow some constraints to be violated that it
1437 // wouldn't normally because the omnibox will fix the problems later.
1438 TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevanceWithReorder) {
1439 struct DefaultFetcherMatch {
1440 std::string contents;
1441 bool allowed_to_be_default_match;
1443 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
1445 const std::string json;
1446 const DefaultFetcherMatch matches[6];
1447 const std::string inline_autocompletion;
1449 // Ensure that suggestrelevance scores reorder matches.
1450 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1451 { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch, kEmptyMatch,
1454 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1455 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1456 "\"google:suggestrelevance\":[1, 2]}]",
1457 { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch,
1458 kEmptyMatch, kEmptyMatch },
1461 // Without suggested relevance scores, we should only allow one
1462 // navsuggest result to be be displayed.
1463 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1464 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1465 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1466 kEmptyMatch, kEmptyMatch },
1469 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1470 // Negative values will have no effect; the calculated value will be used.
1471 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1472 "\"google:suggestrelevance\":[9998]}]",
1473 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1476 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1477 "\"google:suggestrelevance\":[9999]}]",
1478 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1481 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1482 "\"google:suggestrelevance\":[9999]}]",
1483 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1486 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1487 "\"google:suggestrelevance\":[9999]}]",
1488 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1491 { "[\"a\",[\"http://a.com\"],[],[],"
1492 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1493 "\"google:verbatimrelevance\":9999,"
1494 "\"google:suggestrelevance\":[9998]}]",
1495 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1498 { "[\"a\",[\"http://a.com\"],[],[],"
1499 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1500 "\"google:verbatimrelevance\":9998,"
1501 "\"google:suggestrelevance\":[9999]}]",
1502 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1505 { "[\"a\",[\"http://a.com\"],[],[],"
1506 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1507 "\"google:verbatimrelevance\":0,"
1508 "\"google:suggestrelevance\":[9999]}]",
1509 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1512 { "[\"a\",[\"http://a.com\"],[],[],"
1513 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1514 "\"google:verbatimrelevance\":-1,"
1515 "\"google:suggestrelevance\":[9999]}]",
1516 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1520 // Ensure that both types of relevance scores reorder matches together.
1521 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1522 "\"google:verbatimrelevance\":9998}]",
1523 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
1527 // Allow non-inlineable matches to be the highest-scoring match but,
1528 // if the result set lacks a single inlineable result, abandon suggested
1529 // relevance scores entirely.
1530 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1531 { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1534 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1535 "\"google:verbatimrelevance\":0}]",
1536 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1539 { "[\"a\",[\"http://b.com\"],[],[],"
1540 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1541 "\"google:suggestrelevance\":[9999]}]",
1542 { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch,
1543 kEmptyMatch, kEmptyMatch },
1545 { "[\"a\",[\"http://b.com\"],[],[],"
1546 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1547 "\"google:suggestrelevance\":[9999],"
1548 "\"google:verbatimrelevance\":0}]",
1549 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1550 kEmptyMatch, kEmptyMatch },
1553 // Allow low-scoring matches.
1554 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1555 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1558 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1559 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1562 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1563 "\"google:verbatimrelevance\":0}]",
1564 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1567 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1568 "\"google:verbatimrelevance\":0}]",
1569 { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1572 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1573 "\"google:verbatimrelevance\":2}]",
1574 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1577 { "[\"a\",[\"http://a.com\"],[],[],"
1578 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1579 "\"google:suggestrelevance\":[1],"
1580 "\"google:verbatimrelevance\":0}]",
1581 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1584 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1585 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1586 "\"google:suggestrelevance\":[1, 2],"
1587 "\"google:verbatimrelevance\":0}]",
1588 { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1589 kEmptyMatch, kEmptyMatch },
1592 // Ensure that all suggestions are considered, regardless of order.
1593 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1594 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1595 { { "a", true }, { "h", false }, { "g", false }, { "f", false },
1596 { "e", false }, { "d", false } },
1598 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1599 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1600 "\"http://h.com\"],[],[],"
1601 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1602 "\"NAVIGATION\", \"NAVIGATION\","
1603 "\"NAVIGATION\", \"NAVIGATION\","
1605 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1606 { { "a", true }, { "h.com", false }, { "g.com", false },
1607 { "f.com", false }, { "e.com", false }, { "d.com", false } },
1610 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1611 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1612 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
1615 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1616 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1619 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1620 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1621 "\"google:suggestrelevance\":[1]}]",
1622 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1623 kEmptyMatch, kEmptyMatch },
1625 { "[\"a\",[\"http://a1.com\"],[],[],"
1626 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1627 "\"google:suggestrelevance\":[9999, 1]}]",
1628 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1629 kEmptyMatch, kEmptyMatch },
1632 // Ensure that all 'verbatim' results are merged with their maximum score.
1633 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1634 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1635 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1638 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1639 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1640 "\"google:verbatimrelevance\":0}]",
1641 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1645 // Ensure that verbatim is always generated without other suggestions.
1646 // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1647 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1648 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1651 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1652 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1657 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1658 QueryForInput(ASCIIToUTF16("a"), false, false);
1659 net::TestURLFetcher* fetcher =
1660 test_factory_.GetFetcherByID(
1661 SearchProvider::kDefaultProviderURLFetcherID);
1662 ASSERT_TRUE(fetcher);
1663 fetcher->set_response_code(200);
1664 fetcher->SetResponseString(cases[i].json);
1665 fetcher->delegate()->OnURLFetchComplete(fetcher);
1666 RunTillProviderDone();
1668 const std::string description = "for input with json=" + cases[i].json;
1669 const ACMatches& matches = provider_->matches();
1670 ASSERT_FALSE(matches.empty());
1671 // Find the first match that's allowed to be the default match and check
1672 // its inline_autocompletion.
1673 ACMatches::const_iterator it = FindDefaultMatch(matches);
1674 ASSERT_NE(matches.end(), it);
1675 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1676 it->inline_autocompletion) << description;
1678 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1680 // Ensure that the returned matches equal the expectations.
1681 for (; j < matches.size(); ++j) {
1682 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1683 matches[j].contents) << description;
1684 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1685 matches[j].allowed_to_be_default_match) << description;
1687 // Ensure that no expected matches are missing.
1688 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1689 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1690 "Case # " << i << " " << description;
1694 // Verifies that suggest results with relevance scores are added
1695 // properly when using the keyword fetcher. This is similar to the
1696 // test DefaultFetcherSuggestRelevance above but this uses inputs that
1697 // trigger keyword suggestions (i.e., "k a" rather than "a") and has
1698 // different expectations (because now the results are a mix of
1699 // keyword suggestions and default provider suggestions). When a new
1700 // test is added to this TEST_F, please consider if it would be
1701 // appropriate to add to DefaultFetcherSuggestRelevance as well.
1702 TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
1703 struct KeywordFetcherMatch {
1704 std::string contents;
1706 bool allowed_to_be_default_match;
1708 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
1710 const std::string json;
1711 const KeywordFetcherMatch matches[6];
1712 const std::string inline_autocompletion;
1714 // Ensure that suggest relevance scores reorder matches and that
1715 // the keyword verbatim (lacking a suggested verbatim score) beats
1716 // the default provider verbatim.
1717 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1718 { { "a", true, true },
1719 { "k a", false, false },
1720 { "c", true, false },
1721 { "b", true, false },
1722 kEmptyMatch, kEmptyMatch },
1724 // Again, check that relevance scores reorder matches, just this
1725 // time with navigation matches. This also checks that with
1726 // suggested relevance scores we allow multiple navsuggest results.
1727 // Note that navsuggest results that come from a keyword provider
1728 // are marked as not a keyword result. (They don't go to a
1729 // keyword search engine.)
1730 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
1731 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1732 "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
1733 { { "a", true, true },
1734 { "d", true, false },
1735 { "c.com", false, false },
1736 { "b.com", false, false },
1737 { "k a", false, false },
1741 // Without suggested relevance scores, we should only allow one
1742 // navsuggest result to be be displayed.
1743 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1744 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1745 { { "a", true, true },
1746 { "b.com", false, false },
1747 { "k a", false, false },
1748 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1751 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1752 // Negative values will have no effect; the calculated value will be used.
1753 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1754 "\"google:suggestrelevance\":[9998]}]",
1755 { { "a", true, true },
1756 { "a1", true, true },
1757 { "k a", false, false },
1758 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1760 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1761 "\"google:suggestrelevance\":[9999]}]",
1762 { { "a1", true, true },
1763 { "a", true, true },
1764 { "k a", false, false },
1765 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1767 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1768 "\"google:suggestrelevance\":[9999]}]",
1769 { { "a1", true, true },
1770 { "k a", false, false },
1771 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1773 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1774 "\"google:suggestrelevance\":[9999]}]",
1775 { { "a1", true, true },
1776 { "a", true, true },
1777 { "k a", false, false },
1778 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1780 { "[\"a\",[\"http://a.com\"],[],[],"
1781 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1782 "\"google:verbatimrelevance\":9999,"
1783 "\"google:suggestrelevance\":[9998]}]",
1784 { { "a", true, true },
1785 { "a.com", false, false },
1786 { "k a", false, false },
1787 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1790 // Ensure that both types of relevance scores reorder matches together.
1791 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1792 "\"google:verbatimrelevance\":9998}]",
1793 { { "a1", true, true },
1794 { "a", true, true },
1795 { "a2", true, true },
1796 { "k a", false, false },
1797 kEmptyMatch, kEmptyMatch },
1800 // Ensure that only inlinable matches may be ranked as the highest result.
1801 // Ignore all suggested relevance scores if this constraint is violated.
1802 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1803 { { "a", true, true },
1804 { "b", true, false },
1805 { "k a", false, false },
1806 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1808 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1809 "\"google:verbatimrelevance\":0}]",
1810 { { "a", true, true },
1811 { "b", true, false },
1812 { "k a", false, false },
1813 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1815 { "[\"a\",[\"http://b.com\"],[],[],"
1816 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1817 "\"google:suggestrelevance\":[9999]}]",
1818 { { "a", true, true },
1819 { "b.com", false, false },
1820 { "k a", false, false },
1821 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1823 { "[\"a\",[\"http://b.com\"],[],[],"
1824 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1825 "\"google:suggestrelevance\":[9999],"
1826 "\"google:verbatimrelevance\":0}]",
1827 { { "a", true, true },
1828 { "b.com", false, false },
1829 { "k a", false, false },
1830 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1833 // Ensure that the top result is ranked as highly as calculated verbatim.
1834 // Ignore the suggested verbatim relevance if this constraint is violated.
1835 // Note that keyword suggestions by default (not in suggested relevance
1836 // mode) score more highly than the default verbatim.
1837 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1838 { { "a", true, true },
1839 { "a1", true, true },
1840 { "k a", false, false },
1841 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1843 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1844 { { "a", true, true },
1845 { "a1", true, true },
1846 { "k a", false, false },
1847 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1849 // Continuing the same category of tests, but make sure we keep the
1850 // suggested relevance scores even as we discard the verbatim relevance
1852 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1853 "\"google:verbatimrelevance\":0}]",
1854 { { "a", true, true },
1855 { "k a", false, false },
1856 { "a1", true, true },
1857 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1859 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1860 "\"google:verbatimrelevance\":0}]",
1861 { { "a", true, true },
1862 { "k a", false, false },
1863 { "a2", true, true },
1864 { "a1", true, true },
1865 kEmptyMatch, kEmptyMatch },
1867 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1868 "\"google:verbatimrelevance\":2}]",
1869 { { "a", true, true },
1870 { "k a", false, false },
1871 { "a2", true, true },
1872 { "a1", true, true },
1873 kEmptyMatch, kEmptyMatch },
1876 // Ensure that all suggestions are considered, regardless of order.
1877 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1878 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1879 { { "a", true, true },
1880 { "k a", false, false },
1881 { "h", true, false },
1882 { "g", true, false },
1883 { "f", true, false },
1884 { "e", true, false } },
1886 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1887 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1888 "\"http://h.com\"],[],[],"
1889 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1890 "\"NAVIGATION\", \"NAVIGATION\","
1891 "\"NAVIGATION\", \"NAVIGATION\","
1893 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1894 { { "a", true, true },
1895 { "k a", false, false },
1896 { "h.com", false, false },
1897 { "g.com", false, false },
1898 { "f.com", false, false },
1899 { "e.com", false, false } },
1902 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1903 // Note that keyword suggestions by default (not in suggested relevance
1904 // mode) score more highly than the default verbatim.
1905 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1906 { { "a", true, true },
1907 { "a1", true, true },
1908 { "a2", true, true },
1909 { "k a", false, false },
1910 kEmptyMatch, kEmptyMatch },
1912 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1913 { { "a", true, true },
1914 { "a1", true, true },
1915 { "k a", false, false },
1916 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1918 // In this case, ignoring the suggested relevance scores means we keep
1919 // only one navsuggest result.
1920 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1921 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1922 "\"google:suggestrelevance\":[1]}]",
1923 { { "a", true, true },
1924 { "a1.com", false, false },
1925 { "k a", false, false },
1926 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1928 { "[\"a\",[\"http://a1.com\"],[],[],"
1929 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1930 "\"google:suggestrelevance\":[9999, 1]}]",
1931 { { "a", true, true },
1932 { "a1.com", false, false },
1933 { "k a", false, false },
1934 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1937 // Ensure that all 'verbatim' results are merged with their maximum score.
1938 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1939 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1940 { { "a2", true, true },
1941 { "a", true, true },
1942 { "a1", true, true },
1943 { "k a", false, false },
1944 kEmptyMatch, kEmptyMatch },
1946 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1947 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1948 "\"google:verbatimrelevance\":0}]",
1949 { { "a2", true, true },
1950 { "a", true, true },
1951 { "a1", true, true },
1952 { "k a", false, false },
1953 kEmptyMatch, kEmptyMatch },
1956 // Ensure that verbatim is always generated without other suggestions.
1957 // TODO(mpearson): Ensure the value of verbatimrelevance is respected
1958 // (except when suggested relevances are ignored).
1959 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1960 { { "a", true, true },
1961 { "k a", false, false },
1962 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1964 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1965 { { "a", true, true },
1966 { "k a", false, false },
1967 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1970 // Check that navsuggestions will be demoted below queries.
1971 // (Navsuggestions are not allowed to appear first.) In the process,
1972 // make sure the navsuggestions still remain in the same order.
1973 // First, check the situation where navsuggest scores more than verbatim
1974 // and there are no query suggestions.
1975 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1976 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1977 "\"google:verbatimrelevance\":9990,"
1978 "\"google:suggestrelevance\":[9998, 9999]}]",
1979 { { "a", true, true },
1980 { "a2.com", false, false },
1981 { "a1.com", false, false },
1982 { "k a", false, false },
1983 kEmptyMatch, kEmptyMatch },
1985 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1986 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1987 "\"google:verbatimrelevance\":9990,"
1988 "\"google:suggestrelevance\":[9999, 9998]}]",
1989 { { "a", true, true },
1990 { "a1.com", false, false },
1991 { "a2.com", false, false },
1992 { "k a", false, false },
1993 kEmptyMatch, kEmptyMatch },
1995 { "[\"a\",[\"https://a/\"],[],[],"
1996 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1997 "\"google:suggestrelevance\":[9999]}]",
1998 { { "a", true, true },
1999 { "https://a", false, false },
2000 { "k a", false, false },
2001 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2003 // Check when navsuggest scores more than verbatim and there is query
2004 // suggestion but it scores lower.
2005 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2006 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2007 "\"google:verbatimrelevance\":9990,"
2008 "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
2009 { { "a", true, true },
2010 { "a2.com", false, false },
2011 { "a1.com", false, false },
2012 { "a3", true, true },
2013 { "k a", false, false },
2016 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2017 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2018 "\"google:verbatimrelevance\":9990,"
2019 "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
2020 { { "a", true, true },
2021 { "a1.com", false, false },
2022 { "a2.com", false, false },
2023 { "a3", true, true },
2024 { "k a", false, false },
2027 // Check when navsuggest scores more than a query suggestion. There is
2028 // a verbatim but it scores lower.
2029 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2030 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2031 "\"google:verbatimrelevance\":9990,"
2032 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
2033 { { "a3", true, true },
2034 { "a2.com", false, false },
2035 { "a1.com", false, false },
2036 { "a", true, true },
2037 { "k a", false, false },
2040 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2041 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2042 "\"google:verbatimrelevance\":9990,"
2043 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
2044 { { "a3", true, true },
2045 { "a1.com", false, false },
2046 { "a2.com", false, false },
2047 { "a", true, true },
2048 { "k a", false, false },
2051 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2052 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2053 "\"google:verbatimrelevance\":0,"
2054 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
2055 { { "a3", true, true },
2056 { "a2.com", false, false },
2057 { "a1.com", false, false },
2058 { "k a", false, false },
2059 kEmptyMatch, kEmptyMatch },
2061 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2062 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2063 "\"google:verbatimrelevance\":0,"
2064 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
2065 { { "a3", true, true },
2066 { "a1.com", false, false },
2067 { "a2.com", false, false },
2068 { "k a", false, false },
2069 kEmptyMatch, kEmptyMatch },
2071 // Check when there is neither verbatim nor a query suggestion that,
2072 // because we can't demote navsuggestions below a query suggestion,
2073 // we abandon suggested relevance scores entirely. One consequence is
2074 // that this means we restore the keyword verbatim match. Note
2075 // that in this case of abandoning suggested relevance scores, we still
2076 // keep the navsuggestions in the same order, but we revert to only allowing
2077 // one navigation to appear because the scores are completely local.
2078 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2079 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2080 "\"google:verbatimrelevance\":0,"
2081 "\"google:suggestrelevance\":[9998, 9999]}]",
2082 { { "a", true, true },
2083 { "a2.com", false, false },
2084 { "k a", false, false },
2085 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2087 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2088 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2089 "\"google:verbatimrelevance\":0,"
2090 "\"google:suggestrelevance\":[9999, 9998]}]",
2091 { { "a", true, true },
2092 { "a1.com", false, false },
2093 { "k a", false, false },
2094 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2096 // More checks that everything works when it's not necessary to demote.
2097 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2098 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2099 "\"google:verbatimrelevance\":9990,"
2100 "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
2101 { { "a3", true, true },
2102 { "a2.com", false, false },
2103 { "a1.com", false, false },
2104 { "a", true, true },
2105 { "k a", false, false },
2108 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2109 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2110 "\"google:verbatimrelevance\":9990,"
2111 "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
2112 { { "a3", true, true },
2113 { "a1.com", false, false },
2114 { "a2.com", false, false },
2115 { "a", true, true },
2116 { "k a", false, false },
2121 std::map<std::string, std::string> params;
2122 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
2123 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled;
2124 ASSERT_TRUE(chrome_variations::AssociateVariationParams(
2125 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
2126 base::FieldTrialList::CreateFieldTrial(
2127 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
2129 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2130 QueryForInput(ASCIIToUTF16("k a"), false, true);
2132 // Set up a default fetcher with no results.
2133 net::TestURLFetcher* default_fetcher =
2134 test_factory_.GetFetcherByID(
2135 SearchProvider::kDefaultProviderURLFetcherID);
2136 ASSERT_TRUE(default_fetcher);
2137 default_fetcher->set_response_code(200);
2138 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
2139 default_fetcher = NULL;
2141 // Set up a keyword fetcher with provided results.
2142 net::TestURLFetcher* keyword_fetcher =
2143 test_factory_.GetFetcherByID(
2144 SearchProvider::kKeywordProviderURLFetcherID);
2145 ASSERT_TRUE(keyword_fetcher);
2146 keyword_fetcher->set_response_code(200);
2147 keyword_fetcher->SetResponseString(cases[i].json);
2148 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
2149 keyword_fetcher = NULL;
2150 RunTillProviderDone();
2152 const std::string description = "for input with json=" + cases[i].json;
2153 const ACMatches& matches = provider_->matches();
2154 // The top match must inline and score as highly as calculated verbatim.
2155 ASSERT_FALSE(matches.empty());
2156 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2157 matches[0].inline_autocompletion) << description;
2158 EXPECT_GE(matches[0].relevance, 1300) << description;
2160 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2162 // Ensure that the returned matches equal the expectations.
2163 for (; j < matches.size(); ++j) {
2164 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
2165 matches[j].contents) << description;
2166 EXPECT_EQ(cases[i].matches[j].from_keyword,
2167 matches[j].keyword == ASCIIToUTF16("k")) << description;
2168 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
2169 matches[j].allowed_to_be_default_match) << description;
2171 // Ensure that no expected matches are missing.
2172 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
2173 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
2174 "Case # " << i << " " << description;
2178 // This test is like KeywordFetcherSuggestRelevance above except it
2179 // enables the field trial that causes the omnibox to be willing to
2180 // reorder matches to guarantee the top result is a legal default
2181 // match. This field trial causes SearchProvider to allow some
2182 // constraints to be violated that it wouldn't normally because the
2183 // omnibox will fix the problems later.
2184 TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevanceWithReorder) {
2185 struct KeywordFetcherMatch {
2186 std::string contents;
2188 bool allowed_to_be_default_match;
2190 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
2192 const std::string json;
2193 const KeywordFetcherMatch matches[6];
2194 const std::string inline_autocompletion;
2196 // Ensure that suggest relevance scores reorder matches and that
2197 // the keyword verbatim (lacking a suggested verbatim score) beats
2198 // the default provider verbatim.
2199 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2200 { { "a", true, true },
2201 { "k a", false, false },
2202 { "c", true, false },
2203 { "b", true, false },
2204 kEmptyMatch, kEmptyMatch },
2206 // Again, check that relevance scores reorder matches, just this
2207 // time with navigation matches. This also checks that with
2208 // suggested relevance scores we allow multiple navsuggest results.
2209 // Note that navsuggest results that come from a keyword provider
2210 // are marked as not a keyword result. (They don't go to a
2211 // keyword search engine.)
2212 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
2213 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2214 "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
2215 { { "a", true, true },
2216 { "d", true, false },
2217 { "c.com", false, false },
2218 { "b.com", false, false },
2219 { "k a", false, false },
2223 // Without suggested relevance scores, we should only allow one
2224 // navsuggest result to be be displayed.
2225 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
2226 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
2227 { { "a", true, true },
2228 { "b.com", false, false },
2229 { "k a", false, false },
2230 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2233 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
2234 // Negative values will have no effect; the calculated value will be used.
2235 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
2236 "\"google:suggestrelevance\":[9998]}]",
2237 { { "a", true, true },
2238 { "a1", true, true },
2239 { "k a", false, false },
2240 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2242 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
2243 "\"google:suggestrelevance\":[9999]}]",
2244 { { "a1", true, true },
2245 { "a", true, true },
2246 { "k a", false, false },
2247 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2249 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
2250 "\"google:suggestrelevance\":[9999]}]",
2251 { { "a1", true, true },
2252 { "k a", false, false },
2253 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
2255 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
2256 "\"google:suggestrelevance\":[9999]}]",
2257 { { "a1", true, true },
2258 { "a", true, true },
2259 { "k a", false, false },
2260 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2262 { "[\"a\",[\"http://a.com\"],[],[],"
2263 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2264 "\"google:verbatimrelevance\":9999,"
2265 "\"google:suggestrelevance\":[9998]}]",
2266 { { "a", true, true },
2267 { "a.com", false, false },
2268 { "k a", false, false },
2269 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2272 // Ensure that both types of relevance scores reorder matches together.
2273 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
2274 "\"google:verbatimrelevance\":9998}]",
2275 { { "a1", true, true },
2276 { "a", true, true },
2277 { "a2", true, true },
2278 { "k a", false, false },
2279 kEmptyMatch, kEmptyMatch },
2282 // Check that non-inlinable matches may be ranked as the highest result
2283 // if there is at least one inlineable match.
2284 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
2285 { { "b", true, false },
2286 { "a", true, true },
2287 { "k a", false, false },
2288 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2290 { "[\"a\",[\"http://b.com\"],[],[],"
2291 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2292 "\"google:suggestrelevance\":[9999]}]",
2293 { { "b.com", false, false },
2294 { "a", true, true },
2295 { "k a", false, false },
2296 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2298 // On the other hand, if there is no inlineable match, restore
2299 // the keyword verbatim score.
2300 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
2301 "\"google:verbatimrelevance\":0}]",
2302 { { "b", true, false },
2303 { "a", true, true },
2304 { "k a", false, false },
2305 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2307 { "[\"a\",[\"http://b.com\"],[],[],"
2308 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2309 "\"google:suggestrelevance\":[9999],"
2310 "\"google:verbatimrelevance\":0}]",
2311 { { "b.com", false, false },
2312 { "a", true, true },
2313 { "k a", false, false },
2314 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2317 // The top result does not have to score as highly as calculated
2318 // verbatim. i.e., there are no minimum score restrictions in
2320 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
2321 { { "a1", true, true },
2322 { "k a", false, false },
2323 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
2325 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
2326 { { "a1", true, true },
2327 { "k a", false, false },
2328 { "a", true, true },
2329 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2331 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
2332 "\"google:verbatimrelevance\":0}]",
2333 { { "k a", false, false },
2334 { "a1", true, true },
2335 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
2337 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
2338 "\"google:verbatimrelevance\":0}]",
2340 { "k a", false, false },
2341 { "a2", true, true },
2342 { "a1", true, true },
2343 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2345 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
2346 "\"google:verbatimrelevance\":2}]",
2347 { { "k a", false, false },
2348 { "a2", true, true },
2349 { "a", true, true },
2350 { "a1", true, true },
2351 kEmptyMatch, kEmptyMatch },
2354 // Ensure that all suggestions are considered, regardless of order.
2355 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
2356 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
2357 { { "a", true, true },
2358 { "k a", false, false },
2359 { "h", true, false },
2360 { "g", true, false },
2361 { "f", true, false },
2362 { "e", true, false } },
2364 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
2365 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
2366 "\"http://h.com\"],[],[],"
2367 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
2368 "\"NAVIGATION\", \"NAVIGATION\","
2369 "\"NAVIGATION\", \"NAVIGATION\","
2371 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
2372 { { "a", true, true },
2373 { "k a", false, false },
2374 { "h.com", false, false },
2375 { "g.com", false, false },
2376 { "f.com", false, false },
2377 { "e.com", false, false } },
2380 // Ensure that incorrectly sized suggestion relevance lists are ignored.
2381 // Note that keyword suggestions by default (not in suggested relevance
2382 // mode) score more highly than the default verbatim.
2383 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
2384 { { "a", true, true },
2385 { "a1", true, true },
2386 { "a2", true, true },
2387 { "k a", false, false },
2388 kEmptyMatch, kEmptyMatch },
2390 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
2391 { { "a", true, true },
2392 { "a1", true, true },
2393 { "k a", false, false },
2394 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2396 // In this case, ignoring the suggested relevance scores means we keep
2397 // only one navsuggest result.
2398 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2399 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2400 "\"google:suggestrelevance\":[1]}]",
2401 { { "a", true, true },
2402 { "a1.com", false, false },
2403 { "k a", false, false },
2404 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2406 { "[\"a\",[\"http://a1.com\"],[],[],"
2407 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2408 "\"google:suggestrelevance\":[9999, 1]}]",
2409 { { "a", true, true },
2410 { "a1.com", false, false },
2411 { "k a", false, false },
2412 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2415 // Ensure that all 'verbatim' results are merged with their maximum score.
2416 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
2417 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
2418 { { "a2", true, true },
2419 { "a", true, true },
2420 { "a1", true, true },
2421 { "k a", false, false },
2422 kEmptyMatch, kEmptyMatch },
2424 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
2425 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
2426 "\"google:verbatimrelevance\":0}]",
2427 { { "a2", true, true },
2428 { "a", true, true },
2429 { "a1", true, true },
2430 { "k a", false, false },
2431 kEmptyMatch, kEmptyMatch },
2434 // Ensure that verbatim is always generated without other suggestions.
2435 // TODO(mpearson): Ensure the value of verbatimrelevance is respected
2436 // (except when suggested relevances are ignored).
2437 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
2438 { { "k a", false, false },
2439 { "a", true, true },
2440 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
2442 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
2443 { { "a", true, true },
2444 { "k a", false, false },
2445 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
2448 // In reorder mode, navsuggestions will not need to be demoted (because
2449 // they are marked as not allowed to be default match and will be
2450 // reordered as necessary).
2451 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2452 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2453 "\"google:verbatimrelevance\":9990,"
2454 "\"google:suggestrelevance\":[9998, 9999]}]",
2455 { { "a2.com", false, false },
2456 { "a1.com", false, false },
2457 { "a", true, true },
2458 { "k a", false, false },
2459 kEmptyMatch, kEmptyMatch },
2461 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2462 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2463 "\"google:verbatimrelevance\":9990,"
2464 "\"google:suggestrelevance\":[9999, 9998]}]",
2465 { { "a1.com", false, false },
2466 { "a2.com", false, false },
2467 { "a", true, true },
2468 { "k a", false, false },
2469 kEmptyMatch, kEmptyMatch },
2471 { "[\"a\",[\"https://a/\"],[],[],"
2472 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2473 "\"google:suggestrelevance\":[9999]}]",
2474 { { "https://a", false, false },
2475 { "a", true, true },
2476 { "k a", false, false },
2477 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2479 // Check when navsuggest scores more than verbatim and there is query
2480 // suggestion but it scores lower.
2481 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2482 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2483 "\"google:verbatimrelevance\":9990,"
2484 "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
2485 { { "a2.com", false, false },
2486 { "a1.com", false, false },
2487 { "a", true, true },
2488 { "a3", true, true },
2489 { "k a", false, false },
2492 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2493 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2494 "\"google:verbatimrelevance\":9990,"
2495 "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
2496 { { "a1.com", false, false },
2497 { "a2.com", false, false },
2498 { "a", true, true },
2499 { "a3", true, true },
2500 { "k a", false, false },
2503 // Check when navsuggest scores more than a query suggestion. There is
2504 // a verbatim but it scores lower.
2505 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2506 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2507 "\"google:verbatimrelevance\":9990,"
2508 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
2509 { { "a2.com", false, false },
2510 { "a1.com", false, false },
2511 { "a3", true, true },
2512 { "a", true, true },
2513 { "k a", false, false },
2516 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2517 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2518 "\"google:verbatimrelevance\":9990,"
2519 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
2520 { { "a1.com", false, false },
2521 { "a2.com", false, false },
2522 { "a3", true, true },
2523 { "a", true, true },
2524 { "k a", false, false },
2527 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2528 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2529 "\"google:verbatimrelevance\":0,"
2530 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
2531 { { "a2.com", false, false },
2532 { "a1.com", false, false },
2533 { "a3", true, true },
2534 { "k a", false, false },
2535 kEmptyMatch, kEmptyMatch },
2537 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2538 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2539 "\"google:verbatimrelevance\":0,"
2540 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
2541 { { "a1.com", false, false },
2542 { "a2.com", false, false },
2543 { "a3", true, true },
2544 { "k a", false, false },
2545 kEmptyMatch, kEmptyMatch },
2547 // Check when there is neither verbatim nor a query suggestion that,
2548 // because we can't demote navsuggestions below a query suggestion,
2549 // we restore the keyword verbatim score.
2550 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2551 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2552 "\"google:verbatimrelevance\":0,"
2553 "\"google:suggestrelevance\":[9998, 9999]}]",
2554 { { "a2.com", false, false },
2555 { "a1.com", false, false },
2556 { "a", true, true },
2557 { "k a", false, false },
2558 kEmptyMatch, kEmptyMatch },
2560 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2561 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2562 "\"google:verbatimrelevance\":0,"
2563 "\"google:suggestrelevance\":[9999, 9998]}]",
2564 { { "a1.com", false, false },
2565 { "a2.com", false, false },
2566 { "a", true, true },
2567 { "k a", false, false },
2568 kEmptyMatch, kEmptyMatch },
2570 // More checks that everything works when it's not necessary to demote.
2571 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2572 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2573 "\"google:verbatimrelevance\":9990,"
2574 "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
2575 { { "a3", true, true },
2576 { "a2.com", false, false },
2577 { "a1.com", false, false },
2578 { "a", true, true },
2579 { "k a", false, false },
2582 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2583 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2584 "\"google:verbatimrelevance\":9990,"
2585 "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
2586 { { "a3", true, true },
2587 { "a1.com", false, false },
2588 { "a2.com", false, false },
2589 { "a", true, true },
2590 { "k a", false, false },
2595 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2596 QueryForInput(ASCIIToUTF16("k a"), false, true);
2598 // Set up a default fetcher with no results.
2599 net::TestURLFetcher* default_fetcher =
2600 test_factory_.GetFetcherByID(
2601 SearchProvider::kDefaultProviderURLFetcherID);
2602 ASSERT_TRUE(default_fetcher);
2603 default_fetcher->set_response_code(200);
2604 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
2605 default_fetcher = NULL;
2607 // Set up a keyword fetcher with provided results.
2608 net::TestURLFetcher* keyword_fetcher =
2609 test_factory_.GetFetcherByID(
2610 SearchProvider::kKeywordProviderURLFetcherID);
2611 ASSERT_TRUE(keyword_fetcher);
2612 keyword_fetcher->set_response_code(200);
2613 keyword_fetcher->SetResponseString(cases[i].json);
2614 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
2615 keyword_fetcher = NULL;
2616 RunTillProviderDone();
2618 const std::string description = "for input with json=" + cases[i].json;
2619 const ACMatches& matches = provider_->matches();
2620 ASSERT_FALSE(matches.empty());
2621 // Find the first match that's allowed to be the default match and check
2622 // its inline_autocompletion.
2623 ACMatches::const_iterator it = FindDefaultMatch(matches);
2624 ASSERT_NE(matches.end(), it);
2625 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2626 it->inline_autocompletion) << description;
2628 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2630 // Ensure that the returned matches equal the expectations.
2631 for (; j < matches.size(); ++j) {
2632 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
2633 matches[j].contents) << description;
2634 EXPECT_EQ(cases[i].matches[j].from_keyword,
2635 matches[j].keyword == ASCIIToUTF16("k")) << description;
2636 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
2637 matches[j].allowed_to_be_default_match) << description;
2639 // Ensure that no expected matches are missing.
2640 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
2641 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
2642 "Case # " << i << " " << description;
2646 TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
2647 // We hardcode the string "term1" below, so ensure that the search term that
2648 // got added to history already is that string.
2649 ASSERT_EQ(ASCIIToUTF16("term1"), term1_);
2650 base::string16 term = term1_.substr(0, term1_.length() - 1);
2652 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2);
2653 profile_.BlockUntilHistoryProcessesPendingRequests();
2656 const base::string16 input;
2657 const std::string json;
2658 const std::string matches[6];
2660 // The history results outscore the default verbatim score. term2 has more
2661 // visits so it outscores term1. The suggestions are still returned since
2662 // they're server-scored.
2664 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
2665 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
2666 "\"google:suggestrelevance\":[1, 2, 3]}]",
2667 { "term2", "term1", "term", "a3", "a2", "a1" } },
2668 // Because we already have three suggestions by the time we see the history
2669 // results, they don't get returned.
2671 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
2672 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
2673 "\"google:verbatimrelevance\":1450,"
2674 "\"google:suggestrelevance\":[1440, 1430, 1420]}]",
2675 { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } },
2676 // If we only have two suggestions, we have room for a history result.
2678 "[\"term\",[\"a1\", \"a2\"],[],[],"
2679 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
2680 "\"google:verbatimrelevance\":1450,"
2681 "\"google:suggestrelevance\":[1430, 1410]}]",
2682 { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } },
2683 // If we have more than three suggestions, they should all be returned as
2684 // long as we have enough total space for them.
2686 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
2687 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
2688 "\"google:verbatimrelevance\":1450,"
2689 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]",
2690 { "term", "a1", "a2", "a3", "a4", kNotApplicable } },
2692 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[],"
2693 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\","
2694 "\"QUERY\", \"QUERY\"],"
2695 "\"google:verbatimrelevance\":1450,"
2696 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]",
2697 { "term", "a1", "a2", "a3", "a4", "a5" } },
2699 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
2700 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
2701 "\"google:verbatimrelevance\":1450,"
2702 "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]",
2703 { "term", "a1", "a2", "term2", "a3", "a4" } }
2706 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2707 QueryForInput(cases[i].input, false, false);
2708 net::TestURLFetcher* fetcher =
2709 test_factory_.GetFetcherByID(
2710 SearchProvider::kDefaultProviderURLFetcherID);
2711 ASSERT_TRUE(fetcher);
2712 fetcher->set_response_code(200);
2713 fetcher->SetResponseString(cases[i].json);
2714 fetcher->delegate()->OnURLFetchComplete(fetcher);
2715 RunTillProviderDone();
2717 const std::string description = "for input with json=" + cases[i].json;
2718 const ACMatches& matches = provider_->matches();
2720 // Ensure no extra matches are present.
2721 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2724 // Ensure that the returned matches equal the expectations.
2725 for (; j < matches.size(); ++j)
2726 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]),
2727 matches[j].contents) << description;
2728 // Ensure that no expected matches are missing.
2729 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
2730 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) <<
2731 "Case # " << i << " " << description;
2735 // Verifies suggest relevance behavior for URL input.
2736 TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
2737 struct DefaultFetcherUrlInputMatch {
2738 const std::string match_contents;
2739 AutocompleteMatch::Type match_type;
2740 bool allowed_to_be_default_match;
2742 const DefaultFetcherUrlInputMatch kEmptyMatch =
2743 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
2745 const std::string input;
2746 const std::string json;
2747 const DefaultFetcherUrlInputMatch output[4];
2749 // Ensure topmost NAVIGATION matches are allowed for URL input.
2750 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
2751 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2752 "\"google:suggestrelevance\":[9999]}]",
2753 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true },
2754 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2755 kEmptyMatch, kEmptyMatch } },
2756 { "a.com", "[\"a.com\",[\"https://a.com\"],[],[],"
2757 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2758 "\"google:suggestrelevance\":[9999]}]",
2759 { { "https://a.com", AutocompleteMatchType::NAVSUGGEST, true },
2760 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2761 kEmptyMatch, kEmptyMatch } },
2763 // Ensure topmost SUGGEST matches are not allowed for URL input.
2764 // SearchProvider disregards search and verbatim suggested relevances.
2765 { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2766 "{\"google:suggestrelevance\":[9999]}]",
2767 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2768 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true },
2769 kEmptyMatch, kEmptyMatch } },
2770 { "a.com", "[\"a.com\",[\"a.com/a\"],[],[],"
2771 "{\"google:suggestrelevance\":[9999]}]",
2772 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2773 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2774 kEmptyMatch, kEmptyMatch } },
2776 // Ensure the fallback mechanism allows inlinable NAVIGATION matches.
2777 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
2778 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2779 "\"google:suggestrelevance\":[9999, 9998]}]",
2780 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2781 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2782 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2784 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
2785 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2786 "\"google:suggestrelevance\":[9998, 9997],"
2787 "\"google:verbatimrelevance\":9999}]",
2788 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2789 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2790 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2793 // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches.
2794 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
2795 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2796 "\"google:suggestrelevance\":[9999, 9998]}]",
2797 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2798 { "abc.com", AutocompleteMatchType::NAVSUGGEST, false },
2799 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2801 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
2802 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2803 "\"google:suggestrelevance\":[9998, 9997],"
2804 "\"google:verbatimrelevance\":9999}]",
2805 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2806 { "abc.com", AutocompleteMatchType::NAVSUGGEST, false },
2807 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2811 std::map<std::string, std::string> params;
2812 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
2813 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled;
2814 ASSERT_TRUE(chrome_variations::AssociateVariationParams(
2815 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
2816 base::FieldTrialList::CreateFieldTrial(
2817 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
2819 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2820 QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2821 net::TestURLFetcher* fetcher =
2822 test_factory_.GetFetcherByID(
2823 SearchProvider::kDefaultProviderURLFetcherID);
2824 ASSERT_TRUE(fetcher);
2825 fetcher->set_response_code(200);
2826 fetcher->SetResponseString(cases[i].json);
2827 fetcher->delegate()->OnURLFetchComplete(fetcher);
2828 RunTillProviderDone();
2831 const ACMatches& matches = provider_->matches();
2832 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].output));
2833 // Ensure that the returned matches equal the expectations.
2834 for (; j < matches.size(); ++j) {
2835 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2836 matches[j].contents);
2837 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2838 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2839 matches[j].allowed_to_be_default_match);
2841 // Ensure that no expected matches are missing.
2842 for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) {
2843 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2844 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2845 cases[i].output[j].match_type);
2846 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2851 // This test is like DefaultProviderSuggestRelevanceScoringUrlInput
2852 // above except it enables the field trial that causes the omnibox to
2853 // be willing to reorder matches to guarantee the top result is a
2854 // legal default match. This field trial causes SearchProvider to
2855 // allow some constraints to be violated that it wouldn't normally
2856 // because the omnibox will fix the problems later.
2857 TEST_F(SearchProviderTest,
2858 DefaultProviderSuggestRelevanceScoringUrlInputWithReorder) {
2859 struct DefaultFetcherUrlInputMatch {
2860 const std::string match_contents;
2861 AutocompleteMatch::Type match_type;
2862 bool allowed_to_be_default_match;
2864 const DefaultFetcherUrlInputMatch kEmptyMatch =
2865 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
2867 const std::string input;
2868 const std::string json;
2869 const DefaultFetcherUrlInputMatch output[4];
2871 // Ensure NAVIGATION matches are allowed to be listed first for URL
2872 // input regardless of whether the match is inlineable. Note that
2873 // non-inlineable matches should not be allowed to be the default match.
2874 { "a.com", "[\"a.com\",[\"http://b.com/\"],[],[],"
2875 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2876 "\"google:suggestrelevance\":[9999]}]",
2877 { { "b.com", AutocompleteMatchType::NAVSUGGEST, false },
2878 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2879 kEmptyMatch, kEmptyMatch } },
2880 { "a.com", "[\"a.com\",[\"https://b.com\"],[],[],"
2881 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2882 "\"google:suggestrelevance\":[9999]}]",
2883 { { "https://b.com", AutocompleteMatchType::NAVSUGGEST, false },
2884 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2885 kEmptyMatch, kEmptyMatch } },
2886 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
2887 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2888 "\"google:suggestrelevance\":[9999]}]",
2889 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true },
2890 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2891 kEmptyMatch, kEmptyMatch } },
2892 { "a.com", "[\"a.com\",[\"https://a.com\"],[],[],"
2893 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2894 "\"google:suggestrelevance\":[9999]}]",
2895 { { "https://a.com", AutocompleteMatchType::NAVSUGGEST, true },
2896 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2897 kEmptyMatch, kEmptyMatch } },
2899 // Ensure topmost inlineable SUGGEST matches are NOT allowed for URL
2900 // input. SearchProvider disregards search and verbatim suggested
2902 { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2903 "{\"google:suggestrelevance\":[9999]}]",
2904 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2905 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true },
2906 kEmptyMatch, kEmptyMatch } },
2907 { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2908 "{\"google:suggestrelevance\":[9999]}]",
2909 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2910 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true },
2911 kEmptyMatch, kEmptyMatch } },
2913 // Ensure the fallback mechanism allows inlinable NAVIGATION matches.
2914 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2915 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2916 "\"google:suggestrelevance\":[9999, 9998]}]",
2917 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2918 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2919 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true },
2921 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2922 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2923 "\"google:suggestrelevance\":[9998, 9997],"
2924 "\"google:verbatimrelevance\":9999}]",
2925 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2926 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2927 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true },
2930 // Ensure topmost non-inlineable SUGGEST matches are allowed for URL
2931 // input assuming the top inlineable match is not a query (i.e., is a
2933 { "a.com", "[\"a.com\",[\"info\"],[],[],"
2934 "{\"google:suggestrelevance\":[9999]}]",
2935 { { "info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2936 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2937 kEmptyMatch, kEmptyMatch } },
2938 { "a.com", "[\"a.com\",[\"info\"],[],[],"
2939 "{\"google:suggestrelevance\":[9999]}]",
2940 { { "info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2941 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2942 kEmptyMatch, kEmptyMatch } },
2945 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2946 QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2947 net::TestURLFetcher* fetcher =
2948 test_factory_.GetFetcherByID(
2949 SearchProvider::kDefaultProviderURLFetcherID);
2950 ASSERT_TRUE(fetcher);
2951 fetcher->set_response_code(200);
2952 fetcher->SetResponseString(cases[i].json);
2953 fetcher->delegate()->OnURLFetchComplete(fetcher);
2954 RunTillProviderDone();
2957 const ACMatches& matches = provider_->matches();
2958 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].output));
2959 // Ensure that the returned matches equal the expectations.
2960 for (; j < matches.size(); ++j) {
2961 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2962 matches[j].contents);
2963 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2964 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2965 matches[j].allowed_to_be_default_match);
2967 // Ensure that no expected matches are missing.
2968 for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) {
2969 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2970 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2971 cases[i].output[j].match_type);
2972 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2977 // A basic test that verifies the field trial triggered parsing logic.
2978 TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) {
2979 QueryForInput(ASCIIToUTF16("foo"), false, false);
2981 // Make sure the default providers suggest service was queried.
2982 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
2983 SearchProvider::kDefaultProviderURLFetcherID);
2984 ASSERT_TRUE(fetcher);
2986 // Tell the SearchProvider the suggest query is done.
2987 fetcher->set_response_code(200);
2988 fetcher->SetResponseString(
2989 "[\"foo\",[\"foo bar\"],[\"\"],[],"
2990 "{\"google:suggesttype\":[\"QUERY\"],"
2991 "\"google:fieldtrialtriggered\":true}]");
2992 fetcher->delegate()->OnURLFetchComplete(fetcher);
2995 // Run till the history results complete.
2996 RunTillProviderDone();
2999 // Check for the match and field trial triggered bits.
3000 AutocompleteMatch match;
3001 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match));
3002 ProvidersInfo providers_info;
3003 provider_->AddProviderInfo(&providers_info);
3004 ASSERT_EQ(1U, providers_info.size());
3005 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
3006 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size());
3009 // Reset the session and check that bits are reset.
3010 provider_->ResetSession();
3011 ProvidersInfo providers_info;
3012 provider_->AddProviderInfo(&providers_info);
3013 ASSERT_EQ(1U, providers_info.size());
3014 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
3015 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size());
3019 // Verifies inline autocompletion of navigational results.
3020 TEST_F(SearchProviderTest, NavigationInline) {
3022 const std::string input;
3023 const std::string url;
3024 // Test the expected fill_into_edit, which may drop "http://".
3025 // Some cases do not trim "http://" to match from the start of the scheme.
3026 const std::string fill_into_edit;
3027 const std::string inline_autocompletion;
3028 const bool allowed_to_be_default_match_in_regular_mode;
3029 const bool allowed_to_be_default_match_in_prevent_inline_mode;
3031 // Do not inline matches that do not contain the input; trim http as needed.
3032 { "x", "http://www.abc.com",
3033 "www.abc.com", std::string(), false, false },
3034 { "https:", "http://www.abc.com",
3035 "www.abc.com", std::string(), false, false },
3036 { "http://www.abc.com/a", "http://www.abc.com",
3037 "http://www.abc.com", std::string(), false,
3039 { "http://www.abc.com", "https://www.abc.com",
3040 "https://www.abc.com", std::string(), false,
3042 { "http://abc.com", "ftp://abc.com",
3043 "ftp://abc.com", std::string(), false,
3045 { "https://www.abc.com", "http://www.abc.com",
3046 "www.abc.com", std::string(), false,
3048 { "ftp://abc.com", "http://abc.com",
3049 "abc.com", std::string(), false,
3052 // Do not inline matches with invalid input prefixes; trim http as needed.
3053 { "ttp", "http://www.abc.com",
3054 "www.abc.com", std::string(), false, false },
3055 { "://w", "http://www.abc.com",
3056 "www.abc.com", std::string(), false, false },
3057 { "ww.", "http://www.abc.com",
3058 "www.abc.com", std::string(), false, false },
3059 { ".ab", "http://www.abc.com",
3060 "www.abc.com", std::string(), false, false },
3061 { "bc", "http://www.abc.com",
3062 "www.abc.com", std::string(), false, false },
3063 { ".com", "http://www.abc.com",
3064 "www.abc.com", std::string(), false, false },
3066 // Do not inline matches that omit input domain labels; trim http as needed.
3067 { "www.a", "http://a.com",
3068 "a.com", std::string(), false, false },
3069 { "http://www.a", "http://a.com",
3070 "http://a.com", std::string(), false, false },
3071 { "www.a", "ftp://a.com",
3072 "ftp://a.com", std::string(), false, false },
3073 { "ftp://www.a", "ftp://a.com",
3074 "ftp://a.com", std::string(), false, false },
3076 // Input matching but with nothing to inline will not yield an offset, but
3077 // will be allowed to be default.
3078 { "abc.com", "http://www.abc.com",
3079 "www.abc.com", std::string(), true, true },
3080 { "abc.com/", "http://www.abc.com",
3081 "www.abc.com", std::string(), true, true },
3082 { "http://www.abc.com", "http://www.abc.com",
3083 "http://www.abc.com", std::string(), true, true },
3084 { "http://www.abc.com/", "http://www.abc.com",
3085 "http://www.abc.com", std::string(), true, true },
3087 // Inline matches when the input is a leading substring of the scheme.
3088 { "h", "http://www.abc.com",
3089 "http://www.abc.com", "ttp://www.abc.com", true, false },
3090 { "http", "http://www.abc.com",
3091 "http://www.abc.com", "://www.abc.com", true, false },
3093 // Inline matches when the input is a leading substring of the full URL.
3094 { "http:", "http://www.abc.com",
3095 "http://www.abc.com", "//www.abc.com", true, false },
3096 { "http://w", "http://www.abc.com",
3097 "http://www.abc.com", "ww.abc.com", true, false },
3098 { "http://www.", "http://www.abc.com",
3099 "http://www.abc.com", "abc.com", true, false },
3100 { "http://www.ab", "http://www.abc.com",
3101 "http://www.abc.com", "c.com", true, false },
3102 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
3103 "http://www.abc.com/path/file.htm?q=x#foo",
3104 "ath/file.htm?q=x#foo",
3106 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo",
3107 "http://abc.com/path/file.htm?q=x#foo",
3108 "ath/file.htm?q=x#foo",
3111 // Inline matches with valid URLPrefixes; only trim "http://".
3112 { "w", "http://www.abc.com",
3113 "www.abc.com", "ww.abc.com", true, false },
3114 { "www.a", "http://www.abc.com",
3115 "www.abc.com", "bc.com", true, false },
3116 { "abc", "http://www.abc.com",
3117 "www.abc.com", ".com", true, false },
3118 { "abc.c", "http://www.abc.com",
3119 "www.abc.com", "om", true, false },
3120 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
3121 "www.abc.com/path/file.htm?q=x#foo",
3122 "ath/file.htm?q=x#foo",
3124 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo",
3125 "abc.com/path/file.htm?q=x#foo",
3126 "ath/file.htm?q=x#foo",
3129 // Inline matches using the maximal URLPrefix components.
3130 { "h", "http://help.com",
3131 "help.com", "elp.com", true, false },
3132 { "http", "http://http.com",
3133 "http.com", ".com", true, false },
3134 { "h", "http://www.help.com",
3135 "www.help.com", "elp.com", true, false },
3136 { "http", "http://www.http.com",
3137 "www.http.com", ".com", true, false },
3138 { "w", "http://www.www.com",
3139 "www.www.com", "ww.com", true, false },
3141 // Test similar behavior for the ftp and https schemes.
3142 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
3143 "ftp://www.abc.com/path/file.htm?q=x#foo",
3144 "c.com/path/file.htm?q=x#foo", true, false },
3145 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
3146 "ftp://www.abc.com/path/file.htm?q=x#foo",
3147 "c.com/path/file.htm?q=x#foo", true, false },
3148 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
3149 "ftp://www.abc.com/path/file.htm?q=x#foo",
3150 "c.com/path/file.htm?q=x#foo", true, false },
3151 { "ab", "ftp://abc.com/path/file.htm?q=x#foo",
3152 "ftp://abc.com/path/file.htm?q=x#foo",
3153 "c.com/path/file.htm?q=x#foo", true, false },
3154 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo",
3155 "https://www.abc.com/path/file.htm?q=x#foo",
3156 "c.com/path/file.htm?q=x#foo",
3158 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo",
3159 "https://www.abc.com/path/file.htm?q=x#foo",
3160 "c.com/path/file.htm?q=x#foo", true, false },
3161 { "ab", "https://www.abc.com/path/file.htm?q=x#foo",
3162 "https://www.abc.com/path/file.htm?q=x#foo",
3163 "c.com/path/file.htm?q=x#foo", true, false },
3164 { "ab", "https://abc.com/path/file.htm?q=x#foo",
3165 "https://abc.com/path/file.htm?q=x#foo",
3166 "c.com/path/file.htm?q=x#foo", true, false },
3168 // Forced query input should inline and retain the "?" prefix.
3169 { "?http://www.ab", "http://www.abc.com",
3170 "?http://www.abc.com", "c.com", true, false },
3171 { "?www.ab", "http://www.abc.com",
3172 "?www.abc.com", "c.com", true, false },
3173 { "?ab", "http://www.abc.com",
3174 "?www.abc.com", "c.com", true, false },
3175 { "?abc.com", "http://www.abc.com",
3176 "?www.abc.com", "", true, true },
3179 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3180 // First test regular mode.
3181 QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
3182 AutocompleteMatch match(
3183 provider_->NavigationToMatch(SearchProvider::NavigationResult(
3184 *provider_.get(), GURL(cases[i].url), base::string16(), false, 0,
3185 false, ASCIIToUTF16(cases[i].input), std::string())));
3186 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
3187 match.inline_autocompletion);
3188 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit);
3189 EXPECT_EQ(cases[i].allowed_to_be_default_match_in_regular_mode,
3190 match.allowed_to_be_default_match);
3192 // Then test prevent-inline-autocomplete mode.
3193 QueryForInput(ASCIIToUTF16(cases[i].input), true, false);
3194 AutocompleteMatch match_prevent_inline(
3195 provider_->NavigationToMatch(SearchProvider::NavigationResult(
3196 *provider_.get(), GURL(cases[i].url), base::string16(), false, 0,
3197 false, ASCIIToUTF16(cases[i].input), std::string())));
3198 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
3199 match_prevent_inline.inline_autocompletion);
3200 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit),
3201 match_prevent_inline.fill_into_edit);
3202 EXPECT_EQ(cases[i].allowed_to_be_default_match_in_prevent_inline_mode,
3203 match_prevent_inline.allowed_to_be_default_match);
3207 // Verifies that "http://" is not trimmed for input that is a leading substring.
3208 TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
3209 const base::string16 input(ASCIIToUTF16("ht"));
3210 const base::string16 url(ASCIIToUTF16("http://a.com"));
3211 const SearchProvider::NavigationResult result(
3212 *provider_.get(), GURL(url), base::string16(), false, 0, false,
3213 input, std::string());
3215 // Check the offset and strings when inline autocompletion is allowed.
3216 QueryForInput(input, false, false);
3217 AutocompleteMatch match_inline(provider_->NavigationToMatch(result));
3218 EXPECT_EQ(url, match_inline.fill_into_edit);
3219 EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion);
3220 EXPECT_TRUE(match_inline.allowed_to_be_default_match);
3221 EXPECT_EQ(url, match_inline.contents);
3223 // Check the same strings when inline autocompletion is prevented.
3224 QueryForInput(input, true, false);
3225 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result));
3226 EXPECT_EQ(url, match_prevent.fill_into_edit);
3227 EXPECT_FALSE(match_prevent.allowed_to_be_default_match);
3228 EXPECT_EQ(url, match_prevent.contents);
3231 // Verifies that input "w" marks a more significant domain label than "www.".
3232 TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
3233 QueryForInput(ASCIIToUTF16("w"), false, false);
3234 AutocompleteMatch match(
3235 provider_->NavigationToMatch(SearchProvider::NavigationResult(
3236 *provider_.get(), GURL("http://www.wow.com"), base::string16(), false,
3237 0, false, ASCIIToUTF16("w"), std::string())));
3238 EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion);
3239 EXPECT_TRUE(match.allowed_to_be_default_match);
3240 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit);
3241 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents);
3243 // Ensure that the match for input "w" is marked on "wow" and not "www".
3244 ASSERT_EQ(3U, match.contents_class.size());
3245 EXPECT_EQ(0U, match.contents_class[0].offset);
3246 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
3247 match.contents_class[0].style);
3248 EXPECT_EQ(4U, match.contents_class[1].offset);
3249 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL |
3250 AutocompleteMatch::ACMatchClassification::MATCH,
3251 match.contents_class[1].style);
3252 EXPECT_EQ(5U, match.contents_class[2].offset);
3253 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
3254 match.contents_class[2].style);
3257 TEST_F(SearchProviderTest, RemoveStaleResultsTest) {
3258 // TODO(mpearson): Consider expanding this test to explicitly cover
3259 // testing staleness for keyword results.
3261 const std::string omnibox_input;
3262 const int verbatim_relevance;
3263 // These cached suggestions should already be sorted.
3264 // The particular number 5 as the length of the array is
3265 // unimportant; it's merely enough cached results to fully test
3266 // the functioning of RemoveAllStaleResults().
3268 const std::string suggestion;
3269 const bool is_navigation_result;
3270 const int relevance;
3271 // |expect_match| is true if this result should survive
3272 // RemoveAllStaleResults() filtering against |omnibox_input| below.
3273 const bool expect_match;
3276 // Simple case: multiple query suggestions and no navsuggestions.
3277 // All query suggestions score less than search-what-you-typed and
3278 // thus none should be filtered because none will appear first.
3280 { { "food", false, 1299, true },
3281 { "foobar", false, 1298, true },
3282 { "crazy", false, 1297, true },
3283 { "friend", false, 1296, true },
3284 { kNotApplicable, false, 0, false } } },
3286 // Similarly simple cases, but the query suggestion appears first.
3288 { { "food", false, 1299, true },
3289 { "foobar", false, 1298, true },
3290 { "crazy", false, 1297, true },
3291 { "friend", false, 1296, true },
3292 { kNotApplicable, false, 0, false } } },
3294 { { "food", false, 1299, false },
3295 { "foobar", false, 1298, false },
3296 { "crazy", false, 1297, true },
3297 { "friend", false, 1296, true },
3298 { kNotApplicable, false, 0, false } } },
3300 { { "food", false, 1299, false },
3301 { "foobar", false, 1298, false },
3302 { "crazy", false, 1297, false },
3303 { "friend", false, 1296, false },
3304 { kNotApplicable, false, 0, false } } },
3306 // The same sort of cases, just using a mix of queries and navsuggestions.
3308 { { "http://food.com/", true, 1299, true },
3309 { "foobar", false, 1298, true },
3310 { "http://crazy.com/", true, 1297, true },
3311 { "friend", false, 1296, true },
3312 { "http://friend.com/", true, 1295, true } } },
3314 { { "http://food.com/", true, 1299, true },
3315 { "foobar", false, 1298, true },
3316 { "http://crazy.com/", true, 1297, true },
3317 { "friend", false, 1296, true },
3318 { "http://friend.com/", true, 1295, true } } },
3320 { { "http://food.com/", true, 1299, false },
3321 { "foobar", false, 1298, false },
3322 { "http://crazy.com/", true, 1297, true },
3323 { "friend", false, 1296, true },
3324 { "http://friend.com/", true, 1295, true } } },
3326 { { "http://food.com/", true, 1299, false },
3327 { "foobar", false, 1298, false },
3328 { "http://crazy.com/", true, 1297, false },
3329 { "friend", false, 1296, false },
3330 { "http://friend.com/", true, 1295, false } } },
3332 // Run the three tests immediately above again, just with verbatim
3333 // suppressed. Note that in the last case, all results are filtered.
3334 // Because verbatim is also suppressed, SearchProvider will realize
3335 // in UpdateMatches() that it needs to restore verbatim to fulfill
3336 // its constraints. This restoration does not happen in
3337 // RemoveAllStaleResults() and hence is not tested here. This restoration
3338 // is tested in the DefaultFetcherSuggestRelevance test.
3340 { { "http://food.com/", true, 1299, true },
3341 { "foobar", false, 1298, true },
3342 { "http://crazy.com/", true, 1297, true },
3343 { "friend", false, 1296, true },
3344 { "http://friend.com/", true, 1295, true } } },
3346 { { "http://food.com/", true, 1299, false },
3347 { "foobar", false, 1298, false },
3348 { "http://crazy.com/", true, 1297, true },
3349 { "friend", false, 1296, true },
3350 { "http://friend.com/", true, 1295, true } } },
3352 { { "http://food.com/", true, 1299, false },
3353 { "foobar", false, 1298, false },
3354 { "http://crazy.com/", true, 1297, false },
3355 { "friend", false, 1296, false },
3356 { "http://friend.com/", true, 1295, false } } },
3358 // The same sort of tests again, just with verbatim with a score
3359 // that would place it in between other suggestions.
3361 { { "http://food.com/", true, 1299, true },
3362 { "foobar", false, 1288, true },
3363 { "http://crazy.com/", true, 1277, true },
3364 { "friend", false, 1266, true },
3365 { "http://friend.com/", true, 1255, true } } },
3367 { { "http://food.com/", true, 1299, false },
3368 { "foobar", false, 1288, true },
3369 { "http://crazy.com/", true, 1277, true },
3370 { "friend", false, 1266, true },
3371 { "http://friend.com/", true, 1255, true } } },
3373 { { "http://food.com/", true, 1299, false },
3374 { "foobar", false, 1288, false },
3375 { "http://crazy.com/", true, 1277, true },
3376 { "friend", false, 1266, true },
3377 { "http://friend.com/", true, 1255, true } } },
3379 { { "http://food.com/", true, 1299, false },
3380 { "foobar", false, 1288, false },
3381 { "http://crazy.com/", true, 1277, true },
3382 { "friend", false, 1266, true },
3383 { "http://friend.com/", true, 1255, true } } },
3386 std::map<std::string, std::string> params;
3387 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
3388 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled;
3389 ASSERT_TRUE(chrome_variations::AssociateVariationParams(
3390 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
3391 base::FieldTrialList::CreateFieldTrial(
3392 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
3394 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3395 // Initialize cached results for this test case.
3396 provider_->default_results_.verbatim_relevance =
3397 cases[i].verbatim_relevance;
3398 provider_->default_results_.navigation_results.clear();
3399 provider_->default_results_.suggest_results.clear();
3400 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
3401 const std::string& suggestion = cases[i].results[j].suggestion;
3402 if (suggestion == kNotApplicable)
3404 if (cases[i].results[j].is_navigation_result) {
3405 provider_->default_results_.navigation_results.push_back(
3406 SearchProvider::NavigationResult(
3407 *provider_.get(), GURL(suggestion), base::string16(), false,
3408 cases[i].results[j].relevance, false,
3409 ASCIIToUTF16(cases[i].omnibox_input), std::string()));
3411 provider_->default_results_.suggest_results.push_back(
3412 SearchProvider::SuggestResult(
3413 ASCIIToUTF16(suggestion), AutocompleteMatchType::SEARCH_SUGGEST,
3414 ASCIIToUTF16(suggestion), base::string16(), std::string(),
3415 std::string(), false, cases[i].results[j].relevance, false,
3416 false, ASCIIToUTF16(cases[i].omnibox_input)));
3420 provider_->input_ = AutocompleteInput(
3421 ASCIIToUTF16(cases[i].omnibox_input), base::string16::npos,
3422 base::string16(), GURL(), AutocompleteInput::INVALID_SPEC, false, false,
3423 true, AutocompleteInput::ALL_MATCHES);
3424 provider_->RemoveAllStaleResults();
3426 // Check cached results.
3427 SearchProvider::SuggestResults::const_iterator sug_it =
3428 provider_->default_results_.suggest_results.begin();
3429 const SearchProvider::SuggestResults::const_iterator sug_end =
3430 provider_->default_results_.suggest_results.end();
3431 SearchProvider::NavigationResults::const_iterator nav_it =
3432 provider_->default_results_.navigation_results.begin();
3433 const SearchProvider::NavigationResults::const_iterator nav_end =
3434 provider_->default_results_.navigation_results.end();
3435 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
3436 const std::string& suggestion = cases[i].results[j].suggestion;
3437 if (suggestion == kNotApplicable)
3439 if (!cases[i].results[j].expect_match)
3441 if (cases[i].results[j].is_navigation_result) {
3442 ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion;
3443 EXPECT_EQ(suggestion, nav_it->url().spec());
3446 ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion;
3447 EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion());
3451 EXPECT_EQ(sug_end, sug_it);
3452 EXPECT_EQ(nav_end, nav_it);
3456 #if !defined(OS_WIN)
3457 // Verify entity suggestion parsing.
3458 TEST_F(SearchProviderTest, ParseEntitySuggestion) {
3460 std::string contents;
3461 std::string description;
3462 std::string query_params;
3463 std::string fill_into_edit;
3464 AutocompleteMatchType::Type type;
3466 const Match kEmptyMatch = {
3467 kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable,
3468 AutocompleteMatchType::NUM_TYPES};
3471 const std::string input_text;
3472 const std::string response_json;
3473 const Match matches[5];
3475 // A query and an entity suggestion with different search terms.
3477 "[\"x\",[\"xy\", \"yy\"],[\"\",\"\"],[],"
3478 " {\"google:suggestdetail\":[{},"
3479 " {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
3480 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
3481 { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3482 { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
3483 { "xy", "A", "p=v", "yy",
3484 AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
3489 // A query and an entity suggestion with same search terms.
3491 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
3492 " {\"google:suggestdetail\":[{},"
3493 " {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
3494 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
3495 { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3496 { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
3497 { "xy", "A", "p=v", "xy",
3498 AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
3504 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3505 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
3507 // Set up a default fetcher with provided results.
3508 net::TestURLFetcher* fetcher =
3509 test_factory_.GetFetcherByID(
3510 SearchProvider::kDefaultProviderURLFetcherID);
3511 ASSERT_TRUE(fetcher);
3512 fetcher->set_response_code(200);
3513 fetcher->SetResponseString(cases[i].response_json);
3514 fetcher->delegate()->OnURLFetchComplete(fetcher);
3516 RunTillProviderDone();
3518 const ACMatches& matches = provider_->matches();
3519 ASSERT_FALSE(matches.empty());
3521 SCOPED_TRACE("for input with json = " + cases[i].response_json);
3523 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
3525 // Ensure that the returned matches equal the expectations.
3526 for (; j < matches.size(); ++j) {
3527 const Match& match = cases[i].matches[j];
3528 SCOPED_TRACE(" and match index: " + base::IntToString(j));
3529 EXPECT_EQ(match.contents,
3530 base::UTF16ToUTF8(matches[j].contents));
3531 EXPECT_EQ(match.description,
3532 base::UTF16ToUTF8(matches[j].description));
3533 EXPECT_EQ(match.query_params,
3534 matches[j].search_terms_args->suggest_query_params);
3535 EXPECT_EQ(match.fill_into_edit,
3536 base::UTF16ToUTF8(matches[j].fill_into_edit));
3537 EXPECT_EQ(match.type, matches[j].type);
3539 // Ensure that no expected matches are missing.
3540 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
3541 SCOPED_TRACE(" and match index: " + base::IntToString(j));
3542 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
3543 EXPECT_EQ(cases[i].matches[j].description, kNotApplicable);
3544 EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
3545 EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
3546 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
3550 #endif // !defined(OS_WIN)
3553 // A basic test that verifies the prefetch metadata parsing logic.
3554 TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
3556 std::string contents;
3557 bool allowed_to_be_prefetched;
3558 AutocompleteMatchType::Type type;
3561 const Match kEmptyMatch = { kNotApplicable,
3563 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3567 const std::string input_text;
3568 bool prefer_keyword_provider_results;
3569 const std::string default_provider_response_json;
3570 const std::string keyword_provider_response_json;
3571 const Match matches[5];
3573 // Default provider response does not have prefetch details. Ensure that the
3574 // suggestions are not marked as prefetch query.
3577 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
3579 { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3580 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
3581 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
3586 // Ensure that default provider suggest response prefetch details are
3587 // parsed and recorded in AutocompleteMatch.
3590 "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[],"
3591 "{\"google:clientdata\":{\"phi\": 0},"
3592 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"],"
3593 "\"google:suggestrelevance\":[999, 12, 1]}]",
3595 { { "ab", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3596 { "abc", true, AutocompleteMatchType::SEARCH_SUGGEST, false },
3597 { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false },
3598 { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false },
3602 // Default provider suggest response has prefetch details.
3603 // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for
3604 // the same query string. Ensure that the prefetch details from
3605 // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match.
3608 "[\"ab\",[\"ab\", \"http://ab.com\"],[],[],"
3609 "{\"google:clientdata\":{\"phi\": 0},"
3610 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
3611 "\"google:suggestrelevance\":[99, 98]}]",
3613 { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3614 {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false },
3620 // Default provider response has prefetch details. We prefer keyword
3621 // provider results. Ensure that prefetch bit for a suggestion from the
3622 // default search provider does not get copied onto a higher-scoring match
3623 // for the same query string from the keyword provider.
3626 "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0},"
3627 "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
3628 "\"google:suggestrelevance\":[9, 12]}]",
3629 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
3630 { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true},
3631 { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3632 { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
3633 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true },
3634 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true }
3639 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3640 QueryForInput(ASCIIToUTF16(cases[i].input_text), false,
3641 cases[i].prefer_keyword_provider_results);
3643 // Set up a default fetcher with provided results.
3644 net::TestURLFetcher* fetcher =
3645 test_factory_.GetFetcherByID(
3646 SearchProvider::kDefaultProviderURLFetcherID);
3647 ASSERT_TRUE(fetcher);
3648 fetcher->set_response_code(200);
3649 fetcher->SetResponseString(cases[i].default_provider_response_json);
3650 fetcher->delegate()->OnURLFetchComplete(fetcher);
3652 if (cases[i].prefer_keyword_provider_results) {
3653 // Set up a keyword fetcher with provided results.
3654 net::TestURLFetcher* keyword_fetcher =
3655 test_factory_.GetFetcherByID(
3656 SearchProvider::kKeywordProviderURLFetcherID);
3657 ASSERT_TRUE(keyword_fetcher);
3658 keyword_fetcher->set_response_code(200);
3659 keyword_fetcher->SetResponseString(
3660 cases[i].keyword_provider_response_json);
3661 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
3662 keyword_fetcher = NULL;
3665 RunTillProviderDone();
3667 const std::string description =
3668 "for input with json =" + cases[i].default_provider_response_json;
3669 const ACMatches& matches = provider_->matches();
3670 // The top match must inline and score as highly as calculated verbatim.
3671 ASSERT_FALSE(matches.empty());
3672 EXPECT_GE(matches[0].relevance, 1300);
3674 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
3675 // Ensure that the returned matches equal the expectations.
3676 for (size_t j = 0; j < matches.size(); ++j) {
3677 SCOPED_TRACE(description);
3678 EXPECT_EQ(cases[i].matches[j].contents,
3679 base::UTF16ToUTF8(matches[j].contents));
3680 EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
3681 SearchProvider::ShouldPrefetch(matches[j]));
3682 EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
3683 EXPECT_EQ(cases[i].matches[j].from_keyword,
3684 matches[j].keyword == ASCIIToUTF16("k"));
3689 TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_InvalidResponse) {
3692 std::string input_str("abc");
3693 QueryForInput(ASCIIToUTF16(input_str), false, false);
3695 // Set up a default fetcher with provided results.
3696 net::TestURLFetcher* fetcher =
3697 test_factory_.GetFetcherByID(
3698 SearchProvider::kDefaultProviderURLFetcherID);
3699 ASSERT_TRUE(fetcher);
3700 fetcher->set_response_code(200);
3701 fetcher->SetResponseString("this is a bad non-json response");
3702 fetcher->delegate()->OnURLFetchComplete(fetcher);
3704 RunTillProviderDone();
3706 const ACMatches& matches = provider_->matches();
3708 // Should have exactly one "search what you typed" match
3709 ASSERT_TRUE(matches.size() == 1);
3710 EXPECT_EQ(input_str, base::UTF16ToUTF8(matches[0].contents));
3711 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3715 // A basic test that verifies that the XSSI guarded JSON response is parsed
3717 TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_ValidResponses) {
3719 std::string contents;
3720 AutocompleteMatchType::Type type;
3722 const Match kEmptyMatch = {
3723 kNotApplicable, AutocompleteMatchType::NUM_TYPES
3727 const std::string input_text;
3728 const std::string default_provider_response_json;
3729 const Match matches[4];
3733 "[\"a\",[\"b\", \"c\"],[],[],"
3734 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3735 "\"google:suggestrelevance\":[1, 2]}]",
3736 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3737 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3738 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3742 // Standard XSSI guard - )]}'\n.
3744 ")]}'\n[\"a\",[\"b\", \"c\"],[],[],"
3745 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3746 "\"google:suggestrelevance\":[1, 2]}]",
3747 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3748 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3749 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3753 // Modified XSSI guard - contains "[".
3755 ")]}'\n[)\"[\"a\",[\"b\", \"c\"],[],[],"
3756 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3757 "\"google:suggestrelevance\":[1, 2]}]",
3758 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3759 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3760 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3766 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3768 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
3770 // Set up a default fetcher with provided results.
3771 net::TestURLFetcher* fetcher =
3772 test_factory_.GetFetcherByID(
3773 SearchProvider::kDefaultProviderURLFetcherID);
3774 ASSERT_TRUE(fetcher);
3775 fetcher->set_response_code(200);
3776 fetcher->SetResponseString(cases[i].default_provider_response_json);
3777 fetcher->delegate()->OnURLFetchComplete(fetcher);
3779 RunTillProviderDone();
3781 const ACMatches& matches = provider_->matches();
3782 // The top match must inline and score as highly as calculated verbatim.
3783 ASSERT_FALSE(matches.empty());
3784 EXPECT_GE(matches[0].relevance, 1300);
3786 SCOPED_TRACE("for case: " + base::IntToString(i));
3787 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
3789 // Ensure that the returned matches equal the expectations.
3790 for (; j < matches.size(); ++j) {
3791 SCOPED_TRACE("and match: " + base::IntToString(j));
3792 EXPECT_EQ(cases[i].matches[j].contents,
3793 base::UTF16ToUTF8(matches[j].contents));
3794 EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
3796 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
3797 SCOPED_TRACE("and match: " + base::IntToString(j));
3798 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
3799 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
3804 // Test that deletion url gets set on an AutocompleteMatch when available for a
3805 // personalized query.
3806 TEST_F(SearchProviderTest, ParseDeletionUrl) {
3808 std::string contents;
3809 std::string deletion_url;
3810 AutocompleteMatchType::Type type;
3813 const Match kEmptyMatch = {
3814 kNotApplicable, "", AutocompleteMatchType::NUM_TYPES
3817 const char url[] = "https://www.google.com/complete/deleteitems"
3818 "?delq=ab&client=chrome&deltok=xsrf123";
3821 const std::string input_text;
3822 const std::string response_json;
3823 const Match matches[4];
3825 // A deletion URL on a personalized query should be reflected in the
3826 // resulting AutocompleteMatch.
3828 "[\"a\",[\"ab\", \"ac\"],[],[],"
3829 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"],"
3830 "\"google:suggestrelevance\":[1, 2],"
3831 "\"google:suggestdetail\":[{\"du\":"
3832 "\"https://www.google.com/complete/deleteitems?delq=ab&client=chrome"
3833 "&deltok=xsrf123\"}, {}]}]",
3834 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3835 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
3836 { "ab", url, AutocompleteMatchType::SEARCH_SUGGEST },
3840 // Personalized queries without deletion URLs shouldn't cause errors.
3842 "[\"a\",[\"ab\", \"ac\"],[],[],"
3843 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"],"
3844 "\"google:suggestrelevance\":[1, 2],"
3845 "\"google:suggestdetail\":[{}, {}]}]",
3846 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3847 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
3848 { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
3854 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3855 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
3857 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3858 SearchProvider::kDefaultProviderURLFetcherID);
3859 ASSERT_TRUE(fetcher);
3860 fetcher->set_response_code(200);
3861 fetcher->SetResponseString(cases[i].response_json);
3862 fetcher->delegate()->OnURLFetchComplete(fetcher);
3864 RunTillProviderDone();
3866 const ACMatches& matches = provider_->matches();
3867 ASSERT_FALSE(matches.empty());
3869 SCOPED_TRACE("for input with json = " + cases[i].response_json);
3871 for (size_t j = 0; j < matches.size(); ++j) {
3872 const Match& match = cases[i].matches[j];
3873 SCOPED_TRACE(" and match index: " + base::IntToString(j));
3874 EXPECT_EQ(match.contents, base::UTF16ToUTF8(matches[j].contents));
3875 EXPECT_EQ(match.deletion_url, matches[j].GetAdditionalInfo(
3881 TEST_F(SearchProviderTest, ReflectsBookmarkBarState) {
3882 profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, false);
3883 base::string16 term = term1_.substr(0, term1_.length() - 1);
3884 QueryForInput(term, true, false);
3885 ASSERT_FALSE(provider_->matches().empty());
3886 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3887 provider_->matches()[0].type);
3888 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
3889 EXPECT_FALSE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
3891 profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, true);
3892 term = term1_.substr(0, term1_.length() - 1);
3893 QueryForInput(term, true, false);
3894 ASSERT_FALSE(provider_->matches().empty());
3895 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3896 provider_->matches()[0].type);
3897 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
3898 EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
3901 TEST_F(SearchProviderTest, CanSendURL) {
3902 TemplateURLData template_url_data;
3903 template_url_data.short_name = ASCIIToUTF16("t");
3904 template_url_data.SetURL("http://www.google.com/{searchTerms}");
3905 template_url_data.suggestions_url = "http://www.google.com/{searchTerms}";
3906 template_url_data.instant_url = "http://does/not/exist?strk=1";
3907 template_url_data.search_terms_replacement_key = "strk";
3908 template_url_data.id = SEARCH_ENGINE_GOOGLE;
3909 TemplateURL google_template_url(&profile_, template_url_data);
3911 // Create field trial.
3912 base::FieldTrial* field_trial = base::FieldTrialList::CreateFieldTrial(
3913 "AutocompleteDynamicTrial_2", "EnableZeroSuggest");
3914 field_trial->group();
3917 EXPECT_FALSE(SearchProvider::CanSendURL(
3918 GURL("http://www.google.com/search"),
3919 GURL("https://www.google.com/complete/search"), &google_template_url,
3920 AutocompleteInput::OTHER, &profile_));
3921 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(&profile_);
3922 signin->SetAuthenticatedUsername("test");
3924 // All conditions should be met.
3925 EXPECT_TRUE(SearchProvider::CanSendURL(
3926 GURL("http://www.google.com/search"),
3927 GURL("https://www.google.com/complete/search"), &google_template_url,
3928 AutocompleteInput::OTHER, &profile_));
3930 // Not in field trial.
3931 ResetFieldTrialList();
3932 EXPECT_FALSE(SearchProvider::CanSendURL(
3933 GURL("http://www.google.com/search"),
3934 GURL("https://www.google.com/complete/search"), &google_template_url,
3935 AutocompleteInput::OTHER, &profile_));
3936 field_trial = base::FieldTrialList::CreateFieldTrial(
3937 "AutocompleteDynamicTrial_2", "EnableZeroSuggest");
3938 field_trial->group();
3940 // Invalid page URL.
3941 EXPECT_FALSE(SearchProvider::CanSendURL(
3943 GURL("https://www.google.com/complete/search"), &google_template_url,
3944 AutocompleteInput::OTHER, &profile_));
3946 // Invalid page classification.
3947 EXPECT_FALSE(SearchProvider::CanSendURL(
3948 GURL("http://www.google.com/search"),
3949 GURL("https://www.google.com/complete/search"), &google_template_url,
3950 AutocompleteInput::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS,
3953 // Invalid page classification.
3954 EXPECT_FALSE(SearchProvider::CanSendURL(
3955 GURL("http://www.google.com/search"),
3956 GURL("https://www.google.com/complete/search"), &google_template_url,
3957 AutocompleteInput::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
3960 // HTTPS page URL on same domain as provider.
3961 EXPECT_TRUE(SearchProvider::CanSendURL(
3962 GURL("https://www.google.com/search"),
3963 GURL("https://www.google.com/complete/search"),
3964 &google_template_url, AutocompleteInput::OTHER, &profile_));
3966 // Non-HTTP[S] page URL on same domain as provider.
3967 EXPECT_FALSE(SearchProvider::CanSendURL(
3968 GURL("ftp://www.google.com/search"),
3969 GURL("https://www.google.com/complete/search"), &google_template_url,
3970 AutocompleteInput::OTHER, &profile_));
3972 // Non-HTTP page URL on different domain.
3973 EXPECT_FALSE(SearchProvider::CanSendURL(
3974 GURL("https://www.notgoogle.com/search"),
3975 GURL("https://www.google.com/complete/search"), &google_template_url,
3976 AutocompleteInput::OTHER, &profile_));
3978 // Non-HTTPS provider.
3979 EXPECT_FALSE(SearchProvider::CanSendURL(
3980 GURL("http://www.google.com/search"),
3981 GURL("http://www.google.com/complete/search"), &google_template_url,
3982 AutocompleteInput::OTHER, &profile_));
3984 // Suggest disabled.
3985 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, false);
3986 EXPECT_FALSE(SearchProvider::CanSendURL(
3987 GURL("http://www.google.com/search"),
3988 GURL("https://www.google.com/complete/search"), &google_template_url,
3989 AutocompleteInput::OTHER, &profile_));
3990 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true);
3993 EXPECT_FALSE(SearchProvider::CanSendURL(
3994 GURL("http://www.google.com/search"),
3995 GURL("https://www.google.com/complete/search"), &google_template_url,
3996 AutocompleteInput::OTHER, profile_.GetOffTheRecordProfile()));
3998 // Tab sync not enabled.
3999 profile_.GetPrefs()->SetBoolean(prefs::kSyncKeepEverythingSynced, false);
4000 profile_.GetPrefs()->SetBoolean(prefs::kSyncTabs, false);
4001 EXPECT_FALSE(SearchProvider::CanSendURL(
4002 GURL("http://www.google.com/search"),
4003 GURL("https://www.google.com/complete/search"), &google_template_url,
4004 AutocompleteInput::OTHER, &profile_));
4005 profile_.GetPrefs()->SetBoolean(prefs::kSyncTabs, true);
4007 // Tab sync is encrypted.
4008 ProfileSyncService* service =
4009 ProfileSyncServiceFactory::GetInstance()->GetForProfile(&profile_);
4010 syncer::ModelTypeSet encrypted_types = service->GetEncryptedDataTypes();
4011 encrypted_types.Put(syncer::SESSIONS);
4012 service->OnEncryptedTypesChanged(encrypted_types, false);
4013 EXPECT_FALSE(SearchProvider::CanSendURL(
4014 GURL("http://www.google.com/search"),
4015 GURL("https://www.google.com/complete/search"), &google_template_url,
4016 AutocompleteInput::OTHER, &profile_));
4017 encrypted_types.Remove(syncer::SESSIONS);
4018 service->OnEncryptedTypesChanged(encrypted_types, false);
4020 // Check that there were no side effects from previous tests.
4021 EXPECT_TRUE(SearchProvider::CanSendURL(
4022 GURL("http://www.google.com/search"),
4023 GURL("https://www.google.com/complete/search"), &google_template_url,
4024 AutocompleteInput::OTHER, &profile_));
4027 TEST_F(SearchProviderTest, TestDeleteMatch) {
4028 AutocompleteMatch match(provider_, 0, true,
4029 AutocompleteMatchType::SEARCH_SUGGEST);
4030 match.RecordAdditionalInfo(
4031 SearchProvider::kDeletionUrlKey,
4032 "https://www.google.com/complete/deleteitem?q=foo");
4034 // Test a successful deletion request.
4035 provider_->matches_.push_back(match);
4036 provider_->DeleteMatch(match);
4037 EXPECT_FALSE(provider_->deletion_handlers_.empty());
4038 EXPECT_TRUE(provider_->matches_.empty());
4039 // Set up a default fetcher with provided results.
4040 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
4041 SearchProvider::kDeletionURLFetcherID);
4042 ASSERT_TRUE(fetcher);
4043 fetcher->set_response_code(200);
4044 fetcher->delegate()->OnURLFetchComplete(fetcher);
4045 EXPECT_TRUE(provider_->deletion_handlers_.empty());
4046 EXPECT_TRUE(provider_->is_success());
4048 // Test a failing deletion request.
4049 provider_->matches_.push_back(match);
4050 provider_->DeleteMatch(match);
4051 EXPECT_FALSE(provider_->deletion_handlers_.empty());
4052 // Set up a default fetcher with provided results.
4053 fetcher = test_factory_.GetFetcherByID(
4054 SearchProvider::kDeletionURLFetcherID);
4055 ASSERT_TRUE(fetcher);
4056 fetcher->set_response_code(500);
4057 fetcher->delegate()->OnURLFetchComplete(fetcher);
4058 EXPECT_TRUE(provider_->deletion_handlers_.empty());
4059 EXPECT_FALSE(provider_->is_success());