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/search.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/common/chrome_switches.h"
34 #include "chrome/common/metrics/variations/variations_util.h"
35 #include "chrome/common/pref_names.h"
36 #include "chrome/test/base/testing_browser_process.h"
37 #include "chrome/test/base/testing_profile.h"
38 #include "components/variations/entropy_provider.h"
39 #include "content/public/test/test_browser_thread_bundle.h"
40 #include "net/url_request/test_url_fetcher_factory.h"
41 #include "net/url_request/url_request_status.h"
42 #include "testing/gtest/include/gtest/gtest.h"
44 // SearchProviderTest ---------------------------------------------------------
46 // The following environment is configured for these tests:
47 // . The TemplateURL default_t_url_ is set as the default provider.
48 // . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This
49 // TemplateURL has a valid suggest and search URL.
50 // . The URL created by using the search term term1_ with default_t_url_ is
52 // . The URL created by using the search term keyword_term_ with keyword_t_url_
53 // is added to history.
54 // . test_factory_ is set as the URLFetcherFactory.
55 class SearchProviderTest : public testing::Test,
56 public AutocompleteProviderListener {
59 ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES) {
62 AutocompleteMatch::Type result_type,
63 string16 fill_into_edit)
65 result_type(result_type),
66 fill_into_edit(fill_into_edit) {
70 const AutocompleteMatch::Type result_type;
71 const string16 fill_into_edit;
76 const size_t num_results;
77 const ResultInfo output[3];
81 : default_t_url_(NULL),
82 term1_(ASCIIToUTF16("term1")),
84 keyword_term_(ASCIIToUTF16("keyword")),
86 ResetFieldTrialList();
89 // See description above class for what this registers.
90 virtual void SetUp() OVERRIDE;
91 virtual void TearDown() OVERRIDE;
93 void RunTest(TestData* cases, int num_cases, bool prefer_keyword);
96 // Needed for AutocompleteFieldTrial::ActivateStaticTrials();
97 scoped_ptr<base::FieldTrialList> field_trial_list_;
99 // Default value used for testing.
100 static const std::string kNotApplicable;
102 // Adds a search for |term|, using the engine |t_url| to the history, and
103 // returns the URL for that search.
104 GURL AddSearchToHistory(TemplateURL* t_url, string16 term, int visit_count);
106 // Looks for a match in |provider_| with |contents| equal to |contents|.
107 // Sets |match| to it if found. Returns whether |match| was set.
108 bool FindMatchWithContents(const string16& contents,
109 AutocompleteMatch* match);
111 // Looks for a match in |provider_| with destination |url|. Sets |match| to
112 // it if found. Returns whether |match| was set.
113 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match);
115 // AutocompleteProviderListener:
116 // If we're waiting for the provider to finish, this exits the message loop.
117 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE;
119 // Runs a nested message loop until provider_ is done. The message loop is
120 // exited by way of OnProviderUpdate.
121 void RunTillProviderDone();
123 // Invokes Start on provider_, then runs all pending tasks.
124 void QueryForInput(const string16& text,
125 bool prevent_inline_autocomplete,
126 bool prefer_keyword);
128 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is
129 // non-NULL, sets it to the "what you typed" entry for |text|.
130 void QueryForInputAndSetWYTMatch(const string16& text,
131 AutocompleteMatch* wyt_match);
133 // Notifies the URLFetcher for the suggest query corresponding to the default
134 // search provider that it's done.
135 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE.
136 void FinishDefaultSuggestQuery();
138 void ResetFieldTrialList();
140 void ClearAllResults();
142 // See description above class for details of these fields.
143 TemplateURL* default_t_url_;
144 const string16 term1_;
146 TemplateURL* keyword_t_url_;
147 const string16 keyword_term_;
150 content::TestBrowserThreadBundle thread_bundle_;
152 // URLFetcherFactory implementation registered.
153 net::TestURLFetcherFactory test_factory_;
156 TestingProfile profile_;
159 scoped_refptr<SearchProvider> provider_;
161 // If non-NULL, OnProviderUpdate quits the current |run_loop_|.
162 base::RunLoop* run_loop_;
164 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
168 const std::string SearchProviderTest::kNotApplicable = "Not Applicable";
170 void SearchProviderTest::SetUp() {
171 // Make sure that fetchers are automatically ungregistered upon destruction.
172 test_factory_.set_remove_fetcher_on_delete(true);
174 // We need both the history service and template url model loaded.
175 ASSERT_TRUE(profile_.CreateHistoryService(true, false));
176 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
177 &profile_, &TemplateURLServiceFactory::BuildInstanceFor);
179 TemplateURLService* turl_model =
180 TemplateURLServiceFactory::GetForProfile(&profile_);
184 // Reset the default TemplateURL.
185 TemplateURLData data;
186 data.short_name = ASCIIToUTF16("t");
187 data.SetURL("http://defaultturl/{searchTerms}");
188 data.suggestions_url = "http://defaultturl2/{searchTerms}";
189 data.instant_url = "http://does/not/exist?strk=1";
190 data.search_terms_replacement_key = "strk";
191 default_t_url_ = new TemplateURL(&profile_, data);
192 turl_model->Add(default_t_url_);
193 turl_model->SetDefaultSearchProvider(default_t_url_);
194 TemplateURLID default_provider_id = default_t_url_->id();
195 ASSERT_NE(0, default_provider_id);
197 // Add url1, with search term term1_.
198 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1);
200 // Create another TemplateURL.
201 data.short_name = ASCIIToUTF16("k");
202 data.SetKeyword(ASCIIToUTF16("k"));
203 data.SetURL("http://keyword/{searchTerms}");
204 data.suggestions_url = "http://suggest_keyword/{searchTerms}";
205 keyword_t_url_ = new TemplateURL(&profile_, data);
206 turl_model->Add(keyword_t_url_);
207 ASSERT_NE(0, keyword_t_url_->id());
209 // Add a page and search term for keyword_t_url_.
210 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1);
212 // Keywords are updated by the InMemoryHistoryBackend only after the message
213 // has been processed on the history thread. Block until history processes all
214 // requests to ensure the InMemoryDatabase is the state we expect it.
215 profile_.BlockUntilHistoryProcessesPendingRequests();
217 provider_ = new SearchProvider(this, &profile_);
218 provider_->kMinimumTimeBetweenSuggestQueriesMs = 0;
221 void SearchProviderTest::TearDown() {
222 base::RunLoop().RunUntilIdle();
224 // Shutdown the provider before the profile.
228 void SearchProviderTest::RunTest(TestData* cases,
230 bool prefer_keyword) {
232 for (int i = 0; i < num_cases; ++i) {
233 AutocompleteInput input(cases[i].input, string16::npos, string16(), GURL(),
234 AutocompleteInput::INVALID_SPEC, false,
235 prefer_keyword, true,
236 AutocompleteInput::ALL_MATCHES);
237 provider_->Start(input, false);
238 matches = provider_->matches();
239 string16 diagnostic_details = ASCIIToUTF16("Input was: ") + cases[i].input +
240 ASCIIToUTF16("; prefer_keyword was: ") +
241 (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false"));
242 EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details;
243 if (matches.size() == cases[i].num_results) {
244 for (size_t j = 0; j < cases[i].num_results; ++j) {
245 EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) <<
247 EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) <<
249 EXPECT_EQ(cases[i].output[j].fill_into_edit,
250 matches[j].fill_into_edit) <<
252 // All callers that use this helper function at the moment produce
253 // matches that are always allowed to be the default match.
254 EXPECT_TRUE(matches[j].allowed_to_be_default_match);
260 void SearchProviderTest::OnProviderUpdate(bool updated_matches) {
261 if (run_loop_ && provider_->done()) {
267 void SearchProviderTest::RunTillProviderDone() {
268 if (provider_->done())
271 base::RunLoop run_loop;
272 run_loop_ = &run_loop;
276 void SearchProviderTest::QueryForInput(const string16& text,
277 bool prevent_inline_autocomplete,
278 bool prefer_keyword) {
280 AutocompleteInput input(text, string16::npos, string16(), GURL(),
281 AutocompleteInput::INVALID_SPEC,
282 prevent_inline_autocomplete, prefer_keyword, true,
283 AutocompleteInput::ALL_MATCHES);
284 provider_->Start(input, false);
286 // RunUntilIdle so that the task scheduled by SearchProvider to create the
288 base::RunLoop().RunUntilIdle();
291 void SearchProviderTest::QueryForInputAndSetWYTMatch(
292 const string16& text,
293 AutocompleteMatch* wyt_match) {
294 QueryForInput(text, false, false);
295 profile_.BlockUntilHistoryProcessesPendingRequests();
296 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
299 ASSERT_GE(provider_->matches().size(), 1u);
300 EXPECT_TRUE(FindMatchWithDestination(GURL(
301 default_t_url_->url_ref().ReplaceSearchTerms(
302 TemplateURLRef::SearchTermsArgs(text))),
306 GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url,
309 HistoryService* history =
310 HistoryServiceFactory::GetForProfile(&profile_,
311 Profile::EXPLICIT_ACCESS);
312 GURL search(t_url->url_ref().ReplaceSearchTerms(
313 TemplateURLRef::SearchTermsArgs(term)));
314 static base::Time last_added_time;
315 last_added_time = std::max(base::Time::Now(),
316 last_added_time + base::TimeDelta::FromMicroseconds(1));
317 history->AddPageWithDetails(search, string16(), visit_count, visit_count,
318 last_added_time, false, history::SOURCE_BROWSED);
319 history->SetKeywordSearchTermsForURL(search, t_url->id(), term);
323 bool SearchProviderTest::FindMatchWithContents(const string16& contents,
324 AutocompleteMatch* match) {
325 for (ACMatches::const_iterator i = provider_->matches().begin();
326 i != provider_->matches().end(); ++i) {
327 if (i->contents == contents) {
335 bool SearchProviderTest::FindMatchWithDestination(const GURL& url,
336 AutocompleteMatch* match) {
337 for (ACMatches::const_iterator i = provider_->matches().begin();
338 i != provider_->matches().end(); ++i) {
339 if (i->destination_url == url) {
347 void SearchProviderTest::FinishDefaultSuggestQuery() {
348 net::TestURLFetcher* default_fetcher =
349 test_factory_.GetFetcherByID(
350 SearchProvider::kDefaultProviderURLFetcherID);
351 ASSERT_TRUE(default_fetcher);
353 // Tell the SearchProvider the default suggest query is done.
354 default_fetcher->set_response_code(200);
355 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
358 void SearchProviderTest::ResetFieldTrialList() {
359 // Destroy the existing FieldTrialList before creating a new one to avoid
361 field_trial_list_.reset();
362 field_trial_list_.reset(new base::FieldTrialList(
363 new metrics::SHA1EntropyProvider("foo")));
364 chrome_variations::testing::ClearAllVariationParams();
365 base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
366 "AutocompleteDynamicTrial_0", "DefaultGroup");
370 void SearchProviderTest::ClearAllResults() {
371 provider_->ClearAllResults();
374 // Actual Tests ---------------------------------------------------------------
376 // Make sure we query history for the default provider and a URLFetcher is
377 // created for the default provider suggest results.
378 TEST_F(SearchProviderTest, QueryDefaultProvider) {
379 string16 term = term1_.substr(0, term1_.length() - 1);
380 QueryForInput(term, false, false);
382 // Make sure the default providers suggest service was queried.
383 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
384 SearchProvider::kDefaultProviderURLFetcherID);
385 ASSERT_TRUE(fetcher);
387 // And the URL matches what we expected.
388 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
389 TemplateURLRef::SearchTermsArgs(term)));
390 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url);
392 // Tell the SearchProvider the suggest query is done.
393 fetcher->set_response_code(200);
394 fetcher->delegate()->OnURLFetchComplete(fetcher);
397 // Run till the history results complete.
398 RunTillProviderDone();
400 // The SearchProvider is done. Make sure it has a result for the history
402 AutocompleteMatch term1_match;
403 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match));
404 // Term1 should not have a description, it's set later.
405 EXPECT_TRUE(term1_match.description.empty());
407 AutocompleteMatch wyt_match;
408 EXPECT_TRUE(FindMatchWithDestination(
409 GURL(default_t_url_->url_ref().ReplaceSearchTerms(
410 TemplateURLRef::SearchTermsArgs(term))), &wyt_match));
411 EXPECT_TRUE(wyt_match.description.empty());
413 // The match for term1 should be more relevant than the what you typed match.
414 EXPECT_GT(term1_match.relevance, wyt_match.relevance);
415 // This longer match should be inlineable.
416 EXPECT_TRUE(term1_match.allowed_to_be_default_match);
417 // The what you typed match should be too, of course.
418 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
421 TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
422 string16 term = term1_.substr(0, term1_.length() - 1);
423 QueryForInput(term, true, false);
425 ASSERT_FALSE(provider_->matches().empty());
426 ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
427 provider_->matches()[0].type);
428 EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match);
431 // Issues a query that matches the registered keyword and makes sure history
432 // is queried as well as URLFetchers getting created.
433 TEST_F(SearchProviderTest, QueryKeywordProvider) {
434 string16 term = keyword_term_.substr(0, keyword_term_.length() - 1);
435 QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term,
439 // Make sure the default providers suggest service was queried.
440 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID(
441 SearchProvider::kDefaultProviderURLFetcherID);
442 ASSERT_TRUE(default_fetcher);
444 // Tell the SearchProvider the default suggest query is done.
445 default_fetcher->set_response_code(200);
446 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
447 default_fetcher = NULL;
449 // Make sure the keyword providers suggest service was queried.
450 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID(
451 SearchProvider::kKeywordProviderURLFetcherID);
452 ASSERT_TRUE(keyword_fetcher);
454 // And the URL matches what we expected.
455 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms(
456 TemplateURLRef::SearchTermsArgs(term)));
457 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url);
459 // Tell the SearchProvider the keyword suggest query is done.
460 keyword_fetcher->set_response_code(200);
461 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
462 keyword_fetcher = NULL;
464 // Run till the history results complete.
465 RunTillProviderDone();
467 // The SearchProvider is done. Make sure it has a result for the history
469 AutocompleteMatch match;
470 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match));
472 // The match should have an associated keyword.
473 EXPECT_FALSE(match.keyword.empty());
475 // The fill into edit should contain the keyword.
476 EXPECT_EQ(keyword_t_url_->keyword() + char16(' ') + keyword_term_,
477 match.fill_into_edit);
480 TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) {
481 // None of the following input strings should be sent to the suggest server,
482 // because they may contain private data.
483 const char* inputs[] = {
485 "http://username:password",
486 "https://username:password",
487 "username:password@hostname",
488 "http://username:password@hostname/",
491 "unknownscheme:anything",
492 "http://hostname/?query=q",
493 "http://hostname/path#ref",
494 "http://hostname/path #ref",
495 "https://hostname/path",
498 for (size_t i = 0; i < arraysize(inputs); ++i) {
499 QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
500 // Make sure the default provider's suggest service was not queried.
501 ASSERT_TRUE(test_factory_.GetFetcherByID(
502 SearchProvider::kDefaultProviderURLFetcherID) == NULL);
503 // Run till the history results complete.
504 RunTillProviderDone();
508 TEST_F(SearchProviderTest, SendNonPrivateDataToSuggest) {
509 // All of the following input strings should be sent to the suggest server,
510 // because they should not get caught by the private data checks.
511 const char* inputs[] = {
515 "http://hostname/path",
516 "http://hostname #ref",
517 "www.hostname.com #ref",
520 "foo https://hostname/path"
523 profile_.BlockUntilHistoryProcessesPendingRequests();
524 for (size_t i = 0; i < arraysize(inputs); ++i) {
525 QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
526 // Make sure the default provider's suggest service was queried.
527 ASSERT_TRUE(test_factory_.GetFetcherByID(
528 SearchProvider::kDefaultProviderURLFetcherID) != NULL);
532 TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
533 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
534 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor);
535 GURL url = AddSearchToHistory(default_t_url_,
536 ASCIIToUTF16("docs.google.com"), 1);
538 // Add the term as a url.
539 HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)->
540 AddPageWithDetails(GURL("http://docs.google.com"), string16(), 1, 1,
541 base::Time::Now(), false, history::SOURCE_BROWSED);
542 profile_.BlockUntilHistoryProcessesPendingRequests();
544 AutocompleteMatch wyt_match;
545 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"),
548 // There should be two matches, one for what you typed, the other for
549 // 'docs.google.com'. The search term should have a lower priority than the
550 // what you typed match.
551 ASSERT_EQ(2u, provider_->matches().size());
552 AutocompleteMatch term_match;
553 EXPECT_TRUE(FindMatchWithDestination(url, &term_match));
554 EXPECT_GT(wyt_match.relevance, term_match.relevance);
555 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
556 EXPECT_TRUE(term_match.allowed_to_be_default_match);
559 // A multiword search with one visit should not autocomplete until multiple
561 TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) {
562 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"),
564 profile_.BlockUntilHistoryProcessesPendingRequests();
566 AutocompleteMatch wyt_match;
567 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"),
569 ASSERT_EQ(2u, provider_->matches().size());
570 AutocompleteMatch term_match;
571 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
572 EXPECT_GT(wyt_match.relevance, term_match.relevance);
573 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
574 EXPECT_TRUE(term_match.allowed_to_be_default_match);
576 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"),
578 ASSERT_EQ(2u, provider_->matches().size());
579 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
580 EXPECT_GT(term_match.relevance, wyt_match.relevance);
581 EXPECT_TRUE(term_match.allowed_to_be_default_match);
582 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
585 // A multiword search with more than one visit should autocomplete immediately.
586 TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) {
587 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
589 profile_.BlockUntilHistoryProcessesPendingRequests();
591 AutocompleteMatch wyt_match;
592 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"),
594 ASSERT_EQ(2u, provider_->matches().size());
595 AutocompleteMatch term_match;
596 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
597 EXPECT_GT(term_match.relevance, wyt_match.relevance);
598 EXPECT_TRUE(term_match.allowed_to_be_default_match);
599 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
602 // Autocompletion should work at a word boundary after a space.
603 TEST_F(SearchProviderTest, AutocompleteAfterSpace) {
604 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
606 profile_.BlockUntilHistoryProcessesPendingRequests();
608 AutocompleteMatch wyt_match;
609 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "),
611 ASSERT_EQ(2u, provider_->matches().size());
612 AutocompleteMatch term_match;
613 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
614 EXPECT_GT(term_match.relevance, wyt_match.relevance);
615 EXPECT_TRUE(term_match.allowed_to_be_default_match);
616 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
619 // Newer multiword searches should score more highly than older ones.
620 TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) {
621 GURL term_url_a(AddSearchToHistory(default_t_url_,
622 ASCIIToUTF16("three searches aaa"), 1));
623 GURL term_url_b(AddSearchToHistory(default_t_url_,
624 ASCIIToUTF16("three searches bbb"), 1));
625 profile_.BlockUntilHistoryProcessesPendingRequests();
627 AutocompleteMatch wyt_match;
628 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"),
630 ASSERT_EQ(3u, provider_->matches().size());
631 AutocompleteMatch term_match_a;
632 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
633 AutocompleteMatch term_match_b;
634 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
635 EXPECT_GT(term_match_b.relevance, term_match_a.relevance);
636 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
637 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
638 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
639 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
642 // An autocompleted multiword search should not be replaced by a different
643 // autocompletion while the user is still typing a valid prefix.
644 TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) {
645 GURL term_url_a(AddSearchToHistory(default_t_url_,
646 ASCIIToUTF16("four searches aaa"), 2));
647 GURL term_url_b(AddSearchToHistory(default_t_url_,
648 ASCIIToUTF16("four searches bbb"), 1));
649 profile_.BlockUntilHistoryProcessesPendingRequests();
651 AutocompleteMatch wyt_match;
652 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"),
654 ASSERT_EQ(3u, provider_->matches().size());
655 AutocompleteMatch term_match_a;
656 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
657 AutocompleteMatch term_match_b;
658 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
659 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
660 EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
661 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
662 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
663 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
665 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"),
667 ASSERT_EQ(3u, provider_->matches().size());
668 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
669 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
670 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
671 EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
672 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
673 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
674 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
677 // Non-completable multiword searches should not crowd out single-word searches.
678 TEST_F(SearchProviderTest, DontCrowdOutSingleWords) {
679 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1));
680 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1);
681 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1);
682 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1);
683 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1);
684 profile_.BlockUntilHistoryProcessesPendingRequests();
686 AutocompleteMatch wyt_match;
687 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"),
689 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size());
690 AutocompleteMatch term_match;
691 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
692 EXPECT_GT(term_match.relevance, wyt_match.relevance);
693 EXPECT_TRUE(term_match.allowed_to_be_default_match);
694 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
697 // Inline autocomplete matches regardless of case differences from the input.
698 TEST_F(SearchProviderTest, InlineMixedCaseMatches) {
699 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1));
700 profile_.BlockUntilHistoryProcessesPendingRequests();
702 AutocompleteMatch wyt_match;
703 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"),
705 ASSERT_EQ(2u, provider_->matches().size());
706 AutocompleteMatch term_match;
707 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
708 EXPECT_GT(term_match.relevance, wyt_match.relevance);
709 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit);
710 EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion);
711 EXPECT_TRUE(term_match.allowed_to_be_default_match);
714 // Verifies AutocompleteControllers return results (including keyword
715 // results) in the right order and set descriptions for them correctly.
716 TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) {
717 // Add an entry that corresponds to a keyword search with 'term2'.
718 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1);
719 profile_.BlockUntilHistoryProcessesPendingRequests();
721 AutocompleteController controller(&profile_, NULL,
722 AutocompleteProvider::TYPE_SEARCH);
723 controller.Start(AutocompleteInput(
724 ASCIIToUTF16("k t"), string16::npos, string16(), GURL(),
725 AutocompleteInput::INVALID_SPEC, false, false, true,
726 AutocompleteInput::ALL_MATCHES));
727 const AutocompleteResult& result = controller.result();
729 // There should be three matches, one for the keyword history, one for
730 // keyword provider's what-you-typed, and one for the default provider's
731 // what you typed, in that order.
732 ASSERT_EQ(3u, result.size());
733 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type);
734 EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE,
735 result.match_at(1).type);
736 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
737 result.match_at(2).type);
738 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance);
739 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance);
740 EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match);
741 EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match);
742 EXPECT_TRUE(result.match_at(2).allowed_to_be_default_match);
744 // The two keyword results should come with the keyword we expect.
745 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword);
746 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword);
747 // The default provider has a different keyword. (We don't explicitly
748 // set it during this test, so all we do is assert that it's different.)
749 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword);
751 // The top result will always have a description. The third result,
752 // coming from a different provider than the first two, should also.
753 // Whether the second result has one doesn't matter much. (If it was
754 // missing, people would infer that it's the same search provider as
755 // the one above it.)
756 EXPECT_FALSE(result.match_at(0).description.empty());
757 EXPECT_FALSE(result.match_at(2).description.empty());
758 EXPECT_NE(result.match_at(0).description, result.match_at(2).description);
761 TEST_F(SearchProviderTest, KeywordVerbatim) {
763 // Test a simple keyword input.
764 { ASCIIToUTF16("k foo"), 2,
765 { ResultInfo(GURL("http://keyword/foo"),
766 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
767 ASCIIToUTF16("k foo")),
768 ResultInfo(GURL("http://defaultturl/k%20foo"),
769 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
770 ASCIIToUTF16("k foo") ) } },
772 // Make sure extra whitespace after the keyword doesn't change the
773 // keyword verbatim query.
774 { ASCIIToUTF16("k foo"), 2,
775 { ResultInfo(GURL("http://keyword/foo"),
776 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
777 ASCIIToUTF16("k foo")),
778 ResultInfo(GURL("http://defaultturl/k%20%20%20foo"),
779 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
780 ASCIIToUTF16("k foo")) } },
781 // Leading whitespace should be stripped before SearchProvider gets the
782 // input; hence there are no tests here about how it handles those inputs.
784 // But whitespace elsewhere in the query string should matter to both
786 { ASCIIToUTF16("k foo bar"), 2,
787 { ResultInfo(GURL("http://keyword/foo%20%20bar"),
788 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
789 ASCIIToUTF16("k foo bar")),
790 ResultInfo(GURL("http://defaultturl/k%20%20foo%20%20bar"),
791 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
792 ASCIIToUTF16("k foo bar")) } },
793 // Note in the above test case we don't test trailing whitespace because
794 // SearchProvider still doesn't handle this well. See related bugs:
795 // 102690, 99239, 164635.
797 // Keywords can be prefixed by certain things that should get ignored
798 // when constructing the keyword match.
799 { ASCIIToUTF16("www.k foo"), 2,
800 { ResultInfo(GURL("http://keyword/foo"),
801 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
802 ASCIIToUTF16("k foo")),
803 ResultInfo(GURL("http://defaultturl/www.k%20foo"),
804 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
805 ASCIIToUTF16("www.k foo")) } },
806 { ASCIIToUTF16("http://k foo"), 2,
807 { ResultInfo(GURL("http://keyword/foo"),
808 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
809 ASCIIToUTF16("k foo")),
810 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"),
811 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
812 ASCIIToUTF16("http://k foo")) } },
813 { ASCIIToUTF16("http://www.k foo"), 2,
814 { ResultInfo(GURL("http://keyword/foo"),
815 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
816 ASCIIToUTF16("k foo")),
817 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"),
818 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
819 ASCIIToUTF16("http://www.k foo")) } },
821 // A keyword with no remaining input shouldn't get a keyword
823 { ASCIIToUTF16("k"), 1,
824 { ResultInfo(GURL("http://defaultturl/k"),
825 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
826 ASCIIToUTF16("k")) } },
827 { ASCIIToUTF16("k "), 1,
828 { ResultInfo(GURL("http://defaultturl/k%20"),
829 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
830 ASCIIToUTF16("k ")) } }
832 // The fact that verbatim queries to keyword are handled by KeywordProvider
833 // not SearchProvider is tested in
834 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc.
837 // Test not in keyword mode.
838 RunTest(cases, arraysize(cases), false);
840 // Test in keyword mode. (Both modes should give the same result.)
841 RunTest(cases, arraysize(cases), true);
844 // Ensures command-line flags are reflected in the URLs the search provider
846 TEST_F(SearchProviderTest, CommandLineOverrides) {
847 TemplateURLService* turl_model =
848 TemplateURLServiceFactory::GetForProfile(&profile_);
850 TemplateURLData data;
851 data.short_name = ASCIIToUTF16("default");
852 data.SetKeyword(data.short_name);
853 data.SetURL("{google:baseURL}{searchTerms}");
854 default_t_url_ = new TemplateURL(&profile_, data);
855 turl_model->Add(default_t_url_);
856 turl_model->SetDefaultSearchProvider(default_t_url_);
858 CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL,
859 "http://www.bar.com/");
860 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
861 switches::kExtraSearchQueryParams, "a=b");
864 { ASCIIToUTF16("k a"), 2,
865 { ResultInfo(GURL("http://keyword/a"),
866 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
867 ASCIIToUTF16("k a")),
868 ResultInfo(GURL("http://www.bar.com/k%20a?a=b"),
869 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
870 ASCIIToUTF16("k a")) } },
873 RunTest(cases, arraysize(cases), false);
876 // Verifies Navsuggest results don't set a TemplateURL, which Instant relies on.
877 // Also verifies that just the *first* navigational result is listed as a match
878 // if suggested relevance scores were not sent.
879 TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) {
880 QueryForInput(ASCIIToUTF16("a.c"), false, false);
882 // Make sure the default providers suggest service was queried.
883 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
884 SearchProvider::kDefaultProviderURLFetcherID);
885 ASSERT_TRUE(fetcher);
887 // Tell the SearchProvider the suggest query is done.
888 fetcher->set_response_code(200);
889 fetcher->SetResponseString(
890 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[],"
891 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]");
892 fetcher->delegate()->OnURLFetchComplete(fetcher);
895 // Run till the history results complete.
896 RunTillProviderDone();
898 // Make sure the only match is 'a.com' and it doesn't have a template_url.
899 AutocompleteMatch nav_match;
900 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match));
901 EXPECT_TRUE(nav_match.keyword.empty());
902 EXPECT_TRUE(nav_match.allowed_to_be_default_match);
903 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match));
906 // Verifies that the most relevant suggest results are added properly.
907 TEST_F(SearchProviderTest, SuggestRelevance) {
908 QueryForInput(ASCIIToUTF16("a"), false, false);
910 // Make sure the default provider's suggest service was queried.
911 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
912 SearchProvider::kDefaultProviderURLFetcherID);
913 ASSERT_TRUE(fetcher);
915 // Tell the SearchProvider the suggest query is done.
916 fetcher->set_response_code(200);
917 fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]");
918 fetcher->delegate()->OnURLFetchComplete(fetcher);
921 // Run till the history results complete.
922 RunTillProviderDone();
924 // Check the expected verbatim and (first 3) suggestions' relative relevances.
925 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4;
926 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
927 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1));
928 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2));
929 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3));
930 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4));
931 EXPECT_GT(verbatim.relevance, match_a1.relevance);
932 EXPECT_GT(match_a1.relevance, match_a2.relevance);
933 EXPECT_GT(match_a2.relevance, match_a3.relevance);
934 EXPECT_TRUE(verbatim.allowed_to_be_default_match);
935 EXPECT_TRUE(match_a1.allowed_to_be_default_match);
936 EXPECT_TRUE(match_a2.allowed_to_be_default_match);
937 EXPECT_TRUE(match_a3.allowed_to_be_default_match);
940 // Verifies that suggest results with relevance scores are added
941 // properly when using the default fetcher. When adding a new test
942 // case to this test, please consider adding it to the tests in
943 // KeywordFetcherSuggestRelevance below.
944 TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
945 struct DefaultFetcherMatch {
946 std::string contents;
947 bool allowed_to_be_default_match;
949 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
951 const std::string json;
952 const DefaultFetcherMatch matches[4];
953 const std::string inline_autocompletion;
955 // Ensure that suggestrelevance scores reorder matches.
956 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
957 { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch },
959 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
960 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
961 "\"google:suggestrelevance\":[1, 2]}]",
962 { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch },
965 // Without suggested relevance scores, we should only allow one
966 // navsuggest result to be be displayed.
967 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
968 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
969 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
972 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
973 // Negative values will have no effect; the calculated value will be used.
974 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
975 "\"google:suggestrelevance\":[9998]}]",
976 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch },
978 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
979 "\"google:suggestrelevance\":[9999]}]",
980 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
982 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
983 "\"google:suggestrelevance\":[9999]}]",
984 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
986 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
987 "\"google:suggestrelevance\":[9999]}]",
988 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
990 { "[\"a\",[\"http://a.com\"],[],[],"
991 "{\"google:suggesttype\":[\"NAVIGATION\"],"
992 "\"google:verbatimrelevance\":9999,"
993 "\"google:suggestrelevance\":[9998]}]",
994 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
996 { "[\"a\",[\"http://a.com\"],[],[],"
997 "{\"google:suggesttype\":[\"NAVIGATION\"],"
998 "\"google:verbatimrelevance\":9998,"
999 "\"google:suggestrelevance\":[9999]}]",
1000 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1002 { "[\"a\",[\"http://a.com\"],[],[],"
1003 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1004 "\"google:verbatimrelevance\":0,"
1005 "\"google:suggestrelevance\":[9999]}]",
1006 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1008 { "[\"a\",[\"http://a.com\"],[],[],"
1009 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1010 "\"google:verbatimrelevance\":-1,"
1011 "\"google:suggestrelevance\":[9999]}]",
1012 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1015 // Ensure that both types of relevance scores reorder matches together.
1016 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1017 "\"google:verbatimrelevance\":9998}]",
1018 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch },
1021 // Ensure that only inlinable matches may be ranked as the highest result.
1022 // Ignore all suggested relevance scores if this constraint is violated.
1023 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1024 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
1026 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1027 "\"google:verbatimrelevance\":0}]",
1028 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
1030 { "[\"a\",[\"http://b.com\"],[],[],"
1031 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1032 "\"google:suggestrelevance\":[9999]}]",
1033 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1035 { "[\"a\",[\"http://b.com\"],[],[],"
1036 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1037 "\"google:suggestrelevance\":[9999],"
1038 "\"google:verbatimrelevance\":0}]",
1039 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1041 { "[\"a\",[\"https://a/\"],[],[],"
1042 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1043 "\"google:suggestrelevance\":[9999]}]",
1044 { { "https://a", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1047 // Ensure that the top result is ranked as highly as calculated verbatim.
1048 // Ignore the suggested verbatim relevance if this constraint is violated.
1049 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1050 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1052 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1053 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1055 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1056 "\"google:verbatimrelevance\":0}]",
1057 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1059 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1060 "\"google:verbatimrelevance\":0}]",
1061 { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch },
1063 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1064 "\"google:verbatimrelevance\":2}]",
1065 { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch },
1067 { "[\"a\",[\"http://a.com\"],[],[],"
1068 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1069 "\"google:suggestrelevance\":[1],"
1070 "\"google:verbatimrelevance\":0}]",
1071 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
1073 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1074 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1075 "\"google:suggestrelevance\":[1, 2],"
1076 "\"google:verbatimrelevance\":0}]",
1077 { { "a", true }, { "a2.com", true }, { "a1.com", true }, kEmptyMatch },
1080 // Ensure that all suggestions are considered, regardless of order.
1081 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1082 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1083 { { "a", true }, { "h", false }, { "g", false }, { "f", false } },
1085 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1086 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1087 "\"http://h.com\"],[],[],"
1088 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1089 "\"NAVIGATION\", \"NAVIGATION\","
1090 "\"NAVIGATION\", \"NAVIGATION\","
1092 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1093 { { "a", true }, { "h.com", false }, { "g.com", false },
1094 { "f.com", false } },
1097 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1098 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1099 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch },
1101 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1102 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1104 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1105 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1106 "\"google:suggestrelevance\":[1]}]",
1107 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1109 { "[\"a\",[\"http://a1.com\"],[],[],"
1110 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1111 "\"google:suggestrelevance\":[9999, 1]}]",
1112 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1115 // Ensure that all 'verbatim' results are merged with their maximum score.
1116 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1117 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1118 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1120 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1121 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1122 "\"google:verbatimrelevance\":0}]",
1123 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1126 // Ensure that verbatim is always generated without other suggestions.
1127 // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1128 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1129 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1131 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1132 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1136 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1137 QueryForInput(ASCIIToUTF16("a"), false, false);
1138 net::TestURLFetcher* fetcher =
1139 test_factory_.GetFetcherByID(
1140 SearchProvider::kDefaultProviderURLFetcherID);
1141 ASSERT_TRUE(fetcher);
1142 fetcher->set_response_code(200);
1143 fetcher->SetResponseString(cases[i].json);
1144 fetcher->delegate()->OnURLFetchComplete(fetcher);
1145 RunTillProviderDone();
1147 const std::string description = "for input with json=" + cases[i].json;
1148 const ACMatches& matches = provider_->matches();
1149 // The top match must inline and score as highly as calculated verbatim.
1150 ASSERT_FALSE(matches.empty());
1151 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1152 matches[0].inline_autocompletion) << description;
1153 EXPECT_GE(matches[0].relevance, 1300) << description;
1156 // Ensure that the returned matches equal the expectations.
1157 for (; j < matches.size(); ++j) {
1158 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1159 matches[j].contents) << description;
1160 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1161 matches[j].allowed_to_be_default_match) << description;
1163 // Ensure that no expected matches are missing.
1164 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1165 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1166 "Case # " << i << " " << description;
1170 // This test is like DefaultFetcherSuggestRelevance above except it enables
1171 // the field trial that causes the omnibox to be willing to reorder matches
1172 // to guarantee the top result is a legal default match. This field trial
1173 // causes SearchProvider to allow some constraints to be violated that it
1174 // wouldn't normally because the omnibox will fix the problems later.
1175 TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevanceWithReorder) {
1176 struct DefaultFetcherMatch {
1177 std::string contents;
1178 bool allowed_to_be_default_match;
1180 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
1182 const std::string json;
1183 const DefaultFetcherMatch matches[4];
1184 const std::string inline_autocompletion;
1186 // Ensure that suggestrelevance scores reorder matches.
1187 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1188 { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch },
1190 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1191 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1192 "\"google:suggestrelevance\":[1, 2]}]",
1193 { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch },
1196 // Without suggested relevance scores, we should only allow one
1197 // navsuggest result to be be displayed.
1198 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1199 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1200 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1203 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1204 // Negative values will have no effect; the calculated value will be used.
1205 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1206 "\"google:suggestrelevance\":[9998]}]",
1207 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch },
1209 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1210 "\"google:suggestrelevance\":[9999]}]",
1211 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1213 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1214 "\"google:suggestrelevance\":[9999]}]",
1215 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1217 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1218 "\"google:suggestrelevance\":[9999]}]",
1219 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1221 { "[\"a\",[\"http://a.com\"],[],[],"
1222 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1223 "\"google:verbatimrelevance\":9999,"
1224 "\"google:suggestrelevance\":[9998]}]",
1225 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
1227 { "[\"a\",[\"http://a.com\"],[],[],"
1228 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1229 "\"google:verbatimrelevance\":9998,"
1230 "\"google:suggestrelevance\":[9999]}]",
1231 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1233 { "[\"a\",[\"http://a.com\"],[],[],"
1234 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1235 "\"google:verbatimrelevance\":0,"
1236 "\"google:suggestrelevance\":[9999]}]",
1237 { { "a.com", true }, 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 },
1246 // Ensure that both types of relevance scores reorder matches together.
1247 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1248 "\"google:verbatimrelevance\":9998}]",
1249 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch },
1252 // Allow non-inlineable matches to be the highest-scoring match but,
1253 // if the result set lacks a single inlineable result, abandon suggested
1254 // relevance scores entirely.
1255 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1256 { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch },
1258 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1259 "\"google:verbatimrelevance\":0}]",
1260 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
1262 { "[\"a\",[\"http://b.com\"],[],[],"
1263 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1264 "\"google:suggestrelevance\":[9999]}]",
1265 { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch },
1267 { "[\"a\",[\"http://b.com\"],[],[],"
1268 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1269 "\"google:suggestrelevance\":[9999],"
1270 "\"google:verbatimrelevance\":0}]",
1271 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1274 // Allow low-scoring matches.
1275 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1276 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1278 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1279 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1281 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1282 "\"google:verbatimrelevance\":0}]",
1283 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1285 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1286 "\"google:verbatimrelevance\":0}]",
1287 { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1289 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1290 "\"google:verbatimrelevance\":2}]",
1291 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1293 { "[\"a\",[\"http://a.com\"],[],[],"
1294 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1295 "\"google:suggestrelevance\":[1],"
1296 "\"google:verbatimrelevance\":0}]",
1297 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1299 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1300 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1301 "\"google:suggestrelevance\":[1, 2],"
1302 "\"google:verbatimrelevance\":0}]",
1303 { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1306 // Ensure that all suggestions are considered, regardless of order.
1307 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1308 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1309 { { "a", true }, { "h", false }, { "g", false }, { "f", false } },
1311 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1312 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1313 "\"http://h.com\"],[],[],"
1314 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1315 "\"NAVIGATION\", \"NAVIGATION\","
1316 "\"NAVIGATION\", \"NAVIGATION\","
1318 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1319 { { "a", true }, { "h.com", false }, { "g.com", false },
1320 { "f.com", false } },
1323 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1324 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1325 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch },
1327 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1328 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1330 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1331 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1332 "\"google:suggestrelevance\":[1]}]",
1333 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1335 { "[\"a\",[\"http://a1.com\"],[],[],"
1336 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1337 "\"google:suggestrelevance\":[9999, 1]}]",
1338 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1341 // Ensure that all 'verbatim' results are merged with their maximum score.
1342 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1343 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1344 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1346 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1347 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1348 "\"google:verbatimrelevance\":0}]",
1349 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1352 // Ensure that verbatim is always generated without other suggestions.
1353 // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1354 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1355 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1357 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1358 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1362 std::map<std::string, std::string> params;
1363 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
1364 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleEnabled;
1365 ASSERT_TRUE(chrome_variations::AssociateVariationParams(
1366 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
1367 base::FieldTrialList::CreateFieldTrial(
1368 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
1370 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1371 QueryForInput(ASCIIToUTF16("a"), false, false);
1372 net::TestURLFetcher* fetcher =
1373 test_factory_.GetFetcherByID(
1374 SearchProvider::kDefaultProviderURLFetcherID);
1375 ASSERT_TRUE(fetcher);
1376 fetcher->set_response_code(200);
1377 fetcher->SetResponseString(cases[i].json);
1378 fetcher->delegate()->OnURLFetchComplete(fetcher);
1379 RunTillProviderDone();
1381 const std::string description = "for input with json=" + cases[i].json;
1382 const ACMatches& matches = provider_->matches();
1383 // The top match must inline and score as highly as calculated verbatim.
1384 ASSERT_FALSE(matches.empty());
1385 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1386 matches[0].inline_autocompletion) << description;
1389 // Ensure that the returned matches equal the expectations.
1390 for (; j < matches.size(); ++j) {
1391 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1392 matches[j].contents) << description;
1393 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1394 matches[j].allowed_to_be_default_match) << description;
1396 // Ensure that no expected matches are missing.
1397 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1398 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1399 "Case # " << i << " " << description;
1403 // Verifies that suggest results with relevance scores are added
1404 // properly when using the keyword fetcher. This is similar to the
1405 // test DefaultFetcherSuggestRelevance above but this uses inputs that
1406 // trigger keyword suggestions (i.e., "k a" rather than "a") and has
1407 // different expectations (because now the results are a mix of
1408 // keyword suggestions and default provider suggestions). When a new
1409 // test is added to this TEST_F, please consider if it would be
1410 // appropriate to add to DefaultFetcherSuggestRelevance as well.
1411 TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
1412 struct KeywordFetcherMatch {
1413 std::string contents;
1415 bool allowed_to_be_default_match;
1417 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
1419 const std::string json;
1420 const KeywordFetcherMatch matches[5];
1421 const std::string inline_autocompletion;
1423 // Ensure that suggest relevance scores reorder matches and that
1424 // the keyword verbatim (lacking a suggested verbatim score) beats
1425 // the default provider verbatim.
1426 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1427 { { "a", true, true },
1428 { "k a", false, true },
1429 { "c", true, false },
1430 { "b", true, false },
1433 // Again, check that relevance scores reorder matches, just this
1434 // time with navigation matches. This also checks that with
1435 // suggested relevance scores we allow multiple navsuggest results.
1436 // It's odd that navsuggest results that come from a keyword
1437 // provider are marked as not a keyword result. I think this
1438 // comes from them not going to a keyword search engine).
1439 // TODO(mpearson): Investigate the implications (if any) of
1440 // tagging these results appropriately. If so, do it because it
1441 // makes more sense.
1442 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
1443 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1444 "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
1445 { { "a", true, true },
1446 { "d", true, false },
1447 { "c.com", false, false },
1448 { "b.com", false, false },
1449 { "k a", false, true }, },
1452 // Without suggested relevance scores, we should only allow one
1453 // navsuggest result to be be displayed.
1454 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1455 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1456 { { "a", true, true },
1457 { "b.com", false, false },
1458 { "k a", false, true },
1459 kEmptyMatch, kEmptyMatch },
1462 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1463 // Negative values will have no effect; the calculated value will be used.
1464 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1465 "\"google:suggestrelevance\":[9998]}]",
1466 { { "a", true, true },
1467 { "a1", true, true },
1468 { "k a", false, true },
1469 kEmptyMatch, kEmptyMatch },
1471 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1472 "\"google:suggestrelevance\":[9999]}]",
1473 { { "a1", true, true },
1474 { "a", true, true },
1475 { "k a", false, true },
1476 kEmptyMatch, kEmptyMatch },
1478 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1479 "\"google:suggestrelevance\":[9999]}]",
1480 { { "a1", true, true },
1481 { "k a", false, true },
1482 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1484 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1485 "\"google:suggestrelevance\":[9999]}]",
1486 { { "a1", true, true },
1487 { "a", true, true },
1488 { "k a", false, true },
1489 kEmptyMatch, kEmptyMatch },
1491 { "[\"a\",[\"http://a.com\"],[],[],"
1492 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1493 "\"google:verbatimrelevance\":9999,"
1494 "\"google:suggestrelevance\":[9998]}]",
1495 { { "a", true, true },
1496 { "a.com", false, true },
1497 { "k a", false, true },
1498 kEmptyMatch, kEmptyMatch },
1501 // Ensure that both types of relevance scores reorder matches together.
1502 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1503 "\"google:verbatimrelevance\":9998}]",
1504 { { "a1", true, true },
1505 { "a", true, true },
1506 { "a2", true, true },
1507 { "k a", false, true },
1511 // Ensure that only inlinable matches may be ranked as the highest result.
1512 // Ignore all suggested relevance scores if this constraint is violated.
1513 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1514 { { "a", true, true },
1515 { "b", true, false },
1516 { "k a", false, true },
1517 kEmptyMatch, kEmptyMatch },
1519 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1520 "\"google:verbatimrelevance\":0}]",
1521 { { "a", true, true },
1522 { "b", true, false },
1523 { "k a", false, true },
1524 kEmptyMatch, kEmptyMatch },
1526 { "[\"a\",[\"http://b.com\"],[],[],"
1527 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1528 "\"google:suggestrelevance\":[9999]}]",
1529 { { "a", true, true },
1530 { "b.com", false, false },
1531 { "k a", false, true },
1532 kEmptyMatch, kEmptyMatch },
1534 { "[\"a\",[\"http://b.com\"],[],[],"
1535 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1536 "\"google:suggestrelevance\":[9999],"
1537 "\"google:verbatimrelevance\":0}]",
1538 { { "a", true, true },
1539 { "b.com", false, false },
1540 { "k a", false, true },
1541 kEmptyMatch, kEmptyMatch },
1544 // Ensure that the top result is ranked as highly as calculated verbatim.
1545 // Ignore the suggested verbatim relevance if this constraint is violated.
1546 // Note that keyword suggestions by default (not in suggested relevance
1547 // mode) score more highly than the default verbatim.
1548 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1549 { { "a", true, true },
1550 { "a1", true, true },
1551 { "k a", false, true },
1552 kEmptyMatch, kEmptyMatch },
1554 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1555 { { "a", true, true },
1556 { "a1", true, true },
1557 { "k a", false, true },
1558 kEmptyMatch, kEmptyMatch},
1560 // Continuing the same category of tests, but make sure we keep the
1561 // suggested relevance scores even as we discard the verbatim relevance
1563 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1564 "\"google:verbatimrelevance\":0}]",
1565 { { "a", true, true },
1566 { "k a", false, true },
1567 { "a1", true, true },
1568 kEmptyMatch, kEmptyMatch},
1570 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1571 "\"google:verbatimrelevance\":0}]",
1572 { { "a", true, true },
1573 { "k a", false, true },
1574 { "a2", true, true },
1575 { "a1", true, true },
1578 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1579 "\"google:verbatimrelevance\":2}]",
1580 { { "a", true, true },
1581 { "k a", false, true },
1582 { "a2", true, true },
1583 { "a1", true, true },
1587 // Ensure that all suggestions are considered, regardless of order.
1588 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1589 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1590 { { "a", true, true },
1591 { "k a", false, true },
1592 { "h", true, false },
1593 { "g", true, false },
1594 { "f", true, false } },
1596 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1597 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1598 "\"http://h.com\"],[],[],"
1599 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1600 "\"NAVIGATION\", \"NAVIGATION\","
1601 "\"NAVIGATION\", \"NAVIGATION\","
1603 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1604 { { "a", true, true },
1605 { "k a", false, true },
1606 { "h.com", false, false },
1607 { "g.com", false, false },
1608 { "f.com", false, false } },
1611 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1612 // Note that keyword suggestions by default (not in suggested relevance
1613 // mode) score more highly than the default verbatim.
1614 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1615 { { "a", true, true },
1616 { "a1", true, true },
1617 { "a2", true, true },
1618 { "k a", false, true },
1621 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1622 { { "a", true, true },
1623 { "a1", true, true },
1624 { "k a", false, true },
1625 kEmptyMatch, kEmptyMatch},
1627 // In this case, ignored the suggested relevance scores means we keep
1628 // only one navsuggest result.
1629 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1630 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1631 "\"google:suggestrelevance\":[1]}]",
1632 { { "a", true, true },
1633 { "a1.com", false, true },
1634 { "k a", false, true },
1635 kEmptyMatch, kEmptyMatch},
1637 { "[\"a\",[\"http://a1.com\"],[],[],"
1638 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1639 "\"google:suggestrelevance\":[9999, 1]}]",
1640 { { "a", true, true },
1641 { "a1.com", false, true },
1642 { "k a", false, true },
1643 kEmptyMatch, kEmptyMatch},
1646 // Ensure that all 'verbatim' results are merged with their maximum score.
1647 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1648 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1649 { { "a2", true, true },
1650 { "a", true, true },
1651 { "a1", true, true },
1652 { "k a", false, true },
1655 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1656 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1657 "\"google:verbatimrelevance\":0}]",
1658 { { "a2", true, true },
1659 { "a", true, true },
1660 { "a1", true, true },
1661 { "k a", false, true },
1665 // Ensure that verbatim is always generated without other suggestions.
1666 // TODO(mpearson): Ensure the value of verbatimrelevance is respected
1667 // (except when suggested relevances are ignored).
1668 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1669 { { "a", true, true },
1670 { "k a", false, true },
1671 kEmptyMatch, kEmptyMatch, kEmptyMatch},
1673 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1674 { { "a", true, true },
1675 { "k a", false, true },
1676 kEmptyMatch, kEmptyMatch, kEmptyMatch},
1679 // Check that navsuggestions will be demoted below queries.
1680 // (Navsuggestions are not allowed to appear first.) In the process,
1681 // make sure the navsuggestions still remain in the same order.
1682 // First, check the situation where navsuggest scores more than verbatim
1683 // and there are no query suggestions.
1684 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1685 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1686 "\"google:verbatimrelevance\":9990,"
1687 "\"google:suggestrelevance\":[9998, 9999]}]",
1688 { { "a", true, true },
1689 { "a2.com", false, true },
1690 { "a1.com", false, true },
1691 { "k a", false, true },
1694 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1695 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1696 "\"google:verbatimrelevance\":9990,"
1697 "\"google:suggestrelevance\":[9999, 9998]}]",
1698 { { "a", true, true },
1699 { "a1.com", false, true },
1700 { "a2.com", false, true },
1701 { "k a", false, true },
1704 { "[\"a\",[\"https://a/\"],[],[],"
1705 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1706 "\"google:suggestrelevance\":[9999]}]",
1707 { { "a", true, true },
1708 { "https://a", false, true },
1709 { "k a", false, true },
1713 // Check when navsuggest scores more than verbatim and there is query
1714 // suggestion but it scores lower.
1715 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1716 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1717 "\"google:verbatimrelevance\":9990,"
1718 "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
1719 { { "a", true, true },
1720 { "a2.com", false, true },
1721 { "a1.com", false, true },
1722 { "a3", true, true },
1723 { "k a", false, true } },
1725 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1726 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1727 "\"google:verbatimrelevance\":9990,"
1728 "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
1729 { { "a", true, true },
1730 { "a1.com", false, true },
1731 { "a2.com", false, true },
1732 { "a3", true, true },
1733 { "k a", false, true } },
1735 // Check when navsuggest scores more than a query suggestion. There is
1736 // a verbatim but it scores lower.
1737 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1738 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1739 "\"google:verbatimrelevance\":9990,"
1740 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1741 { { "a3", true, true },
1742 { "a2.com", false, true },
1743 { "a1.com", false, true },
1744 { "a", true, true },
1745 { "k a", false, true } },
1747 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1748 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1749 "\"google:verbatimrelevance\":9990,"
1750 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1751 { { "a3", true, true },
1752 { "a1.com", false, true },
1753 { "a2.com", false, true },
1754 { "a", true, true },
1755 { "k a", false, true } },
1757 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1758 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1759 "\"google:verbatimrelevance\":0,"
1760 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1761 { { "a3", true, true },
1762 { "a2.com", false, true },
1763 { "a1.com", false, true },
1764 { "k a", false, true },
1767 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1768 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1769 "\"google:verbatimrelevance\":0,"
1770 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1771 { { "a3", true, true },
1772 { "a1.com", false, true },
1773 { "a2.com", false, true },
1774 { "k a", false, true },
1777 // Check when there is neither verbatim nor a query suggestion that,
1778 // because we can't demote navsuggestions below a query suggestion,
1779 // we abandon suggested relevance scores entirely. One consequence is
1780 // that this means we restore the keyword verbatim match. Note
1781 // that in this case of abandoning suggested relevance scores, we still
1782 // keep the navsuggestions in the same order, but we revert to only allowing
1783 // one navigation to appear because the scores are completely local.
1784 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1785 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1786 "\"google:verbatimrelevance\":0,"
1787 "\"google:suggestrelevance\":[9998, 9999]}]",
1788 { { "a", true, true },
1789 { "a2.com", false, true },
1790 { "k a", false, true },
1791 kEmptyMatch, kEmptyMatch},
1793 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1794 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1795 "\"google:verbatimrelevance\":0,"
1796 "\"google:suggestrelevance\":[9999, 9998]}]",
1797 { { "a", true, true },
1798 { "a1.com", false, true },
1799 { "k a", false, true },
1800 kEmptyMatch, kEmptyMatch},
1802 // More checks that everything works when it's not necessary to demote.
1803 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1804 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1805 "\"google:verbatimrelevance\":9990,"
1806 "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
1807 { { "a3", true, true },
1808 { "a2.com", false, true },
1809 { "a1.com", false, true },
1810 { "a", true, true },
1811 { "k a", false, true } },
1813 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1814 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1815 "\"google:verbatimrelevance\":9990,"
1816 "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1817 { { "a3", true, true },
1818 { "a1.com", false, true },
1819 { "a2.com", false, true },
1820 { "a", true, true },
1821 { "k a", false, true } },
1825 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1826 QueryForInput(ASCIIToUTF16("k a"), false, true);
1828 // Set up a default fetcher with no results.
1829 net::TestURLFetcher* default_fetcher =
1830 test_factory_.GetFetcherByID(
1831 SearchProvider::kDefaultProviderURLFetcherID);
1832 ASSERT_TRUE(default_fetcher);
1833 default_fetcher->set_response_code(200);
1834 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
1835 default_fetcher = NULL;
1837 // Set up a keyword fetcher with provided results.
1838 net::TestURLFetcher* keyword_fetcher =
1839 test_factory_.GetFetcherByID(
1840 SearchProvider::kKeywordProviderURLFetcherID);
1841 ASSERT_TRUE(keyword_fetcher);
1842 keyword_fetcher->set_response_code(200);
1843 keyword_fetcher->SetResponseString(cases[i].json);
1844 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
1845 keyword_fetcher = NULL;
1846 RunTillProviderDone();
1848 const std::string description = "for input with json=" + cases[i].json;
1849 const ACMatches& matches = provider_->matches();
1850 // The top match must inline and score as highly as calculated verbatim.
1851 ASSERT_FALSE(matches.empty());
1852 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1853 matches[0].inline_autocompletion) << description;
1854 EXPECT_GE(matches[0].relevance, 1300) << description;
1857 // Ensure that the returned matches equal the expectations.
1858 for (; j < matches.size(); ++j) {
1859 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1860 matches[j].contents) << description;
1861 EXPECT_EQ(cases[i].matches[j].from_keyword,
1862 matches[j].keyword == ASCIIToUTF16("k")) << description;
1863 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1864 matches[j].allowed_to_be_default_match) << description;
1866 // Ensure that no expected matches are missing.
1867 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1868 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1869 "Case # " << i << " " << description;
1873 TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
1874 // Enable Instant Extended in order to allow an increased number of
1876 chrome::EnableInstantExtendedAPIForTesting();
1878 // We hardcode the string "term1" below, so ensure that the search term that
1879 // got added to history already is that string.
1880 ASSERT_EQ(ASCIIToUTF16("term1"), term1_);
1881 string16 term = term1_.substr(0, term1_.length() - 1);
1883 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2);
1884 profile_.BlockUntilHistoryProcessesPendingRequests();
1887 const string16 input;
1888 const std::string json;
1889 const std::string matches[6];
1891 // The history results outscore the default verbatim score. term2 has more
1892 // visits so it outscores term1. The suggestions are still returned since
1893 // they're server-scored.
1895 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
1896 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
1897 "\"google:suggestrelevance\":[1, 2, 3]}]",
1898 { "term2", "term1", "term", "a3", "a2", "a1" } },
1899 // Because we already have three suggestions by the time we see the history
1900 // results, they don't get returned.
1902 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
1903 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
1904 "\"google:verbatimrelevance\":1450,"
1905 "\"google:suggestrelevance\":[1440, 1430, 1420]}]",
1906 { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } },
1907 // If we only have two suggestions, we have room for a history result.
1909 "[\"term\",[\"a1\", \"a2\"],[],[],"
1910 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
1911 "\"google:verbatimrelevance\":1450,"
1912 "\"google:suggestrelevance\":[1430, 1410]}]",
1913 { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } },
1914 // If we have more than three suggestions, they should all be returned as
1915 // long as we have enough total space for them.
1917 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
1918 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
1919 "\"google:verbatimrelevance\":1450,"
1920 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]",
1921 { "term", "a1", "a2", "a3", "a4", kNotApplicable } },
1923 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[],"
1924 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\","
1925 "\"QUERY\", \"QUERY\"],"
1926 "\"google:verbatimrelevance\":1450,"
1927 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]",
1928 { "term", "a1", "a2", "a3", "a4", "a5" } },
1930 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
1931 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
1932 "\"google:verbatimrelevance\":1450,"
1933 "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]",
1934 { "term", "a1", "a2", "term2", "a3", "a4" } },
1935 // When the input looks like a URL, we disallow having a query as the
1936 // highest-ranking result. If the query was provided by a suggestion, we
1937 // reset the suggest scores to enforce this (see
1938 // SearchProvider::UpdateMatches()). Even if we reset the suggest scores,
1939 // however, we should still allow navsuggestions to be treated as
1941 { ASCIIToUTF16("a.com"),
1942 "[\"a.com\",[\"a1\", \"a2\", \"a.com/1\", \"a.com/2\"],[],[],"
1943 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"NAVIGATION\","
1945 // A verbatim query for URL-like input scores 850, so the navigation
1946 // scores here should bracket it.
1947 "\"google:suggestrelevance\":[9999, 9998, 900, 800]}]",
1948 { "a.com/1", "a.com", "a.com/2", "a1", kNotApplicable, kNotApplicable } },
1951 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1952 QueryForInput(cases[i].input, false, false);
1953 net::TestURLFetcher* fetcher =
1954 test_factory_.GetFetcherByID(
1955 SearchProvider::kDefaultProviderURLFetcherID);
1956 ASSERT_TRUE(fetcher);
1957 fetcher->set_response_code(200);
1958 fetcher->SetResponseString(cases[i].json);
1959 fetcher->delegate()->OnURLFetchComplete(fetcher);
1960 RunTillProviderDone();
1962 const std::string description = "for input with json=" + cases[i].json;
1963 const ACMatches& matches = provider_->matches();
1965 // Ensure no extra matches are present.
1966 ASSERT_LE(matches.size(), 6U);
1969 // Ensure that the returned matches equal the expectations.
1970 for (; j < matches.size(); ++j)
1971 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]),
1972 matches[j].contents) << description;
1973 // Ensure that no expected matches are missing.
1974 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1975 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) <<
1976 "Case # " << i << " " << description;
1980 // Verifies suggest relevance behavior for URL input.
1981 TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
1982 struct DefaultFetcherUrlInputMatch {
1983 const std::string match_contents;
1984 AutocompleteMatch::Type match_type;
1985 bool allowed_to_be_default_match;
1987 const DefaultFetcherUrlInputMatch kEmptyMatch =
1988 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
1990 const std::string input;
1991 const std::string json;
1992 const DefaultFetcherUrlInputMatch output[4];
1994 // Ensure topmost NAVIGATION matches are allowed for URL input.
1995 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
1996 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1997 "\"google:suggestrelevance\":[9999]}]",
1998 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true },
1999 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2000 kEmptyMatch, kEmptyMatch } },
2001 { "a.com", "[\"a.com\",[\"https://a.com\"],[],[],"
2002 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2003 "\"google:suggestrelevance\":[9999]}]",
2004 { { "https://a.com", AutocompleteMatchType::NAVSUGGEST, true },
2005 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2006 kEmptyMatch, kEmptyMatch } },
2008 // Ensure topmost SUGGEST matches are not allowed for URL input.
2009 // SearchProvider disregards search and verbatim suggested relevances.
2010 { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2011 "{\"google:suggestrelevance\":[9999]}]",
2012 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2013 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true },
2014 kEmptyMatch, kEmptyMatch } },
2015 { "a.com", "[\"a.com\",[\"a.com/a\"],[],[],"
2016 "{\"google:suggestrelevance\":[9999]}]",
2017 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2018 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2019 kEmptyMatch, kEmptyMatch } },
2021 // Ensure the fallback mechanism allows inlinable NAVIGATION matches.
2022 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
2023 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2024 "\"google:suggestrelevance\":[9999, 9998]}]",
2025 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2026 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2027 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2029 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
2030 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2031 "\"google:suggestrelevance\":[9998, 9997],"
2032 "\"google:verbatimrelevance\":9999}]",
2033 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2034 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2035 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2038 // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches.
2039 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
2040 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2041 "\"google:suggestrelevance\":[9999, 9998]}]",
2042 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2043 { "abc.com", AutocompleteMatchType::NAVSUGGEST, false },
2044 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2046 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
2047 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2048 "\"google:suggestrelevance\":[9998, 9997],"
2049 "\"google:verbatimrelevance\":9999}]",
2050 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2051 { "abc.com", AutocompleteMatchType::NAVSUGGEST, false },
2052 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2056 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2057 QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2058 net::TestURLFetcher* fetcher =
2059 test_factory_.GetFetcherByID(
2060 SearchProvider::kDefaultProviderURLFetcherID);
2061 ASSERT_TRUE(fetcher);
2062 fetcher->set_response_code(200);
2063 fetcher->SetResponseString(cases[i].json);
2064 fetcher->delegate()->OnURLFetchComplete(fetcher);
2065 RunTillProviderDone();
2068 const ACMatches& matches = provider_->matches();
2069 // Ensure that the returned matches equal the expectations.
2070 for (; j < matches.size(); ++j) {
2071 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2072 matches[j].contents);
2073 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2074 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2075 matches[j].allowed_to_be_default_match);
2077 // Ensure that no expected matches are missing.
2078 for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) {
2079 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2080 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2081 cases[i].output[j].match_type);
2082 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2087 // A basic test that verifies the field trial triggered parsing logic.
2088 TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) {
2089 QueryForInput(ASCIIToUTF16("foo"), false, false);
2091 // Make sure the default providers suggest service was queried.
2092 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
2093 SearchProvider::kDefaultProviderURLFetcherID);
2094 ASSERT_TRUE(fetcher);
2096 // Tell the SearchProvider the suggest query is done.
2097 fetcher->set_response_code(200);
2098 fetcher->SetResponseString(
2099 "[\"foo\",[\"foo bar\"],[\"\"],[],"
2100 "{\"google:suggesttype\":[\"QUERY\"],"
2101 "\"google:fieldtrialtriggered\":true}]");
2102 fetcher->delegate()->OnURLFetchComplete(fetcher);
2105 // Run till the history results complete.
2106 RunTillProviderDone();
2109 // Check for the match and field trial triggered bits.
2110 AutocompleteMatch match;
2111 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match));
2112 ProvidersInfo providers_info;
2113 provider_->AddProviderInfo(&providers_info);
2114 ASSERT_EQ(1U, providers_info.size());
2115 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2116 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size());
2119 // Reset the session and check that bits are reset.
2120 provider_->ResetSession();
2121 ProvidersInfo providers_info;
2122 provider_->AddProviderInfo(&providers_info);
2123 ASSERT_EQ(1U, providers_info.size());
2124 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2125 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size());
2129 // Verifies inline autocompletion of navigational results.
2130 TEST_F(SearchProviderTest, NavigationInline) {
2132 const std::string input;
2133 const std::string url;
2134 // Test the expected fill_into_edit, which may drop "http://".
2135 // Some cases do not trim "http://" to match from the start of the scheme.
2136 const std::string fill_into_edit;
2137 const std::string inline_autocompletion;
2138 const bool allowed_to_be_default_match;
2140 // Do not inline matches that do not contain the input; trim http as needed.
2141 { "x", "http://www.abc.com",
2142 "www.abc.com", std::string(), false },
2143 { "https:", "http://www.abc.com",
2144 "www.abc.com", std::string(), false },
2145 { "abc.com/", "http://www.abc.com",
2146 "www.abc.com", std::string(), false },
2147 { "http://www.abc.com/a", "http://www.abc.com",
2148 "http://www.abc.com", std::string(), false },
2149 { "http://www.abc.com", "https://www.abc.com",
2150 "https://www.abc.com", std::string(), false },
2151 { "http://abc.com", "ftp://abc.com",
2152 "ftp://abc.com", std::string(), false },
2153 { "https://www.abc.com", "http://www.abc.com",
2154 "www.abc.com", std::string(), false },
2155 { "ftp://abc.com", "http://abc.com",
2156 "abc.com", std::string(), false },
2158 // Do not inline matches with invalid input prefixes; trim http as needed.
2159 { "ttp", "http://www.abc.com",
2160 "www.abc.com", std::string(), false },
2161 { "://w", "http://www.abc.com",
2162 "www.abc.com", std::string(), false },
2163 { "ww.", "http://www.abc.com",
2164 "www.abc.com", std::string(), false },
2165 { ".ab", "http://www.abc.com",
2166 "www.abc.com", std::string(), false },
2167 { "bc", "http://www.abc.com",
2168 "www.abc.com", std::string(), false },
2169 { ".com", "http://www.abc.com",
2170 "www.abc.com", std::string(), false },
2172 // Do not inline matches that omit input domain labels; trim http as needed.
2173 { "www.a", "http://a.com",
2174 "a.com", std::string(), false },
2175 { "http://www.a", "http://a.com",
2176 "http://a.com", std::string(), false },
2177 { "www.a", "ftp://a.com",
2178 "ftp://a.com", std::string(), false },
2179 { "ftp://www.a", "ftp://a.com",
2180 "ftp://a.com", std::string(), false },
2182 // Input matching but with nothing to inline will not yield an offset, but
2183 // will be allowed to be default.
2184 { "abc.com", "http://www.abc.com",
2185 "www.abc.com", std::string(), true },
2186 { "http://www.abc.com", "http://www.abc.com",
2187 "http://www.abc.com", std::string(), true },
2189 // Inline matches when the input is a leading substring of the scheme.
2190 { "h", "http://www.abc.com",
2191 "http://www.abc.com", "ttp://www.abc.com", true },
2192 { "http", "http://www.abc.com",
2193 "http://www.abc.com", "://www.abc.com", true },
2195 // Inline matches when the input is a leading substring of the full URL.
2196 { "http:", "http://www.abc.com",
2197 "http://www.abc.com", "//www.abc.com", true },
2198 { "http://w", "http://www.abc.com",
2199 "http://www.abc.com", "ww.abc.com", true },
2200 { "http://www.", "http://www.abc.com",
2201 "http://www.abc.com", "abc.com", true },
2202 { "http://www.ab", "http://www.abc.com",
2203 "http://www.abc.com", "c.com", true },
2204 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
2205 "http://www.abc.com/path/file.htm?q=x#foo",
2206 "ath/file.htm?q=x#foo",
2208 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo",
2209 "http://abc.com/path/file.htm?q=x#foo",
2210 "ath/file.htm?q=x#foo", true},
2212 // Inline matches with valid URLPrefixes; only trim "http://".
2213 { "w", "http://www.abc.com",
2214 "www.abc.com", "ww.abc.com", true },
2215 { "www.a", "http://www.abc.com",
2216 "www.abc.com", "bc.com", true },
2217 { "abc", "http://www.abc.com",
2218 "www.abc.com", ".com", true },
2219 { "abc.c", "http://www.abc.com",
2220 "www.abc.com", "om", true },
2221 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
2222 "www.abc.com/path/file.htm?q=x#foo",
2223 "ath/file.htm?q=x#foo", true },
2224 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo",
2225 "abc.com/path/file.htm?q=x#foo",
2226 "ath/file.htm?q=x#foo", true },
2228 // Inline matches using the maximal URLPrefix components.
2229 { "h", "http://help.com",
2230 "help.com", "elp.com", true },
2231 { "http", "http://http.com",
2232 "http.com", ".com", true },
2233 { "h", "http://www.help.com",
2234 "www.help.com", "elp.com", true },
2235 { "http", "http://www.http.com",
2236 "www.http.com", ".com", true },
2237 { "w", "http://www.www.com",
2238 "www.www.com", "ww.com", true },
2240 // Test similar behavior for the ftp and https schemes.
2241 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
2242 "ftp://www.abc.com/path/file.htm?q=x#foo",
2243 "c.com/path/file.htm?q=x#foo", true },
2244 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
2245 "ftp://www.abc.com/path/file.htm?q=x#foo",
2246 "c.com/path/file.htm?q=x#foo", true },
2247 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
2248 "ftp://www.abc.com/path/file.htm?q=x#foo",
2249 "c.com/path/file.htm?q=x#foo", true },
2250 { "ab", "ftp://abc.com/path/file.htm?q=x#foo",
2251 "ftp://abc.com/path/file.htm?q=x#foo",
2252 "c.com/path/file.htm?q=x#foo", true },
2253 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo",
2254 "https://www.abc.com/path/file.htm?q=x#foo",
2255 "c.com/path/file.htm?q=x#foo", true },
2256 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo",
2257 "https://www.abc.com/path/file.htm?q=x#foo",
2258 "c.com/path/file.htm?q=x#foo", true },
2259 { "ab", "https://www.abc.com/path/file.htm?q=x#foo",
2260 "https://www.abc.com/path/file.htm?q=x#foo",
2261 "c.com/path/file.htm?q=x#foo", true },
2262 { "ab", "https://abc.com/path/file.htm?q=x#foo",
2263 "https://abc.com/path/file.htm?q=x#foo",
2264 "c.com/path/file.htm?q=x#foo", true },
2266 // Forced query input should inline and retain the "?" prefix.
2267 { "?http://www.ab", "http://www.abc.com",
2268 "?http://www.abc.com", "c.com", true },
2269 { "?www.ab", "http://www.abc.com",
2270 "?www.abc.com", "c.com", true },
2271 { "?ab", "http://www.abc.com",
2272 "?www.abc.com", "c.com", true },
2275 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2276 QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2277 AutocompleteMatch match(
2278 provider_->NavigationToMatch(SearchProvider::NavigationResult(
2279 *provider_.get(), GURL(cases[i].url), string16(), false, 0,
2281 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2282 match.inline_autocompletion);
2283 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit);
2284 EXPECT_EQ(cases[i].allowed_to_be_default_match,
2285 match.allowed_to_be_default_match);
2289 // Verifies that "http://" is not trimmed for input that is a leading substring.
2290 TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
2291 const string16 input(ASCIIToUTF16("ht"));
2292 const string16 url(ASCIIToUTF16("http://a.com"));
2293 const SearchProvider::NavigationResult result(
2294 *provider_.get(), GURL(url), string16(), false, 0, false);
2296 // Check the offset and strings when inline autocompletion is allowed.
2297 QueryForInput(input, false, false);
2298 AutocompleteMatch match_inline(provider_->NavigationToMatch(result));
2299 EXPECT_EQ(url, match_inline.fill_into_edit);
2300 EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion);
2301 EXPECT_TRUE(match_inline.allowed_to_be_default_match);
2302 EXPECT_EQ(url, match_inline.contents);
2304 // Check the same offset and strings when inline autocompletion is prevented.
2305 QueryForInput(input, true, false);
2306 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result));
2307 EXPECT_EQ(url, match_prevent.fill_into_edit);
2308 EXPECT_TRUE(match_prevent.inline_autocompletion.empty());
2309 EXPECT_FALSE(match_prevent.allowed_to_be_default_match);
2310 EXPECT_EQ(url, match_prevent.contents);
2313 // Verifies that input "w" marks a more significant domain label than "www.".
2314 TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
2315 QueryForInput(ASCIIToUTF16("w"), false, false);
2316 AutocompleteMatch match(
2317 provider_->NavigationToMatch(SearchProvider::NavigationResult(
2318 *provider_.get(), GURL("http://www.wow.com"), string16(), false, 0,
2320 EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion);
2321 EXPECT_TRUE(match.allowed_to_be_default_match);
2322 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit);
2323 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents);
2325 // Ensure that the match for input "w" is marked on "wow" and not "www".
2326 ASSERT_EQ(3U, match.contents_class.size());
2327 EXPECT_EQ(0U, match.contents_class[0].offset);
2328 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2329 match.contents_class[0].style);
2330 EXPECT_EQ(4U, match.contents_class[1].offset);
2331 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL |
2332 AutocompleteMatch::ACMatchClassification::MATCH,
2333 match.contents_class[1].style);
2334 EXPECT_EQ(5U, match.contents_class[2].offset);
2335 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2336 match.contents_class[2].style);
2339 TEST_F(SearchProviderTest, RemoveStaleResultsTest) {
2340 // TODO(mpearson): Consider expanding this test to explicitly cover
2341 // testing staleness for keyword results.
2343 const std::string omnibox_input;
2344 const int verbatim_relevance;
2345 // These cached suggestions should already be sorted.
2346 // The particular number 5 as the length of the array is
2347 // unimportant; it's merely enough cached results to fully test
2348 // the functioning of RemoveAllStaleResults().
2350 const std::string suggestion;
2351 const bool is_navigation_result;
2352 const int relevance;
2353 // |expect_match| is true if this result should survive
2354 // RemoveAllStaleResults() filtering against |omnibox_input| below.
2355 const bool expect_match;
2358 // Simple case: multiple query suggestions and no navsuggestions.
2359 // All query suggestions score less than search-what-you-typed and
2360 // thus none should be filtered because none will appear first.
2362 { { "food", false, 1299, true },
2363 { "foobar", false, 1298, true },
2364 { "crazy", false, 1297, true },
2365 { "friend", false, 1296, true },
2366 { kNotApplicable, false, 0, false } } },
2368 // Similarly simple cases, but the query suggestion appears first.
2370 { { "food", false, 1299, true },
2371 { "foobar", false, 1298, true },
2372 { "crazy", false, 1297, true },
2373 { "friend", false, 1296, true },
2374 { kNotApplicable, false, 0, false } } },
2376 { { "food", false, 1299, false },
2377 { "foobar", false, 1298, false },
2378 { "crazy", false, 1297, true },
2379 { "friend", false, 1296, true },
2380 { kNotApplicable, false, 0, false } } },
2382 { { "food", false, 1299, false },
2383 { "foobar", false, 1298, false },
2384 { "crazy", false, 1297, false },
2385 { "friend", false, 1296, false },
2386 { kNotApplicable, false, 0, false } } },
2388 // The same sort of cases, just using a mix of queries and navsuggestions.
2390 { { "http://food.com/", true, 1299, true },
2391 { "foobar", false, 1298, true },
2392 { "http://crazy.com/", true, 1297, true },
2393 { "friend", false, 1296, true },
2394 { "http://friend.com/", true, 1295, true } } },
2396 { { "http://food.com/", true, 1299, true },
2397 { "foobar", false, 1298, true },
2398 { "http://crazy.com/", true, 1297, true },
2399 { "friend", false, 1296, true },
2400 { "http://friend.com/", true, 1295, true } } },
2402 { { "http://food.com/", true, 1299, false },
2403 { "foobar", false, 1298, false },
2404 { "http://crazy.com/", true, 1297, true },
2405 { "friend", false, 1296, true },
2406 { "http://friend.com/", true, 1295, true } } },
2408 { { "http://food.com/", true, 1299, false },
2409 { "foobar", false, 1298, false },
2410 { "http://crazy.com/", true, 1297, false },
2411 { "friend", false, 1296, false },
2412 { "http://friend.com/", true, 1295, false } } },
2414 // Run the three tests immediately above again, just with verbatim
2415 // suppressed. Note that in the last case, all results are filtered.
2416 // Because verbatim is also suppressed, SearchProvider will realize
2417 // in UpdateMatches() that it needs to restore verbatim to fulfill
2418 // its constraints. This restoration does not happen in
2419 // RemoveAllStaleResults() and hence is not tested here. This restoration
2420 // is tested in the DefaultFetcherSuggestRelevance test.
2422 { { "http://food.com/", true, 1299, true },
2423 { "foobar", false, 1298, true },
2424 { "http://crazy.com/", true, 1297, true },
2425 { "friend", false, 1296, true },
2426 { "http://friend.com/", true, 1295, true } } },
2428 { { "http://food.com/", true, 1299, false },
2429 { "foobar", false, 1298, false },
2430 { "http://crazy.com/", true, 1297, true },
2431 { "friend", false, 1296, true },
2432 { "http://friend.com/", true, 1295, true } } },
2434 { { "http://food.com/", true, 1299, false },
2435 { "foobar", false, 1298, false },
2436 { "http://crazy.com/", true, 1297, false },
2437 { "friend", false, 1296, false },
2438 { "http://friend.com/", true, 1295, false } } },
2440 // The same sort of tests again, just with verbatim with a score
2441 // that would place it in between other suggestions.
2443 { { "http://food.com/", true, 1299, true },
2444 { "foobar", false, 1288, true },
2445 { "http://crazy.com/", true, 1277, true },
2446 { "friend", false, 1266, true },
2447 { "http://friend.com/", true, 1255, true } } },
2449 { { "http://food.com/", true, 1299, false },
2450 { "foobar", false, 1288, true },
2451 { "http://crazy.com/", true, 1277, true },
2452 { "friend", false, 1266, true },
2453 { "http://friend.com/", true, 1255, true } } },
2455 { { "http://food.com/", true, 1299, false },
2456 { "foobar", false, 1288, false },
2457 { "http://crazy.com/", true, 1277, true },
2458 { "friend", false, 1266, true },
2459 { "http://friend.com/", true, 1255, true } } },
2461 { { "http://food.com/", true, 1299, false },
2462 { "foobar", false, 1288, false },
2463 { "http://crazy.com/", true, 1277, true },
2464 { "friend", false, 1266, true },
2465 { "http://friend.com/", true, 1255, true } } },
2468 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2469 // Initialize cached results for this test case.
2470 provider_->default_results_.verbatim_relevance =
2471 cases[i].verbatim_relevance;
2472 provider_->default_results_.navigation_results.clear();
2473 provider_->default_results_.suggest_results.clear();
2474 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
2475 const std::string& suggestion = cases[i].results[j].suggestion;
2476 if (suggestion == kNotApplicable)
2478 if (cases[i].results[j].is_navigation_result) {
2479 provider_->default_results_.navigation_results.push_back(
2480 SearchProvider::NavigationResult(
2481 *provider_.get(), GURL(suggestion), string16(), false,
2482 cases[i].results[j].relevance, false));
2484 provider_->default_results_.suggest_results.push_back(
2485 SearchProvider::SuggestResult(ASCIIToUTF16(suggestion), string16(),
2486 string16(), std::string(), false,
2487 cases[i].results[j].relevance,
2492 provider_->input_ = AutocompleteInput(
2493 ASCIIToUTF16(cases[i].omnibox_input), string16::npos, string16(),
2494 GURL(), AutocompleteInput::INVALID_SPEC, false, false, true,
2495 AutocompleteInput::ALL_MATCHES);
2496 provider_->RemoveAllStaleResults();
2498 // Check cached results.
2499 SearchProvider::SuggestResults::const_iterator sug_it =
2500 provider_->default_results_.suggest_results.begin();
2501 const SearchProvider::SuggestResults::const_iterator sug_end =
2502 provider_->default_results_.suggest_results.end();
2503 SearchProvider::NavigationResults::const_iterator nav_it =
2504 provider_->default_results_.navigation_results.begin();
2505 const SearchProvider::NavigationResults::const_iterator nav_end =
2506 provider_->default_results_.navigation_results.end();
2507 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
2508 const std::string& suggestion = cases[i].results[j].suggestion;
2509 if (suggestion == kNotApplicable)
2511 if (!cases[i].results[j].expect_match)
2513 if (cases[i].results[j].is_navigation_result) {
2514 ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion;
2515 EXPECT_EQ(suggestion, nav_it->url().spec());
2518 ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion;
2519 EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion());
2523 EXPECT_EQ(sug_end, sug_it);
2524 EXPECT_EQ(nav_end, nav_it);
2528 #if !defined(OS_WIN)
2529 // Verify entity suggestion parsing.
2530 TEST_F(SearchProviderTest, ParseEntitySuggestion) {
2532 std::string contents;
2533 std::string query_params;
2534 std::string fill_into_edit;
2535 AutocompleteMatchType::Type type;
2536 size_t classification_offsets[3];
2537 int classification_styles[3];
2539 const size_t invalid_offset = 10;
2540 const int invalid_style = -1;
2541 const Match kEmptyMatch = {
2542 kNotApplicable, kNotApplicable, kNotApplicable,
2543 AutocompleteMatchType::NUM_TYPES,
2544 { invalid_offset, invalid_offset, invalid_offset },
2545 { invalid_style, invalid_style, invalid_style } };
2548 const std::string input_text;
2549 const std::string response_json;
2550 const Match matches[5];
2552 // A query and an entity suggestion with different search terms.
2554 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
2555 " {\"google:suggestdetail\":[{},"
2556 " {\"a\":\"A\",\"dq\":\"yy\",\"q\":\"p=v\"}],"
2557 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
2558 { { "x", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2559 { 0, invalid_offset, invalid_offset },
2560 { ACMatchClassification::NONE, invalid_style, invalid_style } },
2561 { "xy", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST,
2562 { 0, 1, invalid_offset },
2563 { ACMatchClassification::NONE, ACMatchClassification::MATCH,
2565 { "xy - A", "p=v", "yy", AutocompleteMatchType::SEARCH_SUGGEST,
2567 { ACMatchClassification::NONE, ACMatchClassification::MATCH,
2568 ACMatchClassification::DIM } },
2573 // A query and an entity suggestion with same search terms.
2575 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
2576 " {\"google:suggestdetail\":[{},"
2577 " {\"a\":\"A\",\"dq\":\"xy\",\"q\":\"p=v\"}],"
2578 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
2579 { { "x", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2580 { 0, invalid_offset, invalid_offset },
2581 { ACMatchClassification::NONE, invalid_style, invalid_style } },
2582 { "xy", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST,
2583 { 0, 1, invalid_offset },
2584 { ACMatchClassification::NONE, ACMatchClassification::MATCH,
2586 { "xy - A", "p=v", "xy", AutocompleteMatchType::SEARCH_SUGGEST,
2588 { ACMatchClassification::NONE, ACMatchClassification::MATCH,
2589 ACMatchClassification::DIM } },
2595 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2596 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
2598 // Set up a default fetcher with provided results.
2599 net::TestURLFetcher* fetcher =
2600 test_factory_.GetFetcherByID(
2601 SearchProvider::kDefaultProviderURLFetcherID);
2602 ASSERT_TRUE(fetcher);
2603 fetcher->set_response_code(200);
2604 fetcher->SetResponseString(cases[i].response_json);
2605 fetcher->delegate()->OnURLFetchComplete(fetcher);
2607 RunTillProviderDone();
2609 const ACMatches& matches = provider_->matches();
2610 ASSERT_FALSE(matches.empty());
2612 SCOPED_TRACE("for input with json = " + cases[i].response_json);
2615 // Ensure that the returned matches equal the expectations.
2616 for (; j < matches.size(); ++j) {
2617 const Match& match = cases[i].matches[j];
2618 SCOPED_TRACE(" and match index: " + base::IntToString(j));
2619 EXPECT_EQ(match.contents,
2620 UTF16ToUTF8(matches[j].contents));
2621 EXPECT_EQ(match.query_params,
2622 matches[j].search_terms_args->suggest_query_params);
2623 EXPECT_EQ(match.fill_into_edit,
2624 UTF16ToUTF8(matches[j].fill_into_edit));
2625 EXPECT_EQ(match.type, matches[j].type);
2628 for (; k < matches[j].contents_class.size(); k++) {
2629 SCOPED_TRACE(" and contents class: " + base::IntToString(k));
2630 EXPECT_EQ(match.classification_offsets[k],
2631 matches[j].contents_class[k].offset);
2632 EXPECT_EQ(match.classification_styles[k],
2633 matches[j].contents_class[k].style);
2635 for (; k < ARRAYSIZE_UNSAFE(match.classification_offsets); k++) {
2636 SCOPED_TRACE(" and contents class: " + base::IntToString(k));
2637 EXPECT_EQ(match.classification_offsets[k], invalid_offset);
2638 EXPECT_EQ(match.classification_styles[k], invalid_style);
2641 // Ensure that no expected matches are missing.
2642 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
2643 SCOPED_TRACE(" and match index: " + base::IntToString(j));
2644 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
2645 EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
2646 EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
2647 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
2651 #endif // !defined(OS_WIN)
2653 TEST_F(SearchProviderTest, SearchHistorySuppressesEntitySuggestion) {
2655 std::string contents;
2656 std::string query_params;
2657 std::string fill_into_edit;
2658 AutocompleteMatchType::Type type;
2660 const Match kEmptyMatch = { kNotApplicable, kNotApplicable, kNotApplicable,
2661 AutocompleteMatchType::NUM_TYPES};
2664 const std::string input_text;
2665 const std::string history_search_term;
2666 const std::string response_json;
2667 const Match matches[5];
2669 // Search history suppresses both query and entity suggestions.
2671 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
2672 " {\"google:suggestdetail\":[{},"
2673 " {\"a\":\"A\",\"dq\":\"xy\",\"q\":\"p=v\"}],"
2674 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
2676 {"xy", "", "xy", AutocompleteMatchType::SEARCH_HISTORY},
2677 {"x", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED},
2678 {"xy - A", "p=v", "xy", AutocompleteMatchType::SEARCH_SUGGEST},
2683 // Search history suppresses only query suggestion.
2685 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
2686 " {\"google:suggestdetail\":[{},"
2687 " {\"a\":\"A\",\"dq\":\"xyy\",\"q\":\"p=v\"}],"
2688 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
2690 {"xyy", "", "xyy", AutocompleteMatchType::SEARCH_HISTORY},
2691 {"xy", "", "xy", AutocompleteMatchType::SEARCH_HISTORY},
2692 {"x", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED},
2693 {"xy - A", "p=v", "xyy", AutocompleteMatchType::SEARCH_SUGGEST},
2699 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2700 GURL term_url(AddSearchToHistory(
2701 default_t_url_, ASCIIToUTF16(cases[i].history_search_term), 10));
2702 profile_.BlockUntilHistoryProcessesPendingRequests();
2703 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
2705 // Set up a default fetcher with provided results.
2706 net::TestURLFetcher* fetcher =
2707 test_factory_.GetFetcherByID(
2708 SearchProvider::kDefaultProviderURLFetcherID);
2709 ASSERT_TRUE(fetcher);
2710 fetcher->set_response_code(200);
2711 fetcher->SetResponseString(cases[i].response_json);
2712 fetcher->delegate()->OnURLFetchComplete(fetcher);
2714 RunTillProviderDone();
2716 const ACMatches& matches = provider_->matches();
2717 ASSERT_FALSE(matches.empty());
2718 SCOPED_TRACE("for case: " + base::IntToString(i));
2721 // Ensure that the returned matches equal the expectations.
2722 for (; j < matches.size(); ++j) {
2723 SCOPED_TRACE(" and match index: " + base::IntToString(j));
2724 EXPECT_EQ(cases[i].matches[j].contents,
2725 UTF16ToUTF8(matches[j].contents));
2726 EXPECT_EQ(cases[i].matches[j].query_params,
2727 matches[j].search_terms_args->suggest_query_params);
2728 EXPECT_EQ(cases[i].matches[j].fill_into_edit,
2729 UTF16ToUTF8(matches[j].fill_into_edit));
2730 EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
2732 // Ensure that no expected matches are missing.
2733 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
2734 SCOPED_TRACE(" and match index: " + base::IntToString(j));
2735 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
2736 EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
2737 EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
2738 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
2743 // A basic test that verifies the prefetch metadata parsing logic.
2744 TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
2746 std::string contents;
2747 bool allowed_to_be_prefetched;
2748 AutocompleteMatchType::Type type;
2751 const Match kEmptyMatch = { kNotApplicable,
2753 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2757 const std::string input_text;
2758 bool prefer_keyword_provider_results;
2759 const std::string default_provider_response_json;
2760 const std::string keyword_provider_response_json;
2761 const Match matches[5];
2763 // Default provider response does not have prefetch details. Ensure that the
2764 // suggestions are not marked as prefetch query.
2767 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2769 { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2770 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2771 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2776 // Ensure that default provider suggest response prefetch details are
2777 // parsed and recorded in AutocompleteMatch.
2780 "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[],"
2781 "{\"google:clientdata\":{\"phi\": 0},"
2782 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"],"
2783 "\"google:suggestrelevance\":[999, 12, 1]}]",
2785 { { "ab", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2786 { "abc", true, AutocompleteMatchType::SEARCH_SUGGEST, false },
2787 { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2788 { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2792 // Default provider suggest response has prefetch details.
2793 // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for
2794 // the same query string. Ensure that the prefetch details from
2795 // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match.
2798 "[\"ab\",[\"ab\", \"http://ab.com\"],[],[],"
2799 "{\"google:clientdata\":{\"phi\": 0},"
2800 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2801 "\"google:suggestrelevance\":[99, 98]}]",
2803 { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2804 {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2810 // Default provider response has prefetch details. We prefer keyword
2811 // provider results. Ensure that prefetch bit for a suggestion from the
2812 // default search provider does not get copied onto a higher-scoring match
2813 // for the same query string from the keyword provider.
2816 "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0},"
2817 "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
2818 "\"google:suggestrelevance\":[9, 12]}]",
2819 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2820 { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true},
2821 { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2822 { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2823 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true },
2824 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true }
2829 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2830 QueryForInput(ASCIIToUTF16(cases[i].input_text), false,
2831 cases[i].prefer_keyword_provider_results);
2833 // Set up a default fetcher with provided results.
2834 net::TestURLFetcher* fetcher =
2835 test_factory_.GetFetcherByID(
2836 SearchProvider::kDefaultProviderURLFetcherID);
2837 ASSERT_TRUE(fetcher);
2838 fetcher->set_response_code(200);
2839 fetcher->SetResponseString(cases[i].default_provider_response_json);
2840 fetcher->delegate()->OnURLFetchComplete(fetcher);
2842 if (cases[i].prefer_keyword_provider_results) {
2843 // Set up a keyword fetcher with provided results.
2844 net::TestURLFetcher* keyword_fetcher =
2845 test_factory_.GetFetcherByID(
2846 SearchProvider::kKeywordProviderURLFetcherID);
2847 ASSERT_TRUE(keyword_fetcher);
2848 keyword_fetcher->set_response_code(200);
2849 keyword_fetcher->SetResponseString(
2850 cases[i].keyword_provider_response_json);
2851 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
2852 keyword_fetcher = NULL;
2855 RunTillProviderDone();
2857 const std::string description =
2858 "for input with json =" + cases[i].default_provider_response_json;
2859 const ACMatches& matches = provider_->matches();
2860 // The top match must inline and score as highly as calculated verbatim.
2861 ASSERT_FALSE(matches.empty());
2862 EXPECT_GE(matches[0].relevance, 1300);
2864 // Ensure that the returned matches equal the expectations.
2865 for (size_t j = 0; j < matches.size(); ++j) {
2866 SCOPED_TRACE(description);
2867 EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents));
2868 EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
2869 SearchProvider::ShouldPrefetch(matches[j]));
2870 EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
2871 EXPECT_EQ(cases[i].matches[j].from_keyword,
2872 matches[j].keyword == ASCIIToUTF16("k"));
2877 // A basic test that verifies that the XSSI guarded JSON response is parsed
2879 TEST_F(SearchProviderTest, XSSIGuardedJSONParsing) {
2881 std::string contents;
2882 AutocompleteMatchType::Type type;
2884 const Match kEmptyMatch = { kNotApplicable,
2885 AutocompleteMatchType::NUM_TYPES};
2888 const std::string input_text;
2889 const std::string default_provider_response_json;
2890 const Match matches[4];
2894 "[\"a\",[\"b\", \"c\"],[],[],"
2895 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
2896 "\"google:suggestrelevance\":[1, 2]}]",
2897 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2898 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
2899 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
2903 // Standard XSSI guard - )]}'\n.
2905 ")]}'\n[\"a\",[\"b\", \"c\"],[],[],"
2906 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
2907 "\"google:suggestrelevance\":[1, 2]}]",
2908 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2909 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
2910 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
2914 // Modified XSSI guard - contains "[".
2916 ")]}'\n[)\"[\"a\",[\"b\", \"c\"],[],[],"
2917 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
2918 "\"google:suggestrelevance\":[1, 2]}]",
2919 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2920 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
2921 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
2927 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2929 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
2931 // Set up a default fetcher with provided results.
2932 net::TestURLFetcher* fetcher =
2933 test_factory_.GetFetcherByID(
2934 SearchProvider::kDefaultProviderURLFetcherID);
2935 ASSERT_TRUE(fetcher);
2936 fetcher->set_response_code(200);
2937 fetcher->SetResponseString(cases[i].default_provider_response_json);
2938 fetcher->delegate()->OnURLFetchComplete(fetcher);
2940 RunTillProviderDone();
2942 const ACMatches& matches = provider_->matches();
2943 // The top match must inline and score as highly as calculated verbatim.
2944 ASSERT_FALSE(matches.empty());
2945 EXPECT_GE(matches[0].relevance, 1300);
2947 SCOPED_TRACE("for case: " + base::IntToString(i));
2949 // Ensure that the returned matches equal the expectations.
2950 for (; j < matches.size(); ++j) {
2951 SCOPED_TRACE("and match: " + base::IntToString(j));
2952 EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents));
2953 EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
2955 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
2956 SCOPED_TRACE("and match: " + base::IntToString(j));
2957 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
2958 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
2964 TEST_F(SearchProviderTest, ReflectsBookmarkBarState) {
2965 profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, false);
2966 string16 term = term1_.substr(0, term1_.length() - 1);
2967 QueryForInput(term, true, false);
2968 ASSERT_FALSE(provider_->matches().empty());
2969 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2970 provider_->matches()[0].type);
2971 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
2972 EXPECT_FALSE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
2974 profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, true);
2975 term = term1_.substr(0, term1_.length() - 1);
2976 QueryForInput(term, true, false);
2977 ASSERT_FALSE(provider_->matches().empty());
2978 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2979 provider_->matches()[0].type);
2980 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
2981 EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);