search_provider_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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. 4 5#include "chrome/browser/autocomplete/search_provider.h" 6 7#include <string> 8 9#include "base/command_line.h" 10#include "base/metrics/field_trial.h" 11#include "base/prefs/pref_service.h" 12#include "base/run_loop.h" 13#include "base/strings/string16.h" 14#include "base/strings/string_number_conversions.h" 15#include "base/strings/string_util.h" 16#include "base/strings/utf_string_conversions.h" 17#include "base/time/time.h" 18#include "build/build_config.h" 19#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" 20#include "chrome/browser/autocomplete/autocomplete_controller.h" 21#include "chrome/browser/autocomplete/autocomplete_input.h" 22#include "chrome/browser/autocomplete/autocomplete_match.h" 23#include "chrome/browser/autocomplete/autocomplete_provider.h" 24#include "chrome/browser/autocomplete/autocomplete_provider_listener.h" 25#include "chrome/browser/autocomplete/history_url_provider.h" 26#include "chrome/browser/history/history_service.h" 27#include "chrome/browser/history/history_service_factory.h" 28#include "chrome/browser/omnibox/omnibox_field_trial.h" 29#include "chrome/browser/search_engines/search_engine_type.h" 30#include "chrome/browser/search_engines/template_url.h" 31#include "chrome/browser/search_engines/template_url_service.h" 32#include "chrome/browser/search_engines/template_url_service_factory.h" 33#include "chrome/browser/signin/signin_manager.h" 34#include "chrome/browser/signin/signin_manager_factory.h" 35#include "chrome/browser/sync/profile_sync_service.h" 36#include "chrome/browser/sync/profile_sync_service_factory.h" 37#include "chrome/common/chrome_switches.h" 38#include "chrome/common/metrics/variations/variations_util.h" 39#include "chrome/common/pref_names.h" 40#include "chrome/test/base/testing_browser_process.h" 41#include "chrome/test/base/testing_profile.h" 42#include "components/variations/entropy_provider.h" 43#include "content/public/test/test_browser_thread_bundle.h" 44#include "net/url_request/test_url_fetcher_factory.h" 45#include "net/url_request/url_request_status.h" 46#include "testing/gtest/include/gtest/gtest.h" 47 48using base::ASCIIToUTF16; 49 50namespace { 51 52// Returns the first match in |matches| with |allowed_to_be_default_match| 53// set to true. 54ACMatches::const_iterator FindDefaultMatch(const ACMatches& matches) { 55 ACMatches::const_iterator it = matches.begin(); 56 while ((it != matches.end()) && !it->allowed_to_be_default_match) 57 ++it; 58 return it; 59} 60 61class SuggestionDeletionHandler; 62class SearchProviderForTest : public SearchProvider { 63 public: 64 SearchProviderForTest( 65 AutocompleteProviderListener* listener, 66 Profile* profile); 67 bool is_success() { return is_success_; }; 68 69 protected: 70 virtual ~SearchProviderForTest(); 71 72 private: 73 virtual void RecordDeletionResult(bool success) OVERRIDE; 74 bool is_success_; 75 DISALLOW_COPY_AND_ASSIGN(SearchProviderForTest); 76}; 77 78SearchProviderForTest::SearchProviderForTest( 79 AutocompleteProviderListener* listener, 80 Profile* profile) 81 : SearchProvider(listener, profile), is_success_(false) { 82} 83 84SearchProviderForTest::~SearchProviderForTest() { 85} 86 87void SearchProviderForTest::RecordDeletionResult(bool success) { 88 is_success_ = success; 89} 90 91} // namespace 92 93// SearchProviderTest --------------------------------------------------------- 94 95// The following environment is configured for these tests: 96// . The TemplateURL default_t_url_ is set as the default provider. 97// . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This 98// TemplateURL has a valid suggest and search URL. 99// . The URL created by using the search term term1_ with default_t_url_ is 100// added to history. 101// . The URL created by using the search term keyword_term_ with keyword_t_url_ 102// is added to history. 103// . test_factory_ is set as the URLFetcherFactory. 104class SearchProviderTest : public testing::Test, 105 public AutocompleteProviderListener { 106 public: 107 struct ResultInfo { 108 ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES), 109 allowed_to_be_default_match(false) { 110 } 111 ResultInfo(GURL gurl, 112 AutocompleteMatch::Type result_type, 113 bool allowed_to_be_default_match, 114 base::string16 fill_into_edit) 115 : gurl(gurl), 116 result_type(result_type), 117 allowed_to_be_default_match(allowed_to_be_default_match), 118 fill_into_edit(fill_into_edit) { 119 } 120 121 const GURL gurl; 122 const AutocompleteMatch::Type result_type; 123 const bool allowed_to_be_default_match; 124 const base::string16 fill_into_edit; 125 }; 126 127 struct TestData { 128 const base::string16 input; 129 const size_t num_results; 130 const ResultInfo output[3]; 131 }; 132 133 SearchProviderTest() 134 : default_t_url_(NULL), 135 term1_(ASCIIToUTF16("term1")), 136 keyword_t_url_(NULL), 137 keyword_term_(ASCIIToUTF16("keyword")), 138 run_loop_(NULL) { 139 ResetFieldTrialList(); 140 } 141 142 // See description above class for what this registers. 143 virtual void SetUp() OVERRIDE; 144 virtual void TearDown() OVERRIDE; 145 146 void RunTest(TestData* cases, int num_cases, bool prefer_keyword); 147 148 protected: 149 // Needed for AutocompleteFieldTrial::ActivateStaticTrials(); 150 scoped_ptr<base::FieldTrialList> field_trial_list_; 151 152 // Default value used for testing. 153 static const std::string kNotApplicable; 154 155 // Adds a search for |term|, using the engine |t_url| to the history, and 156 // returns the URL for that search. 157 GURL AddSearchToHistory(TemplateURL* t_url, base::string16 term, int visit_count); 158 159 // Looks for a match in |provider_| with |contents| equal to |contents|. 160 // Sets |match| to it if found. Returns whether |match| was set. 161 bool FindMatchWithContents(const base::string16& contents, 162 AutocompleteMatch* match); 163 164 // Looks for a match in |provider_| with destination |url|. Sets |match| to 165 // it if found. Returns whether |match| was set. 166 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match); 167 168 // AutocompleteProviderListener: 169 // If we're waiting for the provider to finish, this exits the message loop. 170 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE; 171 172 // Runs a nested message loop until provider_ is done. The message loop is 173 // exited by way of OnProviderUpdate. 174 void RunTillProviderDone(); 175 176 // Invokes Start on provider_, then runs all pending tasks. 177 void QueryForInput(const base::string16& text, 178 bool prevent_inline_autocomplete, 179 bool prefer_keyword); 180 181 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is 182 // non-NULL, sets it to the "what you typed" entry for |text|. 183 void QueryForInputAndSetWYTMatch(const base::string16& text, 184 AutocompleteMatch* wyt_match); 185 186 // Notifies the URLFetcher for the suggest query corresponding to the default 187 // search provider that it's done. 188 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE. 189 void FinishDefaultSuggestQuery(); 190 191 // Runs SearchProvider on |input|, for which the suggest server replies 192 // with |json|, and expects that the resulting matches' contents equals 193 // that in |matches|. An empty entry in |matches| means no match should 194 // be returned in that position. Reports any errors with a message that 195 // includes |error_description|. 196 void ForcedQueryTestHelper(const std::string& input, 197 const std::string& json, 198 const std::string matches[3], 199 const std::string& error_description); 200 201 void ResetFieldTrialList(); 202 203 void ClearAllResults(); 204 205 // See description above class for details of these fields. 206 TemplateURL* default_t_url_; 207 const base::string16 term1_; 208 GURL term1_url_; 209 TemplateURL* keyword_t_url_; 210 const base::string16 keyword_term_; 211 GURL keyword_url_; 212 213 content::TestBrowserThreadBundle thread_bundle_; 214 215 // URLFetcherFactory implementation registered. 216 net::TestURLFetcherFactory test_factory_; 217 218 // Profile we use. 219 TestingProfile profile_; 220 221 // The provider. 222 scoped_refptr<SearchProviderForTest> provider_; 223 224 // If non-NULL, OnProviderUpdate quits the current |run_loop_|. 225 base::RunLoop* run_loop_; 226 227 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest); 228}; 229 230// static 231const std::string SearchProviderTest::kNotApplicable = "Not Applicable"; 232 233void SearchProviderTest::SetUp() { 234 // Make sure that fetchers are automatically ungregistered upon destruction. 235 test_factory_.set_remove_fetcher_on_delete(true); 236 237 // We need both the history service and template url model loaded. 238 ASSERT_TRUE(profile_.CreateHistoryService(true, false)); 239 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 240 &profile_, &TemplateURLServiceFactory::BuildInstanceFor); 241 242 TemplateURLService* turl_model = 243 TemplateURLServiceFactory::GetForProfile(&profile_); 244 245 turl_model->Load(); 246 247 // Reset the default TemplateURL. 248 TemplateURLData data; 249 data.short_name = ASCIIToUTF16("t"); 250 data.SetURL("http://defaultturl/{searchTerms}"); 251 data.suggestions_url = "http://defaultturl2/{searchTerms}"; 252 data.instant_url = "http://does/not/exist?strk=1"; 253 data.search_terms_replacement_key = "strk"; 254 default_t_url_ = new TemplateURL(&profile_, data); 255 turl_model->Add(default_t_url_); 256 turl_model->SetDefaultSearchProvider(default_t_url_); 257 TemplateURLID default_provider_id = default_t_url_->id(); 258 ASSERT_NE(0, default_provider_id); 259 260 // Add url1, with search term term1_. 261 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1); 262 263 // Create another TemplateURL. 264 data.short_name = ASCIIToUTF16("k"); 265 data.SetKeyword(ASCIIToUTF16("k")); 266 data.SetURL("http://keyword/{searchTerms}"); 267 data.suggestions_url = "http://suggest_keyword/{searchTerms}"; 268 keyword_t_url_ = new TemplateURL(&profile_, data); 269 turl_model->Add(keyword_t_url_); 270 ASSERT_NE(0, keyword_t_url_->id()); 271 272 // Add a page and search term for keyword_t_url_. 273 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1); 274 275 // Keywords are updated by the InMemoryHistoryBackend only after the message 276 // has been processed on the history thread. Block until history processes all 277 // requests to ensure the InMemoryDatabase is the state we expect it. 278 profile_.BlockUntilHistoryProcessesPendingRequests(); 279 280 provider_ = new SearchProviderForTest(this, &profile_); 281 provider_->kMinimumTimeBetweenSuggestQueriesMs = 0; 282} 283 284void SearchProviderTest::TearDown() { 285 base::RunLoop().RunUntilIdle(); 286 287 // Shutdown the provider before the profile. 288 provider_ = NULL; 289} 290 291void SearchProviderTest::RunTest(TestData* cases, 292 int num_cases, 293 bool prefer_keyword) { 294 ACMatches matches; 295 for (int i = 0; i < num_cases; ++i) { 296 AutocompleteInput input(cases[i].input, base::string16::npos, 297 base::string16(), GURL(), 298 AutocompleteInput::INVALID_SPEC, false, 299 prefer_keyword, true, 300 AutocompleteInput::ALL_MATCHES); 301 provider_->Start(input, false); 302 matches = provider_->matches(); 303 base::string16 diagnostic_details = 304 ASCIIToUTF16("Input was: ") + 305 cases[i].input + 306 ASCIIToUTF16("; prefer_keyword was: ") + 307 (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false")); 308 EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details; 309 if (matches.size() == cases[i].num_results) { 310 for (size_t j = 0; j < cases[i].num_results; ++j) { 311 EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) << 312 diagnostic_details; 313 EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) << 314 diagnostic_details; 315 EXPECT_EQ(cases[i].output[j].fill_into_edit, 316 matches[j].fill_into_edit) << diagnostic_details; 317 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match, 318 matches[j].allowed_to_be_default_match) << diagnostic_details; 319 } 320 } 321 } 322} 323 324void SearchProviderTest::OnProviderUpdate(bool updated_matches) { 325 if (run_loop_ && provider_->done()) { 326 run_loop_->Quit(); 327 run_loop_ = NULL; 328 } 329} 330 331void SearchProviderTest::RunTillProviderDone() { 332 if (provider_->done()) 333 return; 334 335 base::RunLoop run_loop; 336 run_loop_ = &run_loop; 337 run_loop.Run(); 338} 339 340void SearchProviderTest::QueryForInput(const base::string16& text, 341 bool prevent_inline_autocomplete, 342 bool prefer_keyword) { 343 // Start a query. 344 AutocompleteInput input(text, base::string16::npos, base::string16(), GURL(), 345 AutocompleteInput::INVALID_SPEC, 346 prevent_inline_autocomplete, prefer_keyword, true, 347 AutocompleteInput::ALL_MATCHES); 348 provider_->Start(input, false); 349 350 // RunUntilIdle so that the task scheduled by SearchProvider to create the 351 // URLFetchers runs. 352 base::RunLoop().RunUntilIdle(); 353} 354 355void SearchProviderTest::QueryForInputAndSetWYTMatch( 356 const base::string16& text, 357 AutocompleteMatch* wyt_match) { 358 QueryForInput(text, false, false); 359 profile_.BlockUntilHistoryProcessesPendingRequests(); 360 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 361 if (!wyt_match) 362 return; 363 ASSERT_GE(provider_->matches().size(), 1u); 364 EXPECT_TRUE(FindMatchWithDestination(GURL( 365 default_t_url_->url_ref().ReplaceSearchTerms( 366 TemplateURLRef::SearchTermsArgs(text))), 367 wyt_match)); 368} 369 370GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url, 371 base::string16 term, 372 int visit_count) { 373 HistoryService* history = 374 HistoryServiceFactory::GetForProfile(&profile_, 375 Profile::EXPLICIT_ACCESS); 376 GURL search(t_url->url_ref().ReplaceSearchTerms( 377 TemplateURLRef::SearchTermsArgs(term))); 378 static base::Time last_added_time; 379 last_added_time = std::max(base::Time::Now(), 380 last_added_time + base::TimeDelta::FromMicroseconds(1)); 381 history->AddPageWithDetails(search, base::string16(), visit_count, visit_count, 382 last_added_time, false, history::SOURCE_BROWSED); 383 history->SetKeywordSearchTermsForURL(search, t_url->id(), term); 384 return search; 385} 386 387bool SearchProviderTest::FindMatchWithContents(const base::string16& contents, 388 AutocompleteMatch* match) { 389 for (ACMatches::const_iterator i = provider_->matches().begin(); 390 i != provider_->matches().end(); ++i) { 391 if (i->contents == contents) { 392 *match = *i; 393 return true; 394 } 395 } 396 return false; 397} 398 399bool SearchProviderTest::FindMatchWithDestination(const GURL& url, 400 AutocompleteMatch* match) { 401 for (ACMatches::const_iterator i = provider_->matches().begin(); 402 i != provider_->matches().end(); ++i) { 403 if (i->destination_url == url) { 404 *match = *i; 405 return true; 406 } 407 } 408 return false; 409} 410 411void SearchProviderTest::FinishDefaultSuggestQuery() { 412 net::TestURLFetcher* default_fetcher = 413 test_factory_.GetFetcherByID( 414 SearchProvider::kDefaultProviderURLFetcherID); 415 ASSERT_TRUE(default_fetcher); 416 417 // Tell the SearchProvider the default suggest query is done. 418 default_fetcher->set_response_code(200); 419 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 420} 421 422void SearchProviderTest::ForcedQueryTestHelper( 423 const std::string& input, 424 const std::string& json, 425 const std::string expected_matches[3], 426 const std::string& error_description) { 427 QueryForInput(ASCIIToUTF16(input), false, false); 428 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 429 SearchProvider::kDefaultProviderURLFetcherID); 430 ASSERT_TRUE(fetcher); 431 fetcher->set_response_code(200); 432 fetcher->SetResponseString(json); 433 fetcher->delegate()->OnURLFetchComplete(fetcher); 434 RunTillProviderDone(); 435 436 const ACMatches& matches = provider_->matches(); 437 ASSERT_LE(matches.size(), 3u); 438 size_t i = 0; 439 // Ensure that the returned matches equal the expectations. 440 for (; i < matches.size(); ++i) { 441 EXPECT_EQ(ASCIIToUTF16(expected_matches[i]), matches[i].contents) << 442 error_description; 443 } 444 // Ensure that no expected matches are missing. 445 for (; i < 3u; ++i) { 446 EXPECT_EQ(std::string(), expected_matches[i]) << 447 "Case #" << i << ": " << error_description; 448 } 449} 450 451void SearchProviderTest::ResetFieldTrialList() { 452 // Destroy the existing FieldTrialList before creating a new one to avoid 453 // a DCHECK. 454 field_trial_list_.reset(); 455 field_trial_list_.reset(new base::FieldTrialList( 456 new metrics::SHA1EntropyProvider("foo"))); 457 chrome_variations::testing::ClearAllVariationParams(); 458 base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial( 459 "AutocompleteDynamicTrial_0", "DefaultGroup"); 460 trial->group(); 461} 462 463void SearchProviderTest::ClearAllResults() { 464 provider_->ClearAllResults(); 465} 466 467// Actual Tests --------------------------------------------------------------- 468 469// Make sure we query history for the default provider and a URLFetcher is 470// created for the default provider suggest results. 471TEST_F(SearchProviderTest, QueryDefaultProvider) { 472 base::string16 term = term1_.substr(0, term1_.length() - 1); 473 QueryForInput(term, false, false); 474 475 // Make sure the default providers suggest service was queried. 476 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 477 SearchProvider::kDefaultProviderURLFetcherID); 478 ASSERT_TRUE(fetcher); 479 480 // And the URL matches what we expected. 481 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms( 482 TemplateURLRef::SearchTermsArgs(term))); 483 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url); 484 485 // Tell the SearchProvider the suggest query is done. 486 fetcher->set_response_code(200); 487 fetcher->delegate()->OnURLFetchComplete(fetcher); 488 fetcher = NULL; 489 490 // Run till the history results complete. 491 RunTillProviderDone(); 492 493 // The SearchProvider is done. Make sure it has a result for the history 494 // term term1. 495 AutocompleteMatch term1_match; 496 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match)); 497 // Term1 should not have a description, it's set later. 498 EXPECT_TRUE(term1_match.description.empty()); 499 500 AutocompleteMatch wyt_match; 501 EXPECT_TRUE(FindMatchWithDestination( 502 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 503 TemplateURLRef::SearchTermsArgs(term))), &wyt_match)); 504 EXPECT_TRUE(wyt_match.description.empty()); 505 506 // The match for term1 should be more relevant than the what you typed match. 507 EXPECT_GT(term1_match.relevance, wyt_match.relevance); 508 // This longer match should be inlineable. 509 EXPECT_TRUE(term1_match.allowed_to_be_default_match); 510 // The what you typed match should be too, of course. 511 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 512} 513 514TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) { 515 base::string16 term = term1_.substr(0, term1_.length() - 1); 516 QueryForInput(term, true, false); 517 518 ASSERT_FALSE(provider_->matches().empty()); 519 ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 520 provider_->matches()[0].type); 521 EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match); 522} 523 524// Issues a query that matches the registered keyword and makes sure history 525// is queried as well as URLFetchers getting created. 526TEST_F(SearchProviderTest, QueryKeywordProvider) { 527 base::string16 term = keyword_term_.substr(0, keyword_term_.length() - 1); 528 QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term, 529 false, 530 false); 531 532 // Make sure the default providers suggest service was queried. 533 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID( 534 SearchProvider::kDefaultProviderURLFetcherID); 535 ASSERT_TRUE(default_fetcher); 536 537 // Tell the SearchProvider the default suggest query is done. 538 default_fetcher->set_response_code(200); 539 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 540 default_fetcher = NULL; 541 542 // Make sure the keyword providers suggest service was queried. 543 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID( 544 SearchProvider::kKeywordProviderURLFetcherID); 545 ASSERT_TRUE(keyword_fetcher); 546 547 // And the URL matches what we expected. 548 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms( 549 TemplateURLRef::SearchTermsArgs(term))); 550 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url); 551 552 // Tell the SearchProvider the keyword suggest query is done. 553 keyword_fetcher->set_response_code(200); 554 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 555 keyword_fetcher = NULL; 556 557 // Run till the history results complete. 558 RunTillProviderDone(); 559 560 // The SearchProvider is done. Make sure it has a result for the history 561 // term keyword. 562 AutocompleteMatch match; 563 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match)); 564 565 // The match should have an associated keyword. 566 EXPECT_FALSE(match.keyword.empty()); 567 568 // The fill into edit should contain the keyword. 569 EXPECT_EQ(keyword_t_url_->keyword() + base::char16(' ') + keyword_term_, 570 match.fill_into_edit); 571} 572 573TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) { 574 // None of the following input strings should be sent to the suggest server, 575 // because they may contain private data. 576 const char* inputs[] = { 577 "username:password", 578 "http://username:password", 579 "https://username:password", 580 "username:password@hostname", 581 "http://username:password@hostname/", 582 "file://filename", 583 "data://data", 584 "unknownscheme:anything", 585 "http://hostname/?query=q", 586 "http://hostname/path#ref", 587 "http://hostname/path #ref", 588 "https://hostname/path", 589 }; 590 591 for (size_t i = 0; i < arraysize(inputs); ++i) { 592 QueryForInput(ASCIIToUTF16(inputs[i]), false, false); 593 // Make sure the default provider's suggest service was not queried. 594 ASSERT_TRUE(test_factory_.GetFetcherByID( 595 SearchProvider::kDefaultProviderURLFetcherID) == NULL); 596 // Run till the history results complete. 597 RunTillProviderDone(); 598 } 599} 600 601TEST_F(SearchProviderTest, SendNonPrivateDataToSuggest) { 602 // All of the following input strings should be sent to the suggest server, 603 // because they should not get caught by the private data checks. 604 const char* inputs[] = { 605 "query", 606 "query with spaces", 607 "http://hostname", 608 "http://hostname/path", 609 "http://hostname #ref", 610 "www.hostname.com #ref", 611 "https://hostname", 612 "#hashtag", 613 "foo https://hostname/path" 614 }; 615 616 profile_.BlockUntilHistoryProcessesPendingRequests(); 617 for (size_t i = 0; i < arraysize(inputs); ++i) { 618 QueryForInput(ASCIIToUTF16(inputs[i]), false, false); 619 // Make sure the default provider's suggest service was queried. 620 ASSERT_TRUE(test_factory_.GetFetcherByID( 621 SearchProvider::kDefaultProviderURLFetcherID) != NULL); 622 } 623} 624 625TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) { 626 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse( 627 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor); 628 GURL url = AddSearchToHistory(default_t_url_, 629 ASCIIToUTF16("docs.google.com"), 1); 630 631 // Add the term as a url. 632 HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)-> 633 AddPageWithDetails(GURL("http://docs.google.com"), base::string16(), 1, 1, 634 base::Time::Now(), false, history::SOURCE_BROWSED); 635 profile_.BlockUntilHistoryProcessesPendingRequests(); 636 637 AutocompleteMatch wyt_match; 638 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"), 639 &wyt_match)); 640 641 // There should be two matches, one for what you typed, the other for 642 // 'docs.google.com'. The search term should have a lower priority than the 643 // what you typed match. 644 ASSERT_EQ(2u, provider_->matches().size()); 645 AutocompleteMatch term_match; 646 EXPECT_TRUE(FindMatchWithDestination(url, &term_match)); 647 EXPECT_GT(wyt_match.relevance, term_match.relevance); 648 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 649 EXPECT_TRUE(term_match.allowed_to_be_default_match); 650} 651 652TEST_F(SearchProviderTest, DontGiveNavsuggestionsInForcedQueryMode) { 653 const std::string kEmptyMatch; 654 struct { 655 const std::string json; 656 const std::string matches_in_default_mode[3]; 657 const std::string matches_in_forced_query_mode[3]; 658 } cases[] = { 659 // Without suggested relevance scores. 660 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[]," 661 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]}]", 662 { "a", "a1.com", "a2" }, 663 { "a", "a2", kEmptyMatch } }, 664 665 // With suggested relevance scores in a situation where navsuggest would 666 // go second. 667 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[]," 668 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]," 669 "\"google:suggestrelevance\":[1250, 1200]}]", 670 { "a", "a1.com", "a2" }, 671 { "a", "a2", kEmptyMatch } }, 672 673 // With suggested relevance scores in a situation where navsuggest 674 // would go first. 675 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[]," 676 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]," 677 "\"google:suggestrelevance\":[1350, 1250]}]", 678 { "a1.com", "a", "a2" }, 679 { "a", "a2", kEmptyMatch } }, 680 681 // With suggested relevance scores in a situation where navsuggest 682 // would go first only because verbatim has been demoted. 683 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[]," 684 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]," 685 "\"google:suggestrelevance\":[1450, 1400]," 686 "\"google:verbatimrelevance\":1350}]", 687 { "a1.com", "a2", "a" }, 688 { "a2", "a", kEmptyMatch } }, 689 }; 690 691 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 692 ForcedQueryTestHelper("a", cases[i].json, cases[i].matches_in_default_mode, 693 "regular input with json=" + cases[i].json); 694 ForcedQueryTestHelper("?a", cases[i].json, 695 cases[i].matches_in_forced_query_mode, 696 "forced query input with json=" + cases[i].json); 697 } 698} 699 700// A multiword search with one visit should not autocomplete until multiple 701// words are typed. 702TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) { 703 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"), 704 1)); 705 profile_.BlockUntilHistoryProcessesPendingRequests(); 706 707 AutocompleteMatch wyt_match; 708 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"), 709 &wyt_match)); 710 ASSERT_EQ(2u, provider_->matches().size()); 711 AutocompleteMatch term_match; 712 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 713 EXPECT_GT(wyt_match.relevance, term_match.relevance); 714 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 715 EXPECT_TRUE(term_match.allowed_to_be_default_match); 716 717 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"), 718 &wyt_match)); 719 ASSERT_EQ(2u, provider_->matches().size()); 720 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 721 EXPECT_GT(term_match.relevance, wyt_match.relevance); 722 EXPECT_TRUE(term_match.allowed_to_be_default_match); 723 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 724} 725 726// A multiword search with more than one visit should autocomplete immediately. 727TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) { 728 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 729 2)); 730 profile_.BlockUntilHistoryProcessesPendingRequests(); 731 732 AutocompleteMatch wyt_match; 733 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"), 734 &wyt_match)); 735 ASSERT_EQ(2u, provider_->matches().size()); 736 AutocompleteMatch term_match; 737 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 738 EXPECT_GT(term_match.relevance, wyt_match.relevance); 739 EXPECT_TRUE(term_match.allowed_to_be_default_match); 740 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 741} 742 743// Autocompletion should work at a word boundary after a space. 744TEST_F(SearchProviderTest, AutocompleteAfterSpace) { 745 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 746 2)); 747 profile_.BlockUntilHistoryProcessesPendingRequests(); 748 749 AutocompleteMatch wyt_match; 750 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "), 751 &wyt_match)); 752 ASSERT_EQ(2u, provider_->matches().size()); 753 AutocompleteMatch term_match; 754 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 755 EXPECT_GT(term_match.relevance, wyt_match.relevance); 756 EXPECT_TRUE(term_match.allowed_to_be_default_match); 757 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 758} 759 760// Newer multiword searches should score more highly than older ones. 761TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) { 762 GURL term_url_a(AddSearchToHistory(default_t_url_, 763 ASCIIToUTF16("three searches aaa"), 1)); 764 GURL term_url_b(AddSearchToHistory(default_t_url_, 765 ASCIIToUTF16("three searches bbb"), 1)); 766 profile_.BlockUntilHistoryProcessesPendingRequests(); 767 768 AutocompleteMatch wyt_match; 769 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"), 770 &wyt_match)); 771 ASSERT_EQ(3u, provider_->matches().size()); 772 AutocompleteMatch term_match_a; 773 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 774 AutocompleteMatch term_match_b; 775 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 776 EXPECT_GT(term_match_b.relevance, term_match_a.relevance); 777 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 778 EXPECT_TRUE(term_match_b.allowed_to_be_default_match); 779 EXPECT_TRUE(term_match_a.allowed_to_be_default_match); 780 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 781} 782 783// An autocompleted multiword search should not be replaced by a different 784// autocompletion while the user is still typing a valid prefix. 785TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) { 786 GURL term_url_a(AddSearchToHistory(default_t_url_, 787 ASCIIToUTF16("four searches aaa"), 2)); 788 GURL term_url_b(AddSearchToHistory(default_t_url_, 789 ASCIIToUTF16("four searches bbb"), 1)); 790 profile_.BlockUntilHistoryProcessesPendingRequests(); 791 792 AutocompleteMatch wyt_match; 793 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"), 794 &wyt_match)); 795 ASSERT_EQ(3u, provider_->matches().size()); 796 AutocompleteMatch term_match_a; 797 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 798 AutocompleteMatch term_match_b; 799 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 800 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 801 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 802 EXPECT_TRUE(term_match_a.allowed_to_be_default_match); 803 EXPECT_TRUE(term_match_b.allowed_to_be_default_match); 804 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 805 806 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"), 807 &wyt_match)); 808 ASSERT_EQ(3u, provider_->matches().size()); 809 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 810 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 811 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 812 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 813 EXPECT_TRUE(term_match_a.allowed_to_be_default_match); 814 EXPECT_TRUE(term_match_b.allowed_to_be_default_match); 815 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 816} 817 818// Non-completable multiword searches should not crowd out single-word searches. 819TEST_F(SearchProviderTest, DontCrowdOutSingleWords) { 820 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1)); 821 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1); 822 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1); 823 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1); 824 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1); 825 profile_.BlockUntilHistoryProcessesPendingRequests(); 826 827 AutocompleteMatch wyt_match; 828 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"), 829 &wyt_match)); 830 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size()); 831 AutocompleteMatch term_match; 832 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 833 EXPECT_GT(term_match.relevance, wyt_match.relevance); 834 EXPECT_TRUE(term_match.allowed_to_be_default_match); 835 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 836} 837 838// Inline autocomplete matches regardless of case differences from the input. 839TEST_F(SearchProviderTest, InlineMixedCaseMatches) { 840 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1)); 841 profile_.BlockUntilHistoryProcessesPendingRequests(); 842 843 AutocompleteMatch wyt_match; 844 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"), 845 &wyt_match)); 846 ASSERT_EQ(2u, provider_->matches().size()); 847 AutocompleteMatch term_match; 848 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 849 EXPECT_GT(term_match.relevance, wyt_match.relevance); 850 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit); 851 EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion); 852 EXPECT_TRUE(term_match.allowed_to_be_default_match); 853} 854 855// Verifies AutocompleteControllers return results (including keyword 856// results) in the right order and set descriptions for them correctly. 857TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) { 858 // Add an entry that corresponds to a keyword search with 'term2'. 859 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1); 860 profile_.BlockUntilHistoryProcessesPendingRequests(); 861 862 AutocompleteController controller(&profile_, NULL, 863 AutocompleteProvider::TYPE_SEARCH); 864 controller.Start(AutocompleteInput( 865 ASCIIToUTF16("k t"), base::string16::npos, base::string16(), GURL(), 866 AutocompleteInput::INVALID_SPEC, false, false, true, 867 AutocompleteInput::ALL_MATCHES)); 868 const AutocompleteResult& result = controller.result(); 869 870 // There should be three matches, one for the keyword history, one for 871 // keyword provider's what-you-typed, and one for the default provider's 872 // what you typed, in that order. 873 ASSERT_EQ(3u, result.size()); 874 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type); 875 EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE, 876 result.match_at(1).type); 877 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 878 result.match_at(2).type); 879 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance); 880 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance); 881 EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match); 882 EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match); 883 EXPECT_FALSE(result.match_at(2).allowed_to_be_default_match); 884 885 // The two keyword results should come with the keyword we expect. 886 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword); 887 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword); 888 // The default provider has a different keyword. (We don't explicitly 889 // set it during this test, so all we do is assert that it's different.) 890 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword); 891 892 // The top result will always have a description. The third result, 893 // coming from a different provider than the first two, should also. 894 // Whether the second result has one doesn't matter much. (If it was 895 // missing, people would infer that it's the same search provider as 896 // the one above it.) 897 EXPECT_FALSE(result.match_at(0).description.empty()); 898 EXPECT_FALSE(result.match_at(2).description.empty()); 899 EXPECT_NE(result.match_at(0).description, result.match_at(2).description); 900} 901 902TEST_F(SearchProviderTest, KeywordVerbatim) { 903 TestData cases[] = { 904 // Test a simple keyword input. 905 { ASCIIToUTF16("k foo"), 2, 906 { ResultInfo(GURL("http://keyword/foo"), 907 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 908 true, 909 ASCIIToUTF16("k foo")), 910 ResultInfo(GURL("http://defaultturl/k%20foo"), 911 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 912 false, 913 ASCIIToUTF16("k foo") ) } }, 914 915 // Make sure extra whitespace after the keyword doesn't change the 916 // keyword verbatim query. 917 { ASCIIToUTF16("k foo"), 2, 918 { ResultInfo(GURL("http://keyword/foo"), 919 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 920 true, 921 ASCIIToUTF16("k foo")), 922 ResultInfo(GURL("http://defaultturl/k%20%20%20foo"), 923 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 924 false, 925 ASCIIToUTF16("k foo")) } }, 926 // Leading whitespace should be stripped before SearchProvider gets the 927 // input; hence there are no tests here about how it handles those inputs. 928 929 // But whitespace elsewhere in the query string should matter to both 930 // matches. 931 { ASCIIToUTF16("k foo bar"), 2, 932 { ResultInfo(GURL("http://keyword/foo%20%20bar"), 933 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 934 true, 935 ASCIIToUTF16("k foo bar")), 936 ResultInfo(GURL("http://defaultturl/k%20%20foo%20%20bar"), 937 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 938 false, 939 ASCIIToUTF16("k foo bar")) } }, 940 // Note in the above test case we don't test trailing whitespace because 941 // SearchProvider still doesn't handle this well. See related bugs: 942 // 102690, 99239, 164635. 943 944 // Keywords can be prefixed by certain things that should get ignored 945 // when constructing the keyword match. 946 { ASCIIToUTF16("www.k foo"), 2, 947 { ResultInfo(GURL("http://keyword/foo"), 948 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 949 true, 950 ASCIIToUTF16("k foo")), 951 ResultInfo(GURL("http://defaultturl/www.k%20foo"), 952 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 953 false, 954 ASCIIToUTF16("www.k foo")) } }, 955 { ASCIIToUTF16("http://k foo"), 2, 956 { ResultInfo(GURL("http://keyword/foo"), 957 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 958 true, 959 ASCIIToUTF16("k foo")), 960 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"), 961 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 962 false, 963 ASCIIToUTF16("http://k foo")) } }, 964 { ASCIIToUTF16("http://www.k foo"), 2, 965 { ResultInfo(GURL("http://keyword/foo"), 966 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 967 true, 968 ASCIIToUTF16("k foo")), 969 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"), 970 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 971 false, 972 ASCIIToUTF16("http://www.k foo")) } }, 973 974 // A keyword with no remaining input shouldn't get a keyword 975 // verbatim match. 976 { ASCIIToUTF16("k"), 1, 977 { ResultInfo(GURL("http://defaultturl/k"), 978 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 979 true, 980 ASCIIToUTF16("k")) } }, 981 { ASCIIToUTF16("k "), 1, 982 { ResultInfo(GURL("http://defaultturl/k%20"), 983 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 984 true, 985 ASCIIToUTF16("k ")) } } 986 987 // The fact that verbatim queries to keyword are handled by KeywordProvider 988 // not SearchProvider is tested in 989 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc. 990 }; 991 992 // Test not in keyword mode. 993 RunTest(cases, arraysize(cases), false); 994 995 // Test in keyword mode. (Both modes should give the same result.) 996 RunTest(cases, arraysize(cases), true); 997} 998 999// Ensures command-line flags are reflected in the URLs the search provider 1000// generates. 1001TEST_F(SearchProviderTest, CommandLineOverrides) { 1002 TemplateURLService* turl_model = 1003 TemplateURLServiceFactory::GetForProfile(&profile_); 1004 1005 TemplateURLData data; 1006 data.short_name = ASCIIToUTF16("default"); 1007 data.SetKeyword(data.short_name); 1008 data.SetURL("{google:baseURL}{searchTerms}"); 1009 default_t_url_ = new TemplateURL(&profile_, data); 1010 turl_model->Add(default_t_url_); 1011 turl_model->SetDefaultSearchProvider(default_t_url_); 1012 1013 CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL, 1014 "http://www.bar.com/"); 1015 CommandLine::ForCurrentProcess()->AppendSwitchASCII( 1016 switches::kExtraSearchQueryParams, "a=b"); 1017 1018 TestData cases[] = { 1019 { ASCIIToUTF16("k a"), 2, 1020 { ResultInfo(GURL("http://keyword/a"), 1021 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 1022 true, 1023 ASCIIToUTF16("k a")), 1024 ResultInfo(GURL("http://www.bar.com/k%20a?a=b"), 1025 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1026 false, 1027 ASCIIToUTF16("k a")) } }, 1028 }; 1029 1030 RunTest(cases, arraysize(cases), false); 1031} 1032 1033// Verifies Navsuggest results don't set a TemplateURL, which Instant relies on. 1034// Also verifies that just the *first* navigational result is listed as a match 1035// if suggested relevance scores were not sent. 1036TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) { 1037 QueryForInput(ASCIIToUTF16("a.c"), false, false); 1038 1039 // Make sure the default providers suggest service was queried. 1040 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1041 SearchProvider::kDefaultProviderURLFetcherID); 1042 ASSERT_TRUE(fetcher); 1043 1044 // Tell the SearchProvider the suggest query is done. 1045 fetcher->set_response_code(200); 1046 fetcher->SetResponseString( 1047 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[]," 1048 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]"); 1049 fetcher->delegate()->OnURLFetchComplete(fetcher); 1050 fetcher = NULL; 1051 1052 // Run till the history results complete. 1053 RunTillProviderDone(); 1054 1055 // Make sure the only match is 'a.com' and it doesn't have a template_url. 1056 AutocompleteMatch nav_match; 1057 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match)); 1058 EXPECT_TRUE(nav_match.keyword.empty()); 1059 EXPECT_TRUE(nav_match.allowed_to_be_default_match); 1060 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match)); 1061} 1062 1063// Verifies that the most relevant suggest results are added properly. 1064TEST_F(SearchProviderTest, SuggestRelevance) { 1065 QueryForInput(ASCIIToUTF16("a"), false, false); 1066 1067 // Make sure the default provider's suggest service was queried. 1068 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1069 SearchProvider::kDefaultProviderURLFetcherID); 1070 ASSERT_TRUE(fetcher); 1071 1072 // Tell the SearchProvider the suggest query is done. 1073 fetcher->set_response_code(200); 1074 fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]"); 1075 fetcher->delegate()->OnURLFetchComplete(fetcher); 1076 fetcher = NULL; 1077 1078 // Run till the history results complete. 1079 RunTillProviderDone(); 1080 1081 // Check the expected verbatim and (first 3) suggestions' relative relevances. 1082 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4; 1083 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim)); 1084 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1)); 1085 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2)); 1086 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3)); 1087 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4)); 1088 EXPECT_GT(verbatim.relevance, match_a1.relevance); 1089 EXPECT_GT(match_a1.relevance, match_a2.relevance); 1090 EXPECT_GT(match_a2.relevance, match_a3.relevance); 1091 EXPECT_TRUE(verbatim.allowed_to_be_default_match); 1092 EXPECT_TRUE(match_a1.allowed_to_be_default_match); 1093 EXPECT_TRUE(match_a2.allowed_to_be_default_match); 1094 EXPECT_TRUE(match_a3.allowed_to_be_default_match); 1095} 1096 1097// Verifies that the default provider abandons suggested relevance scores 1098// when in keyword mode. This should happen regardless of whether the 1099// keyword provider returns suggested relevance scores. 1100TEST_F(SearchProviderTest, DefaultProviderNoSuggestRelevanceInKeywordMode) { 1101 struct { 1102 const std::string default_provider_json; 1103 const std::string keyword_provider_json; 1104 const std::string matches[5]; 1105 } cases[] = { 1106 // First, try an input where the keyword provider does not deliver 1107 // suggested relevance scores. 1108 { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[]," 1109 "{\"google:verbatimrelevance\":9700," 1110 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1111 "\"google:suggestrelevance\":[9900, 9800]}]", 1112 "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]}]", 1113 { "a", "akeyword-query", "k a", "adefault.com", "k adefault-query" } }, 1114 1115 // Now try with keyword provider suggested relevance scores. 1116 { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[]," 1117 "{\"google:verbatimrelevance\":9700," 1118 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1119 "\"google:suggestrelevance\":[9900, 9800]}]", 1120 "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]," 1121 "\"google:verbatimrelevance\":9500," 1122 "\"google:suggestrelevance\":[9600]}]", 1123 { "akeyword-query", "a", "k a", "adefault.com", "k adefault-query" } } 1124 }; 1125 1126 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1127 QueryForInput(ASCIIToUTF16("k a"), false, true); 1128 net::TestURLFetcher* default_fetcher = 1129 test_factory_.GetFetcherByID( 1130 SearchProvider::kDefaultProviderURLFetcherID); 1131 ASSERT_TRUE(default_fetcher); 1132 default_fetcher->set_response_code(200); 1133 default_fetcher->SetResponseString(cases[i].default_provider_json); 1134 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 1135 net::TestURLFetcher* keyword_fetcher = 1136 test_factory_.GetFetcherByID( 1137 SearchProvider::kKeywordProviderURLFetcherID); 1138 ASSERT_TRUE(keyword_fetcher); 1139 keyword_fetcher->set_response_code(200); 1140 keyword_fetcher->SetResponseString(cases[i].keyword_provider_json); 1141 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 1142 RunTillProviderDone(); 1143 1144 const std::string description = "for input with default_provider_json=" + 1145 cases[i].default_provider_json + " and keyword_provider_json=" + 1146 cases[i].keyword_provider_json; 1147 const ACMatches& matches = provider_->matches(); 1148 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 1149 size_t j = 0; 1150 // Ensure that the returned matches equal the expectations. 1151 for (; j < matches.size(); ++j) { 1152 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), matches[j].contents) << 1153 description; 1154 } 1155 // Ensure that no expected matches are missing. 1156 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1157 EXPECT_EQ(std::string(), cases[i].matches[j]) << description; 1158 } 1159} 1160 1161// Verifies that suggest results with relevance scores are added 1162// properly when using the default fetcher. When adding a new test 1163// case to this test, please consider adding it to the tests in 1164// KeywordFetcherSuggestRelevance below. 1165TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) { 1166 struct DefaultFetcherMatch { 1167 std::string contents; 1168 bool allowed_to_be_default_match; 1169 }; 1170 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false }; 1171 struct { 1172 const std::string json; 1173 const DefaultFetcherMatch matches[6]; 1174 const std::string inline_autocompletion; 1175 } cases[] = { 1176 // Ensure that suggestrelevance scores reorder matches. 1177 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1178 { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch, 1179 kEmptyMatch, kEmptyMatch }, 1180 std::string() }, 1181 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1182 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1183 "\"google:suggestrelevance\":[1, 2]}]", 1184 { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch, 1185 kEmptyMatch, kEmptyMatch }, 1186 std::string() }, 1187 1188 // Without suggested relevance scores, we should only allow one 1189 // navsuggest result to be be displayed. 1190 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1191 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1192 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch, 1193 kEmptyMatch, kEmptyMatch }, 1194 std::string() }, 1195 1196 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1197 // Negative values will have no effect; the calculated value will be used. 1198 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1199 "\"google:suggestrelevance\":[9998]}]", 1200 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1201 kEmptyMatch }, 1202 std::string() }, 1203 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1204 "\"google:suggestrelevance\":[9999]}]", 1205 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1206 kEmptyMatch }, 1207 "1" }, 1208 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1209 "\"google:suggestrelevance\":[9999]}]", 1210 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1211 kEmptyMatch }, 1212 "1" }, 1213 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1214 "\"google:suggestrelevance\":[9999]}]", 1215 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1216 kEmptyMatch }, 1217 "1" }, 1218 { "[\"a\",[\"http://a.com\"],[],[]," 1219 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1220 "\"google:verbatimrelevance\":9999," 1221 "\"google:suggestrelevance\":[9998]}]", 1222 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1223 kEmptyMatch }, 1224 std::string() }, 1225 { "[\"a\",[\"http://a.com\"],[],[]," 1226 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1227 "\"google:verbatimrelevance\":9998," 1228 "\"google:suggestrelevance\":[9999]}]", 1229 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1230 kEmptyMatch }, 1231 ".com" }, 1232 { "[\"a\",[\"http://a.com\"],[],[]," 1233 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1234 "\"google:verbatimrelevance\":0," 1235 "\"google:suggestrelevance\":[9999]}]", 1236 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1237 kEmptyMatch }, 1238 ".com" }, 1239 { "[\"a\",[\"http://a.com\"],[],[]," 1240 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1241 "\"google:verbatimrelevance\":-1," 1242 "\"google:suggestrelevance\":[9999]}]", 1243 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1244 kEmptyMatch }, 1245 ".com" }, 1246 1247 // Ensure that both types of relevance scores reorder matches together. 1248 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1249 "\"google:verbatimrelevance\":9998}]", 1250 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch, kEmptyMatch, 1251 kEmptyMatch }, 1252 "1" }, 1253 1254 // Ensure that only inlinable matches may be ranked as the highest result. 1255 // Ignore all suggested relevance scores if this constraint is violated. 1256 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1257 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1258 kEmptyMatch }, 1259 std::string() }, 1260 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1261 "\"google:verbatimrelevance\":0}]", 1262 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1263 kEmptyMatch }, 1264 std::string() }, 1265 { "[\"a\",[\"http://b.com\"],[],[]," 1266 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1267 "\"google:suggestrelevance\":[9999]}]", 1268 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch, 1269 kEmptyMatch, kEmptyMatch }, 1270 std::string() }, 1271 { "[\"a\",[\"http://b.com\"],[],[]," 1272 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1273 "\"google:suggestrelevance\":[9999]," 1274 "\"google:verbatimrelevance\":0}]", 1275 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch, 1276 kEmptyMatch, kEmptyMatch }, 1277 std::string() }, 1278 { "[\"a\",[\"https://a/\"],[],[]," 1279 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1280 "\"google:suggestrelevance\":[9999]}]", 1281 { { "https://a", true }, { "a", true }, kEmptyMatch, kEmptyMatch, 1282 kEmptyMatch, kEmptyMatch }, 1283 std::string() }, 1284 1285 // Ensure that the top result is ranked as highly as calculated verbatim. 1286 // Ignore the suggested verbatim relevance if this constraint is violated. 1287 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1288 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1289 kEmptyMatch }, 1290 std::string() }, 1291 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1292 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1293 kEmptyMatch }, 1294 std::string() }, 1295 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1296 "\"google:verbatimrelevance\":0}]", 1297 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1298 kEmptyMatch }, 1299 std::string() }, 1300 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1301 "\"google:verbatimrelevance\":0}]", 1302 { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1303 kEmptyMatch }, 1304 std::string() }, 1305 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1306 "\"google:verbatimrelevance\":2}]", 1307 { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1308 kEmptyMatch }, 1309 std::string() }, 1310 { "[\"a\",[\"http://a.com\"],[],[]," 1311 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1312 "\"google:suggestrelevance\":[1]," 1313 "\"google:verbatimrelevance\":0}]", 1314 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1315 kEmptyMatch }, 1316 std::string() }, 1317 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1318 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1319 "\"google:suggestrelevance\":[1, 2]," 1320 "\"google:verbatimrelevance\":0}]", 1321 { { "a", true }, { "a2.com", true }, { "a1.com", true }, kEmptyMatch, 1322 kEmptyMatch, kEmptyMatch }, 1323 std::string() }, 1324 1325 // Ensure that all suggestions are considered, regardless of order. 1326 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1327 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1328 { { "a", true }, { "h", false }, { "g", false }, { "f", false }, 1329 {"e", false }, {"d", false } }, 1330 std::string() }, 1331 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1332 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1333 "\"http://h.com\"],[],[]," 1334 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1335 "\"NAVIGATION\", \"NAVIGATION\"," 1336 "\"NAVIGATION\", \"NAVIGATION\"," 1337 "\"NAVIGATION\"]," 1338 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1339 { { "a", true }, { "h.com", false }, { "g.com", false }, 1340 { "f.com", false }, {"e.com", false }, {"d.com", false } }, 1341 std::string() }, 1342 1343 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1344 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1345 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch, kEmptyMatch, 1346 kEmptyMatch }, 1347 std::string() }, 1348 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1349 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1350 kEmptyMatch }, 1351 std::string() }, 1352 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1353 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1354 "\"google:suggestrelevance\":[1]}]", 1355 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch, 1356 kEmptyMatch, kEmptyMatch }, 1357 std::string() }, 1358 { "[\"a\",[\"http://a1.com\"],[],[]," 1359 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1360 "\"google:suggestrelevance\":[9999, 1]}]", 1361 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch, 1362 kEmptyMatch, kEmptyMatch }, 1363 std::string() }, 1364 1365 // Ensure that all 'verbatim' results are merged with their maximum score. 1366 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1367 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1368 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1369 kEmptyMatch }, 1370 "2" }, 1371 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1372 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1373 "\"google:verbatimrelevance\":0}]", 1374 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1375 kEmptyMatch }, 1376 "2" }, 1377 1378 // Ensure that verbatim is always generated without other suggestions. 1379 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 1380 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1381 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1382 kEmptyMatch }, 1383 std::string() }, 1384 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1385 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1386 kEmptyMatch }, 1387 std::string() }, 1388 }; 1389 1390 std::map<std::string, std::string> params; 1391 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) + 1392 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled; 1393 ASSERT_TRUE(chrome_variations::AssociateVariationParams( 1394 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); 1395 base::FieldTrialList::CreateFieldTrial( 1396 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); 1397 1398 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1399 QueryForInput(ASCIIToUTF16("a"), false, false); 1400 net::TestURLFetcher* fetcher = 1401 test_factory_.GetFetcherByID( 1402 SearchProvider::kDefaultProviderURLFetcherID); 1403 ASSERT_TRUE(fetcher); 1404 fetcher->set_response_code(200); 1405 fetcher->SetResponseString(cases[i].json); 1406 fetcher->delegate()->OnURLFetchComplete(fetcher); 1407 RunTillProviderDone(); 1408 1409 const std::string description = "for input with json=" + cases[i].json; 1410 const ACMatches& matches = provider_->matches(); 1411 // The top match must inline and score as highly as calculated verbatim. 1412 ASSERT_FALSE(matches.empty()); 1413 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1414 matches[0].inline_autocompletion) << description; 1415 EXPECT_GE(matches[0].relevance, 1300) << description; 1416 1417 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 1418 size_t j = 0; 1419 // Ensure that the returned matches equal the expectations. 1420 for (; j < matches.size(); ++j) { 1421 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1422 matches[j].contents) << description; 1423 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 1424 matches[j].allowed_to_be_default_match) << description; 1425 } 1426 // Ensure that no expected matches are missing. 1427 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1428 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1429 "Case # " << i << " " << description; 1430 } 1431} 1432 1433// This test is like DefaultFetcherSuggestRelevance above except it enables 1434// the field trial that causes the omnibox to be willing to reorder matches 1435// to guarantee the top result is a legal default match. This field trial 1436// causes SearchProvider to allow some constraints to be violated that it 1437// wouldn't normally because the omnibox will fix the problems later. 1438TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevanceWithReorder) { 1439 struct DefaultFetcherMatch { 1440 std::string contents; 1441 bool allowed_to_be_default_match; 1442 }; 1443 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false }; 1444 struct { 1445 const std::string json; 1446 const DefaultFetcherMatch matches[6]; 1447 const std::string inline_autocompletion; 1448 } cases[] = { 1449 // Ensure that suggestrelevance scores reorder matches. 1450 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1451 { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch, kEmptyMatch, 1452 kEmptyMatch }, 1453 std::string() }, 1454 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1455 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1456 "\"google:suggestrelevance\":[1, 2]}]", 1457 { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch, 1458 kEmptyMatch, kEmptyMatch }, 1459 std::string() }, 1460 1461 // Without suggested relevance scores, we should only allow one 1462 // navsuggest result to be be displayed. 1463 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1464 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1465 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch, 1466 kEmptyMatch, kEmptyMatch }, 1467 std::string() }, 1468 1469 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1470 // Negative values will have no effect; the calculated value will be used. 1471 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1472 "\"google:suggestrelevance\":[9998]}]", 1473 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1474 kEmptyMatch }, 1475 std::string() }, 1476 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1477 "\"google:suggestrelevance\":[9999]}]", 1478 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1479 kEmptyMatch }, 1480 "1" }, 1481 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1482 "\"google:suggestrelevance\":[9999]}]", 1483 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1484 kEmptyMatch }, 1485 "1" }, 1486 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1487 "\"google:suggestrelevance\":[9999]}]", 1488 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1489 kEmptyMatch }, 1490 "1" }, 1491 { "[\"a\",[\"http://a.com\"],[],[]," 1492 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1493 "\"google:verbatimrelevance\":9999," 1494 "\"google:suggestrelevance\":[9998]}]", 1495 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1496 kEmptyMatch }, 1497 std::string() }, 1498 { "[\"a\",[\"http://a.com\"],[],[]," 1499 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1500 "\"google:verbatimrelevance\":9998," 1501 "\"google:suggestrelevance\":[9999]}]", 1502 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1503 kEmptyMatch }, 1504 ".com" }, 1505 { "[\"a\",[\"http://a.com\"],[],[]," 1506 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1507 "\"google:verbatimrelevance\":0," 1508 "\"google:suggestrelevance\":[9999]}]", 1509 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1510 kEmptyMatch }, 1511 ".com" }, 1512 { "[\"a\",[\"http://a.com\"],[],[]," 1513 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1514 "\"google:verbatimrelevance\":-1," 1515 "\"google:suggestrelevance\":[9999]}]", 1516 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1517 kEmptyMatch }, 1518 ".com" }, 1519 1520 // Ensure that both types of relevance scores reorder matches together. 1521 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1522 "\"google:verbatimrelevance\":9998}]", 1523 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch, kEmptyMatch, 1524 kEmptyMatch }, 1525 "1" }, 1526 1527 // Allow non-inlineable matches to be the highest-scoring match but, 1528 // if the result set lacks a single inlineable result, abandon suggested 1529 // relevance scores entirely. 1530 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1531 { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1532 kEmptyMatch }, 1533 std::string() }, 1534 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1535 "\"google:verbatimrelevance\":0}]", 1536 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1537 kEmptyMatch }, 1538 std::string() }, 1539 { "[\"a\",[\"http://b.com\"],[],[]," 1540 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1541 "\"google:suggestrelevance\":[9999]}]", 1542 { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch, 1543 kEmptyMatch, kEmptyMatch }, 1544 std::string() }, 1545 { "[\"a\",[\"http://b.com\"],[],[]," 1546 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1547 "\"google:suggestrelevance\":[9999]," 1548 "\"google:verbatimrelevance\":0}]", 1549 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch, 1550 kEmptyMatch, kEmptyMatch }, 1551 std::string() }, 1552 1553 // Allow low-scoring matches. 1554 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1555 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1556 kEmptyMatch }, 1557 "1" }, 1558 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1559 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1560 kEmptyMatch }, 1561 "1" }, 1562 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1563 "\"google:verbatimrelevance\":0}]", 1564 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1565 kEmptyMatch }, 1566 "1" }, 1567 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1568 "\"google:verbatimrelevance\":0}]", 1569 { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1570 kEmptyMatch }, 1571 "2" }, 1572 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1573 "\"google:verbatimrelevance\":2}]", 1574 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1575 kEmptyMatch }, 1576 "2" }, 1577 { "[\"a\",[\"http://a.com\"],[],[]," 1578 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1579 "\"google:suggestrelevance\":[1]," 1580 "\"google:verbatimrelevance\":0}]", 1581 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1582 kEmptyMatch }, 1583 ".com" }, 1584 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1585 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1586 "\"google:suggestrelevance\":[1, 2]," 1587 "\"google:verbatimrelevance\":0}]", 1588 { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch, 1589 kEmptyMatch, kEmptyMatch }, 1590 "2.com" }, 1591 1592 // Ensure that all suggestions are considered, regardless of order. 1593 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1594 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1595 { { "a", true }, { "h", false }, { "g", false }, { "f", false }, 1596 { "e", false }, { "d", false } }, 1597 std::string() }, 1598 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1599 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1600 "\"http://h.com\"],[],[]," 1601 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1602 "\"NAVIGATION\", \"NAVIGATION\"," 1603 "\"NAVIGATION\", \"NAVIGATION\"," 1604 "\"NAVIGATION\"]," 1605 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1606 { { "a", true }, { "h.com", false }, { "g.com", false }, 1607 { "f.com", false }, { "e.com", false }, { "d.com", false } }, 1608 std::string() }, 1609 1610 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1611 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1612 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch, kEmptyMatch, 1613 kEmptyMatch }, 1614 std::string() }, 1615 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1616 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1617 kEmptyMatch }, 1618 std::string() }, 1619 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1620 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1621 "\"google:suggestrelevance\":[1]}]", 1622 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch, 1623 kEmptyMatch, kEmptyMatch }, 1624 std::string() }, 1625 { "[\"a\",[\"http://a1.com\"],[],[]," 1626 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1627 "\"google:suggestrelevance\":[9999, 1]}]", 1628 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch, 1629 kEmptyMatch, kEmptyMatch }, 1630 std::string() }, 1631 1632 // Ensure that all 'verbatim' results are merged with their maximum score. 1633 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1634 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1635 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1636 kEmptyMatch }, 1637 "2" }, 1638 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1639 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1640 "\"google:verbatimrelevance\":0}]", 1641 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1642 kEmptyMatch }, 1643 "2" }, 1644 1645 // Ensure that verbatim is always generated without other suggestions. 1646 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 1647 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1648 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1649 kEmptyMatch }, 1650 std::string() }, 1651 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1652 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1653 kEmptyMatch }, 1654 std::string() }, 1655 }; 1656 1657 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1658 QueryForInput(ASCIIToUTF16("a"), false, false); 1659 net::TestURLFetcher* fetcher = 1660 test_factory_.GetFetcherByID( 1661 SearchProvider::kDefaultProviderURLFetcherID); 1662 ASSERT_TRUE(fetcher); 1663 fetcher->set_response_code(200); 1664 fetcher->SetResponseString(cases[i].json); 1665 fetcher->delegate()->OnURLFetchComplete(fetcher); 1666 RunTillProviderDone(); 1667 1668 const std::string description = "for input with json=" + cases[i].json; 1669 const ACMatches& matches = provider_->matches(); 1670 ASSERT_FALSE(matches.empty()); 1671 // Find the first match that's allowed to be the default match and check 1672 // its inline_autocompletion. 1673 ACMatches::const_iterator it = FindDefaultMatch(matches); 1674 ASSERT_NE(matches.end(), it); 1675 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1676 it->inline_autocompletion) << description; 1677 1678 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 1679 size_t j = 0; 1680 // Ensure that the returned matches equal the expectations. 1681 for (; j < matches.size(); ++j) { 1682 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1683 matches[j].contents) << description; 1684 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 1685 matches[j].allowed_to_be_default_match) << description; 1686 } 1687 // Ensure that no expected matches are missing. 1688 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1689 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1690 "Case # " << i << " " << description; 1691 } 1692} 1693 1694// Verifies that suggest results with relevance scores are added 1695// properly when using the keyword fetcher. This is similar to the 1696// test DefaultFetcherSuggestRelevance above but this uses inputs that 1697// trigger keyword suggestions (i.e., "k a" rather than "a") and has 1698// different expectations (because now the results are a mix of 1699// keyword suggestions and default provider suggestions). When a new 1700// test is added to this TEST_F, please consider if it would be 1701// appropriate to add to DefaultFetcherSuggestRelevance as well. 1702TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) { 1703 struct KeywordFetcherMatch { 1704 std::string contents; 1705 bool from_keyword; 1706 bool allowed_to_be_default_match; 1707 }; 1708 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false }; 1709 struct { 1710 const std::string json; 1711 const KeywordFetcherMatch matches[6]; 1712 const std::string inline_autocompletion; 1713 } cases[] = { 1714 // Ensure that suggest relevance scores reorder matches and that 1715 // the keyword verbatim (lacking a suggested verbatim score) beats 1716 // the default provider verbatim. 1717 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1718 { { "a", true, true }, 1719 { "k a", false, false }, 1720 { "c", true, false }, 1721 { "b", true, false }, 1722 kEmptyMatch, kEmptyMatch }, 1723 std::string() }, 1724 // Again, check that relevance scores reorder matches, just this 1725 // time with navigation matches. This also checks that with 1726 // suggested relevance scores we allow multiple navsuggest results. 1727 // Note that navsuggest results that come from a keyword provider 1728 // are marked as not a keyword result. (They don't go to a 1729 // keyword search engine.) 1730 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[]," 1731 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1732 "\"google:suggestrelevance\":[1301, 1302, 1303]}]", 1733 { { "a", true, true }, 1734 { "d", true, false }, 1735 { "c.com", false, false }, 1736 { "b.com", false, false }, 1737 { "k a", false, false }, 1738 kEmptyMatch }, 1739 std::string() }, 1740 1741 // Without suggested relevance scores, we should only allow one 1742 // navsuggest result to be be displayed. 1743 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1744 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1745 { { "a", true, true }, 1746 { "b.com", false, false }, 1747 { "k a", false, false }, 1748 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1749 std::string() }, 1750 1751 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1752 // Negative values will have no effect; the calculated value will be used. 1753 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1754 "\"google:suggestrelevance\":[9998]}]", 1755 { { "a", true, true }, 1756 { "a1", true, true }, 1757 { "k a", false, false }, 1758 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1759 std::string() }, 1760 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1761 "\"google:suggestrelevance\":[9999]}]", 1762 { { "a1", true, true }, 1763 { "a", true, true }, 1764 { "k a", false, false }, 1765 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1766 "1" }, 1767 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1768 "\"google:suggestrelevance\":[9999]}]", 1769 { { "a1", true, true }, 1770 { "k a", false, false }, 1771 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1772 "1" }, 1773 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1774 "\"google:suggestrelevance\":[9999]}]", 1775 { { "a1", true, true }, 1776 { "a", true, true }, 1777 { "k a", false, false }, 1778 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1779 "1" }, 1780 { "[\"a\",[\"http://a.com\"],[],[]," 1781 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1782 "\"google:verbatimrelevance\":9999," 1783 "\"google:suggestrelevance\":[9998]}]", 1784 { { "a", true, true }, 1785 { "a.com", false, false }, 1786 { "k a", false, false }, 1787 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1788 std::string() }, 1789 1790 // Ensure that both types of relevance scores reorder matches together. 1791 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1792 "\"google:verbatimrelevance\":9998}]", 1793 { { "a1", true, true }, 1794 { "a", true, true }, 1795 { "a2", true, true }, 1796 { "k a", false, false }, 1797 kEmptyMatch, kEmptyMatch }, 1798 "1" }, 1799 1800 // Ensure that only inlinable matches may be ranked as the highest result. 1801 // Ignore all suggested relevance scores if this constraint is violated. 1802 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1803 { { "a", true, true }, 1804 { "b", true, false }, 1805 { "k a", false, false }, 1806 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1807 std::string() }, 1808 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1809 "\"google:verbatimrelevance\":0}]", 1810 { { "a", true, true }, 1811 { "b", true, false }, 1812 { "k a", false, false }, 1813 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1814 std::string() }, 1815 { "[\"a\",[\"http://b.com\"],[],[]," 1816 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1817 "\"google:suggestrelevance\":[9999]}]", 1818 { { "a", true, true }, 1819 { "b.com", false, false }, 1820 { "k a", false, false }, 1821 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1822 std::string() }, 1823 { "[\"a\",[\"http://b.com\"],[],[]," 1824 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1825 "\"google:suggestrelevance\":[9999]," 1826 "\"google:verbatimrelevance\":0}]", 1827 { { "a", true, true }, 1828 { "b.com", false, false }, 1829 { "k a", false, false }, 1830 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1831 std::string() }, 1832 1833 // Ensure that the top result is ranked as highly as calculated verbatim. 1834 // Ignore the suggested verbatim relevance if this constraint is violated. 1835 // Note that keyword suggestions by default (not in suggested relevance 1836 // mode) score more highly than the default verbatim. 1837 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1838 { { "a", true, true }, 1839 { "a1", true, true }, 1840 { "k a", false, false }, 1841 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1842 std::string() }, 1843 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1844 { { "a", true, true }, 1845 { "a1", true, true }, 1846 { "k a", false, false }, 1847 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1848 std::string() }, 1849 // Continuing the same category of tests, but make sure we keep the 1850 // suggested relevance scores even as we discard the verbatim relevance 1851 // scores. 1852 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1853 "\"google:verbatimrelevance\":0}]", 1854 { { "a", true, true }, 1855 { "k a", false, false }, 1856 { "a1", true, true }, 1857 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1858 std::string() }, 1859 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1860 "\"google:verbatimrelevance\":0}]", 1861 { { "a", true, true }, 1862 { "k a", false, false }, 1863 { "a2", true, true }, 1864 { "a1", true, true }, 1865 kEmptyMatch, kEmptyMatch }, 1866 std::string() }, 1867 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1868 "\"google:verbatimrelevance\":2}]", 1869 { { "a", true, true }, 1870 { "k a", false, false }, 1871 { "a2", true, true }, 1872 { "a1", true, true }, 1873 kEmptyMatch, kEmptyMatch }, 1874 std::string() }, 1875 1876 // Ensure that all suggestions are considered, regardless of order. 1877 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1878 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1879 { { "a", true, true }, 1880 { "k a", false, false }, 1881 { "h", true, false }, 1882 { "g", true, false }, 1883 { "f", true, false }, 1884 { "e", true, false } }, 1885 std::string() }, 1886 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1887 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1888 "\"http://h.com\"],[],[]," 1889 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1890 "\"NAVIGATION\", \"NAVIGATION\"," 1891 "\"NAVIGATION\", \"NAVIGATION\"," 1892 "\"NAVIGATION\"]," 1893 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1894 { { "a", true, true }, 1895 { "k a", false, false }, 1896 { "h.com", false, false }, 1897 { "g.com", false, false }, 1898 { "f.com", false, false }, 1899 { "e.com", false, false } }, 1900 std::string() }, 1901 1902 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1903 // Note that keyword suggestions by default (not in suggested relevance 1904 // mode) score more highly than the default verbatim. 1905 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1906 { { "a", true, true }, 1907 { "a1", true, true }, 1908 { "a2", true, true }, 1909 { "k a", false, false }, 1910 kEmptyMatch, kEmptyMatch }, 1911 std::string() }, 1912 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1913 { { "a", true, true }, 1914 { "a1", true, true }, 1915 { "k a", false, false }, 1916 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1917 std::string() }, 1918 // In this case, ignoring the suggested relevance scores means we keep 1919 // only one navsuggest result. 1920 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1921 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1922 "\"google:suggestrelevance\":[1]}]", 1923 { { "a", true, true }, 1924 { "a1.com", false, false }, 1925 { "k a", false, false }, 1926 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1927 std::string() }, 1928 { "[\"a\",[\"http://a1.com\"],[],[]," 1929 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1930 "\"google:suggestrelevance\":[9999, 1]}]", 1931 { { "a", true, true }, 1932 { "a1.com", false, false }, 1933 { "k a", false, false }, 1934 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1935 std::string() }, 1936 1937 // Ensure that all 'verbatim' results are merged with their maximum score. 1938 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1939 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1940 { { "a2", true, true }, 1941 { "a", true, true }, 1942 { "a1", true, true }, 1943 { "k a", false, false }, 1944 kEmptyMatch, kEmptyMatch }, 1945 "2" }, 1946 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1947 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1948 "\"google:verbatimrelevance\":0}]", 1949 { { "a2", true, true }, 1950 { "a", true, true }, 1951 { "a1", true, true }, 1952 { "k a", false, false }, 1953 kEmptyMatch, kEmptyMatch }, 1954 "2" }, 1955 1956 // Ensure that verbatim is always generated without other suggestions. 1957 // TODO(mpearson): Ensure the value of verbatimrelevance is respected 1958 // (except when suggested relevances are ignored). 1959 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1960 { { "a", true, true }, 1961 { "k a", false, false }, 1962 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1963 std::string() }, 1964 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1965 { { "a", true, true }, 1966 { "k a", false, false }, 1967 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1968 std::string() }, 1969 1970 // Check that navsuggestions will be demoted below queries. 1971 // (Navsuggestions are not allowed to appear first.) In the process, 1972 // make sure the navsuggestions still remain in the same order. 1973 // First, check the situation where navsuggest scores more than verbatim 1974 // and there are no query suggestions. 1975 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1976 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1977 "\"google:verbatimrelevance\":9990," 1978 "\"google:suggestrelevance\":[9998, 9999]}]", 1979 { { "a", true, true }, 1980 { "a2.com", false, false }, 1981 { "a1.com", false, false }, 1982 { "k a", false, false }, 1983 kEmptyMatch, kEmptyMatch }, 1984 std::string() }, 1985 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1986 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1987 "\"google:verbatimrelevance\":9990," 1988 "\"google:suggestrelevance\":[9999, 9998]}]", 1989 { { "a", true, true }, 1990 { "a1.com", false, false }, 1991 { "a2.com", false, false }, 1992 { "k a", false, false }, 1993 kEmptyMatch, kEmptyMatch }, 1994 std::string() }, 1995 { "[\"a\",[\"https://a/\"],[],[]," 1996 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1997 "\"google:suggestrelevance\":[9999]}]", 1998 { { "a", true, true }, 1999 { "https://a", false, false }, 2000 { "k a", false, false }, 2001 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2002 std::string() }, 2003 // Check when navsuggest scores more than verbatim and there is query 2004 // suggestion but it scores lower. 2005 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2006 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2007 "\"google:verbatimrelevance\":9990," 2008 "\"google:suggestrelevance\":[9998, 9999, 1300]}]", 2009 { { "a", true, true }, 2010 { "a2.com", false, false }, 2011 { "a1.com", false, false }, 2012 { "a3", true, true }, 2013 { "k a", false, false }, 2014 kEmptyMatch }, 2015 std::string() }, 2016 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2017 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2018 "\"google:verbatimrelevance\":9990," 2019 "\"google:suggestrelevance\":[9999, 9998, 1300]}]", 2020 { { "a", true, true }, 2021 { "a1.com", false, false }, 2022 { "a2.com", false, false }, 2023 { "a3", true, true }, 2024 { "k a", false, false }, 2025 kEmptyMatch }, 2026 std::string() }, 2027 // Check when navsuggest scores more than a query suggestion. There is 2028 // a verbatim but it scores lower. 2029 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2030 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2031 "\"google:verbatimrelevance\":9990," 2032 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 2033 { { "a3", true, true }, 2034 { "a2.com", false, false }, 2035 { "a1.com", false, false }, 2036 { "a", true, true }, 2037 { "k a", false, false }, 2038 kEmptyMatch }, 2039 "3" }, 2040 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2041 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2042 "\"google:verbatimrelevance\":9990," 2043 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 2044 { { "a3", true, true }, 2045 { "a1.com", false, false }, 2046 { "a2.com", false, false }, 2047 { "a", true, true }, 2048 { "k a", false, false }, 2049 kEmptyMatch }, 2050 "3" }, 2051 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2052 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2053 "\"google:verbatimrelevance\":0," 2054 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 2055 { { "a3", true, true }, 2056 { "a2.com", false, false }, 2057 { "a1.com", false, false }, 2058 { "k a", false, false }, 2059 kEmptyMatch, kEmptyMatch }, 2060 "3" }, 2061 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2062 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2063 "\"google:verbatimrelevance\":0," 2064 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 2065 { { "a3", true, true }, 2066 { "a1.com", false, false }, 2067 { "a2.com", false, false }, 2068 { "k a", false, false }, 2069 kEmptyMatch, kEmptyMatch }, 2070 "3" }, 2071 // Check when there is neither verbatim nor a query suggestion that, 2072 // because we can't demote navsuggestions below a query suggestion, 2073 // we abandon suggested relevance scores entirely. One consequence is 2074 // that this means we restore the keyword verbatim match. Note 2075 // that in this case of abandoning suggested relevance scores, we still 2076 // keep the navsuggestions in the same order, but we revert to only allowing 2077 // one navigation to appear because the scores are completely local. 2078 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 2079 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 2080 "\"google:verbatimrelevance\":0," 2081 "\"google:suggestrelevance\":[9998, 9999]}]", 2082 { { "a", true, true }, 2083 { "a2.com", false, false }, 2084 { "k a", false, false }, 2085 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2086 std::string() }, 2087 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 2088 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 2089 "\"google:verbatimrelevance\":0," 2090 "\"google:suggestrelevance\":[9999, 9998]}]", 2091 { { "a", true, true }, 2092 { "a1.com", false, false }, 2093 { "k a", false, false }, 2094 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2095 std::string() }, 2096 // More checks that everything works when it's not necessary to demote. 2097 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2098 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2099 "\"google:verbatimrelevance\":9990," 2100 "\"google:suggestrelevance\":[9997, 9998, 9999]}]", 2101 { { "a3", true, true }, 2102 { "a2.com", false, false }, 2103 { "a1.com", false, false }, 2104 { "a", true, true }, 2105 { "k a", false, false }, 2106 kEmptyMatch }, 2107 "3" }, 2108 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2109 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2110 "\"google:verbatimrelevance\":9990," 2111 "\"google:suggestrelevance\":[9998, 9997, 9999]}]", 2112 { { "a3", true, true }, 2113 { "a1.com", false, false }, 2114 { "a2.com", false, false }, 2115 { "a", true, true }, 2116 { "k a", false, false }, 2117 kEmptyMatch }, 2118 "3" }, 2119 }; 2120 2121 std::map<std::string, std::string> params; 2122 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) + 2123 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled; 2124 ASSERT_TRUE(chrome_variations::AssociateVariationParams( 2125 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); 2126 base::FieldTrialList::CreateFieldTrial( 2127 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); 2128 2129 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2130 QueryForInput(ASCIIToUTF16("k a"), false, true); 2131 2132 // Set up a default fetcher with no results. 2133 net::TestURLFetcher* default_fetcher = 2134 test_factory_.GetFetcherByID( 2135 SearchProvider::kDefaultProviderURLFetcherID); 2136 ASSERT_TRUE(default_fetcher); 2137 default_fetcher->set_response_code(200); 2138 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 2139 default_fetcher = NULL; 2140 2141 // Set up a keyword fetcher with provided results. 2142 net::TestURLFetcher* keyword_fetcher = 2143 test_factory_.GetFetcherByID( 2144 SearchProvider::kKeywordProviderURLFetcherID); 2145 ASSERT_TRUE(keyword_fetcher); 2146 keyword_fetcher->set_response_code(200); 2147 keyword_fetcher->SetResponseString(cases[i].json); 2148 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 2149 keyword_fetcher = NULL; 2150 RunTillProviderDone(); 2151 2152 const std::string description = "for input with json=" + cases[i].json; 2153 const ACMatches& matches = provider_->matches(); 2154 // The top match must inline and score as highly as calculated verbatim. 2155 ASSERT_FALSE(matches.empty()); 2156 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 2157 matches[0].inline_autocompletion) << description; 2158 EXPECT_GE(matches[0].relevance, 1300) << description; 2159 2160 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 2161 size_t j = 0; 2162 // Ensure that the returned matches equal the expectations. 2163 for (; j < matches.size(); ++j) { 2164 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 2165 matches[j].contents) << description; 2166 EXPECT_EQ(cases[i].matches[j].from_keyword, 2167 matches[j].keyword == ASCIIToUTF16("k")) << description; 2168 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 2169 matches[j].allowed_to_be_default_match) << description; 2170 } 2171 // Ensure that no expected matches are missing. 2172 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 2173 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 2174 "Case # " << i << " " << description; 2175 } 2176} 2177 2178// This test is like KeywordFetcherSuggestRelevance above except it 2179// enables the field trial that causes the omnibox to be willing to 2180// reorder matches to guarantee the top result is a legal default 2181// match. This field trial causes SearchProvider to allow some 2182// constraints to be violated that it wouldn't normally because the 2183// omnibox will fix the problems later. 2184TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevanceWithReorder) { 2185 struct KeywordFetcherMatch { 2186 std::string contents; 2187 bool from_keyword; 2188 bool allowed_to_be_default_match; 2189 }; 2190 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false }; 2191 struct { 2192 const std::string json; 2193 const KeywordFetcherMatch matches[6]; 2194 const std::string inline_autocompletion; 2195 } cases[] = { 2196 // Ensure that suggest relevance scores reorder matches and that 2197 // the keyword verbatim (lacking a suggested verbatim score) beats 2198 // the default provider verbatim. 2199 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 2200 { { "a", true, true }, 2201 { "k a", false, false }, 2202 { "c", true, false }, 2203 { "b", true, false }, 2204 kEmptyMatch, kEmptyMatch }, 2205 std::string() }, 2206 // Again, check that relevance scores reorder matches, just this 2207 // time with navigation matches. This also checks that with 2208 // suggested relevance scores we allow multiple navsuggest results. 2209 // Note that navsuggest results that come from a keyword provider 2210 // are marked as not a keyword result. (They don't go to a 2211 // keyword search engine.) 2212 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[]," 2213 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2214 "\"google:suggestrelevance\":[1301, 1302, 1303]}]", 2215 { { "a", true, true }, 2216 { "d", true, false }, 2217 { "c.com", false, false }, 2218 { "b.com", false, false }, 2219 { "k a", false, false }, 2220 kEmptyMatch }, 2221 std::string() }, 2222 2223 // Without suggested relevance scores, we should only allow one 2224 // navsuggest result to be be displayed. 2225 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 2226 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 2227 { { "a", true, true }, 2228 { "b.com", false, false }, 2229 { "k a", false, false }, 2230 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2231 std::string() }, 2232 2233 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 2234 // Negative values will have no effect; the calculated value will be used. 2235 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 2236 "\"google:suggestrelevance\":[9998]}]", 2237 { { "a", true, true }, 2238 { "a1", true, true }, 2239 { "k a", false, false }, 2240 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2241 std::string() }, 2242 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 2243 "\"google:suggestrelevance\":[9999]}]", 2244 { { "a1", true, true }, 2245 { "a", true, true }, 2246 { "k a", false, false }, 2247 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2248 "1" }, 2249 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 2250 "\"google:suggestrelevance\":[9999]}]", 2251 { { "a1", true, true }, 2252 { "k a", false, false }, 2253 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2254 "1" }, 2255 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 2256 "\"google:suggestrelevance\":[9999]}]", 2257 { { "a1", true, true }, 2258 { "a", true, true }, 2259 { "k a", false, false }, 2260 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2261 "1" }, 2262 { "[\"a\",[\"http://a.com\"],[],[]," 2263 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2264 "\"google:verbatimrelevance\":9999," 2265 "\"google:suggestrelevance\":[9998]}]", 2266 { { "a", true, true }, 2267 { "a.com", false, false }, 2268 { "k a", false, false }, 2269 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2270 std::string() }, 2271 2272 // Ensure that both types of relevance scores reorder matches together. 2273 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 2274 "\"google:verbatimrelevance\":9998}]", 2275 { { "a1", true, true }, 2276 { "a", true, true }, 2277 { "a2", true, true }, 2278 { "k a", false, false }, 2279 kEmptyMatch, kEmptyMatch }, 2280 "1" }, 2281 2282 // Check that non-inlinable matches may be ranked as the highest result 2283 // if there is at least one inlineable match. 2284 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 2285 { { "b", true, false }, 2286 { "a", true, true }, 2287 { "k a", false, false }, 2288 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2289 std::string() }, 2290 { "[\"a\",[\"http://b.com\"],[],[]," 2291 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2292 "\"google:suggestrelevance\":[9999]}]", 2293 { { "b.com", false, false }, 2294 { "a", true, true }, 2295 { "k a", false, false }, 2296 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2297 std::string() }, 2298 // On the other hand, if there is no inlineable match, restore 2299 // the keyword verbatim score. 2300 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 2301 "\"google:verbatimrelevance\":0}]", 2302 { { "b", true, false }, 2303 { "a", true, true }, 2304 { "k a", false, false }, 2305 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2306 std::string() }, 2307 { "[\"a\",[\"http://b.com\"],[],[]," 2308 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2309 "\"google:suggestrelevance\":[9999]," 2310 "\"google:verbatimrelevance\":0}]", 2311 { { "b.com", false, false }, 2312 { "a", true, true }, 2313 { "k a", false, false }, 2314 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2315 std::string() }, 2316 2317 // The top result does not have to score as highly as calculated 2318 // verbatim. i.e., there are no minimum score restrictions in 2319 // this provider. 2320 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 2321 { { "a1", true, true }, 2322 { "k a", false, false }, 2323 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2324 "1" }, 2325 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 2326 { { "a1", true, true }, 2327 { "k a", false, false }, 2328 { "a", true, true }, 2329 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2330 "1" }, 2331 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 2332 "\"google:verbatimrelevance\":0}]", 2333 { { "k a", false, false }, 2334 { "a1", true, true }, 2335 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2336 "1" }, 2337 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 2338 "\"google:verbatimrelevance\":0}]", 2339 { 2340 { "k a", false, false }, 2341 { "a2", true, true }, 2342 { "a1", true, true }, 2343 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2344 "2" }, 2345 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 2346 "\"google:verbatimrelevance\":2}]", 2347 { { "k a", false, false }, 2348 { "a2", true, true }, 2349 { "a", true, true }, 2350 { "a1", true, true }, 2351 kEmptyMatch, kEmptyMatch }, 2352 "2" }, 2353 2354 // Ensure that all suggestions are considered, regardless of order. 2355 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 2356 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 2357 { { "a", true, true }, 2358 { "k a", false, false }, 2359 { "h", true, false }, 2360 { "g", true, false }, 2361 { "f", true, false }, 2362 { "e", true, false } }, 2363 std::string() }, 2364 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 2365 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 2366 "\"http://h.com\"],[],[]," 2367 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 2368 "\"NAVIGATION\", \"NAVIGATION\"," 2369 "\"NAVIGATION\", \"NAVIGATION\"," 2370 "\"NAVIGATION\"]," 2371 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 2372 { { "a", true, true }, 2373 { "k a", false, false }, 2374 { "h.com", false, false }, 2375 { "g.com", false, false }, 2376 { "f.com", false, false }, 2377 { "e.com", false, false } }, 2378 std::string() }, 2379 2380 // Ensure that incorrectly sized suggestion relevance lists are ignored. 2381 // Note that keyword suggestions by default (not in suggested relevance 2382 // mode) score more highly than the default verbatim. 2383 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 2384 { { "a", true, true }, 2385 { "a1", true, true }, 2386 { "a2", true, true }, 2387 { "k a", false, false }, 2388 kEmptyMatch, kEmptyMatch }, 2389 std::string() }, 2390 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 2391 { { "a", true, true }, 2392 { "a1", true, true }, 2393 { "k a", false, false }, 2394 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2395 std::string() }, 2396 // In this case, ignoring the suggested relevance scores means we keep 2397 // only one navsuggest result. 2398 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 2399 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 2400 "\"google:suggestrelevance\":[1]}]", 2401 { { "a", true, true }, 2402 { "a1.com", false, false }, 2403 { "k a", false, false }, 2404 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2405 std::string() }, 2406 { "[\"a\",[\"http://a1.com\"],[],[]," 2407 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2408 "\"google:suggestrelevance\":[9999, 1]}]", 2409 { { "a", true, true }, 2410 { "a1.com", false, false }, 2411 { "k a", false, false }, 2412 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2413 std::string() }, 2414 2415 // Ensure that all 'verbatim' results are merged with their maximum score. 2416 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 2417 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 2418 { { "a2", true, true }, 2419 { "a", true, true }, 2420 { "a1", true, true }, 2421 { "k a", false, false }, 2422 kEmptyMatch, kEmptyMatch }, 2423 "2" }, 2424 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 2425 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 2426 "\"google:verbatimrelevance\":0}]", 2427 { { "a2", true, true }, 2428 { "a", true, true }, 2429 { "a1", true, true }, 2430 { "k a", false, false }, 2431 kEmptyMatch, kEmptyMatch }, 2432 "2" }, 2433 2434 // Ensure that verbatim is always generated without other suggestions. 2435 // TODO(mpearson): Ensure the value of verbatimrelevance is respected 2436 // (except when suggested relevances are ignored). 2437 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 2438 { { "k a", false, false }, 2439 { "a", true, true }, 2440 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2441 std::string() }, 2442 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 2443 { { "a", true, true }, 2444 { "k a", false, false }, 2445 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2446 std::string() }, 2447 2448 // In reorder mode, navsuggestions will not need to be demoted (because 2449 // they are marked as not allowed to be default match and will be 2450 // reordered as necessary). 2451 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 2452 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 2453 "\"google:verbatimrelevance\":9990," 2454 "\"google:suggestrelevance\":[9998, 9999]}]", 2455 { { "a2.com", false, false }, 2456 { "a1.com", false, false }, 2457 { "a", true, true }, 2458 { "k a", false, false }, 2459 kEmptyMatch, kEmptyMatch }, 2460 std::string() }, 2461 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 2462 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 2463 "\"google:verbatimrelevance\":9990," 2464 "\"google:suggestrelevance\":[9999, 9998]}]", 2465 { { "a1.com", false, false }, 2466 { "a2.com", false, false }, 2467 { "a", true, true }, 2468 { "k a", false, false }, 2469 kEmptyMatch, kEmptyMatch }, 2470 std::string() }, 2471 { "[\"a\",[\"https://a/\"],[],[]," 2472 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2473 "\"google:suggestrelevance\":[9999]}]", 2474 { { "https://a", false, false }, 2475 { "a", true, true }, 2476 { "k a", false, false }, 2477 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 2478 std::string() }, 2479 // Check when navsuggest scores more than verbatim and there is query 2480 // suggestion but it scores lower. 2481 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2482 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2483 "\"google:verbatimrelevance\":9990," 2484 "\"google:suggestrelevance\":[9998, 9999, 1300]}]", 2485 { { "a2.com", false, false }, 2486 { "a1.com", false, false }, 2487 { "a", true, true }, 2488 { "a3", true, true }, 2489 { "k a", false, false }, 2490 kEmptyMatch }, 2491 std::string() }, 2492 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2493 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2494 "\"google:verbatimrelevance\":9990," 2495 "\"google:suggestrelevance\":[9999, 9998, 1300]}]", 2496 { { "a1.com", false, false }, 2497 { "a2.com", false, false }, 2498 { "a", true, true }, 2499 { "a3", true, true }, 2500 { "k a", false, false }, 2501 kEmptyMatch }, 2502 std::string() }, 2503 // Check when navsuggest scores more than a query suggestion. There is 2504 // a verbatim but it scores lower. 2505 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2506 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2507 "\"google:verbatimrelevance\":9990," 2508 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 2509 { { "a2.com", false, false }, 2510 { "a1.com", false, false }, 2511 { "a3", true, true }, 2512 { "a", true, true }, 2513 { "k a", false, false }, 2514 kEmptyMatch }, 2515 "3" }, 2516 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2517 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2518 "\"google:verbatimrelevance\":9990," 2519 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 2520 { { "a1.com", false, false }, 2521 { "a2.com", false, false }, 2522 { "a3", true, true }, 2523 { "a", true, true }, 2524 { "k a", false, false }, 2525 kEmptyMatch }, 2526 "3" }, 2527 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2528 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2529 "\"google:verbatimrelevance\":0," 2530 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 2531 { { "a2.com", false, false }, 2532 { "a1.com", false, false }, 2533 { "a3", true, true }, 2534 { "k a", false, false }, 2535 kEmptyMatch, kEmptyMatch }, 2536 "3" }, 2537 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2538 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2539 "\"google:verbatimrelevance\":0," 2540 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 2541 { { "a1.com", false, false }, 2542 { "a2.com", false, false }, 2543 { "a3", true, true }, 2544 { "k a", false, false }, 2545 kEmptyMatch, kEmptyMatch }, 2546 "3" }, 2547 // Check when there is neither verbatim nor a query suggestion that, 2548 // because we can't demote navsuggestions below a query suggestion, 2549 // we restore the keyword verbatim score. 2550 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 2551 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 2552 "\"google:verbatimrelevance\":0," 2553 "\"google:suggestrelevance\":[9998, 9999]}]", 2554 { { "a2.com", false, false }, 2555 { "a1.com", false, false }, 2556 { "a", true, true }, 2557 { "k a", false, false }, 2558 kEmptyMatch, kEmptyMatch }, 2559 std::string() }, 2560 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 2561 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 2562 "\"google:verbatimrelevance\":0," 2563 "\"google:suggestrelevance\":[9999, 9998]}]", 2564 { { "a1.com", false, false }, 2565 { "a2.com", false, false }, 2566 { "a", true, true }, 2567 { "k a", false, false }, 2568 kEmptyMatch, kEmptyMatch }, 2569 std::string() }, 2570 // More checks that everything works when it's not necessary to demote. 2571 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2572 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2573 "\"google:verbatimrelevance\":9990," 2574 "\"google:suggestrelevance\":[9997, 9998, 9999]}]", 2575 { { "a3", true, true }, 2576 { "a2.com", false, false }, 2577 { "a1.com", false, false }, 2578 { "a", true, true }, 2579 { "k a", false, false }, 2580 kEmptyMatch }, 2581 "3" }, 2582 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 2583 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 2584 "\"google:verbatimrelevance\":9990," 2585 "\"google:suggestrelevance\":[9998, 9997, 9999]}]", 2586 { { "a3", true, true }, 2587 { "a1.com", false, false }, 2588 { "a2.com", false, false }, 2589 { "a", true, true }, 2590 { "k a", false, false }, 2591 kEmptyMatch }, 2592 "3" }, 2593 }; 2594 2595 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2596 QueryForInput(ASCIIToUTF16("k a"), false, true); 2597 2598 // Set up a default fetcher with no results. 2599 net::TestURLFetcher* default_fetcher = 2600 test_factory_.GetFetcherByID( 2601 SearchProvider::kDefaultProviderURLFetcherID); 2602 ASSERT_TRUE(default_fetcher); 2603 default_fetcher->set_response_code(200); 2604 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 2605 default_fetcher = NULL; 2606 2607 // Set up a keyword fetcher with provided results. 2608 net::TestURLFetcher* keyword_fetcher = 2609 test_factory_.GetFetcherByID( 2610 SearchProvider::kKeywordProviderURLFetcherID); 2611 ASSERT_TRUE(keyword_fetcher); 2612 keyword_fetcher->set_response_code(200); 2613 keyword_fetcher->SetResponseString(cases[i].json); 2614 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 2615 keyword_fetcher = NULL; 2616 RunTillProviderDone(); 2617 2618 const std::string description = "for input with json=" + cases[i].json; 2619 const ACMatches& matches = provider_->matches(); 2620 ASSERT_FALSE(matches.empty()); 2621 // Find the first match that's allowed to be the default match and check 2622 // its inline_autocompletion. 2623 ACMatches::const_iterator it = FindDefaultMatch(matches); 2624 ASSERT_NE(matches.end(), it); 2625 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 2626 it->inline_autocompletion) << description; 2627 2628 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 2629 size_t j = 0; 2630 // Ensure that the returned matches equal the expectations. 2631 for (; j < matches.size(); ++j) { 2632 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 2633 matches[j].contents) << description; 2634 EXPECT_EQ(cases[i].matches[j].from_keyword, 2635 matches[j].keyword == ASCIIToUTF16("k")) << description; 2636 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 2637 matches[j].allowed_to_be_default_match) << description; 2638 } 2639 // Ensure that no expected matches are missing. 2640 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 2641 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 2642 "Case # " << i << " " << description; 2643 } 2644} 2645 2646TEST_F(SearchProviderTest, LocalAndRemoteRelevances) { 2647 // We hardcode the string "term1" below, so ensure that the search term that 2648 // got added to history already is that string. 2649 ASSERT_EQ(ASCIIToUTF16("term1"), term1_); 2650 base::string16 term = term1_.substr(0, term1_.length() - 1); 2651 2652 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2); 2653 profile_.BlockUntilHistoryProcessesPendingRequests(); 2654 2655 struct { 2656 const base::string16 input; 2657 const std::string json; 2658 const std::string matches[6]; 2659 } cases[] = { 2660 // The history results outscore the default verbatim score. term2 has more 2661 // visits so it outscores term1. The suggestions are still returned since 2662 // they're server-scored. 2663 { term, 2664 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 2665 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 2666 "\"google:suggestrelevance\":[1, 2, 3]}]", 2667 { "term2", "term1", "term", "a3", "a2", "a1" } }, 2668 // Because we already have three suggestions by the time we see the history 2669 // results, they don't get returned. 2670 { term, 2671 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 2672 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 2673 "\"google:verbatimrelevance\":1450," 2674 "\"google:suggestrelevance\":[1440, 1430, 1420]}]", 2675 { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } }, 2676 // If we only have two suggestions, we have room for a history result. 2677 { term, 2678 "[\"term\",[\"a1\", \"a2\"],[],[]," 2679 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," 2680 "\"google:verbatimrelevance\":1450," 2681 "\"google:suggestrelevance\":[1430, 1410]}]", 2682 { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } }, 2683 // If we have more than three suggestions, they should all be returned as 2684 // long as we have enough total space for them. 2685 { term, 2686 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 2687 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 2688 "\"google:verbatimrelevance\":1450," 2689 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]", 2690 { "term", "a1", "a2", "a3", "a4", kNotApplicable } }, 2691 { term, 2692 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[]," 2693 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"," 2694 "\"QUERY\", \"QUERY\"]," 2695 "\"google:verbatimrelevance\":1450," 2696 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]", 2697 { "term", "a1", "a2", "a3", "a4", "a5" } }, 2698 { term, 2699 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 2700 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 2701 "\"google:verbatimrelevance\":1450," 2702 "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]", 2703 { "term", "a1", "a2", "term2", "a3", "a4" } } 2704 }; 2705 2706 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2707 QueryForInput(cases[i].input, false, false); 2708 net::TestURLFetcher* fetcher = 2709 test_factory_.GetFetcherByID( 2710 SearchProvider::kDefaultProviderURLFetcherID); 2711 ASSERT_TRUE(fetcher); 2712 fetcher->set_response_code(200); 2713 fetcher->SetResponseString(cases[i].json); 2714 fetcher->delegate()->OnURLFetchComplete(fetcher); 2715 RunTillProviderDone(); 2716 2717 const std::string description = "for input with json=" + cases[i].json; 2718 const ACMatches& matches = provider_->matches(); 2719 2720 // Ensure no extra matches are present. 2721 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 2722 2723 size_t j = 0; 2724 // Ensure that the returned matches equal the expectations. 2725 for (; j < matches.size(); ++j) 2726 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), 2727 matches[j].contents) << description; 2728 // Ensure that no expected matches are missing. 2729 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 2730 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) << 2731 "Case # " << i << " " << description; 2732 } 2733} 2734 2735// Verifies suggest relevance behavior for URL input. 2736TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) { 2737 struct DefaultFetcherUrlInputMatch { 2738 const std::string match_contents; 2739 AutocompleteMatch::Type match_type; 2740 bool allowed_to_be_default_match; 2741 }; 2742 const DefaultFetcherUrlInputMatch kEmptyMatch = 2743 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false }; 2744 struct { 2745 const std::string input; 2746 const std::string json; 2747 const DefaultFetcherUrlInputMatch output[4]; 2748 } cases[] = { 2749 // Ensure topmost NAVIGATION matches are allowed for URL input. 2750 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[]," 2751 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2752 "\"google:suggestrelevance\":[9999]}]", 2753 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true }, 2754 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2755 kEmptyMatch, kEmptyMatch } }, 2756 { "a.com", "[\"a.com\",[\"https://a.com\"],[],[]," 2757 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2758 "\"google:suggestrelevance\":[9999]}]", 2759 { { "https://a.com", AutocompleteMatchType::NAVSUGGEST, true }, 2760 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2761 kEmptyMatch, kEmptyMatch } }, 2762 2763 // Ensure topmost SUGGEST matches are not allowed for URL input. 2764 // SearchProvider disregards search and verbatim suggested relevances. 2765 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 2766 "{\"google:suggestrelevance\":[9999]}]", 2767 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2768 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2769 kEmptyMatch, kEmptyMatch } }, 2770 { "a.com", "[\"a.com\",[\"a.com/a\"],[],[]," 2771 "{\"google:suggestrelevance\":[9999]}]", 2772 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2773 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2774 kEmptyMatch, kEmptyMatch } }, 2775 2776 // Ensure the fallback mechanism allows inlinable NAVIGATION matches. 2777 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 2778 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2779 "\"google:suggestrelevance\":[9999, 9998]}]", 2780 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true }, 2781 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2782 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2783 kEmptyMatch } }, 2784 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 2785 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2786 "\"google:suggestrelevance\":[9998, 9997]," 2787 "\"google:verbatimrelevance\":9999}]", 2788 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true }, 2789 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2790 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2791 kEmptyMatch } }, 2792 2793 // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches. 2794 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 2795 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2796 "\"google:suggestrelevance\":[9999, 9998]}]", 2797 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2798 { "abc.com", AutocompleteMatchType::NAVSUGGEST, false }, 2799 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2800 kEmptyMatch } }, 2801 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 2802 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2803 "\"google:suggestrelevance\":[9998, 9997]," 2804 "\"google:verbatimrelevance\":9999}]", 2805 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2806 { "abc.com", AutocompleteMatchType::NAVSUGGEST, false }, 2807 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2808 kEmptyMatch } }, 2809 }; 2810 2811 std::map<std::string, std::string> params; 2812 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) + 2813 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled; 2814 ASSERT_TRUE(chrome_variations::AssociateVariationParams( 2815 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); 2816 base::FieldTrialList::CreateFieldTrial( 2817 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); 2818 2819 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2820 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 2821 net::TestURLFetcher* fetcher = 2822 test_factory_.GetFetcherByID( 2823 SearchProvider::kDefaultProviderURLFetcherID); 2824 ASSERT_TRUE(fetcher); 2825 fetcher->set_response_code(200); 2826 fetcher->SetResponseString(cases[i].json); 2827 fetcher->delegate()->OnURLFetchComplete(fetcher); 2828 RunTillProviderDone(); 2829 2830 size_t j = 0; 2831 const ACMatches& matches = provider_->matches(); 2832 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].output)); 2833 // Ensure that the returned matches equal the expectations. 2834 for (; j < matches.size(); ++j) { 2835 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents), 2836 matches[j].contents); 2837 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type); 2838 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match, 2839 matches[j].allowed_to_be_default_match); 2840 } 2841 // Ensure that no expected matches are missing. 2842 for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) { 2843 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents); 2844 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES, 2845 cases[i].output[j].match_type); 2846 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match); 2847 } 2848 } 2849} 2850 2851// This test is like DefaultProviderSuggestRelevanceScoringUrlInput 2852// above except it enables the field trial that causes the omnibox to 2853// be willing to reorder matches to guarantee the top result is a 2854// legal default match. This field trial causes SearchProvider to 2855// allow some constraints to be violated that it wouldn't normally 2856// because the omnibox will fix the problems later. 2857TEST_F(SearchProviderTest, 2858 DefaultProviderSuggestRelevanceScoringUrlInputWithReorder) { 2859 struct DefaultFetcherUrlInputMatch { 2860 const std::string match_contents; 2861 AutocompleteMatch::Type match_type; 2862 bool allowed_to_be_default_match; 2863 }; 2864 const DefaultFetcherUrlInputMatch kEmptyMatch = 2865 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false }; 2866 struct { 2867 const std::string input; 2868 const std::string json; 2869 const DefaultFetcherUrlInputMatch output[4]; 2870 } cases[] = { 2871 // Ensure NAVIGATION matches are allowed to be listed first for URL 2872 // input regardless of whether the match is inlineable. Note that 2873 // non-inlineable matches should not be allowed to be the default match. 2874 { "a.com", "[\"a.com\",[\"http://b.com/\"],[],[]," 2875 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2876 "\"google:suggestrelevance\":[9999]}]", 2877 { { "b.com", AutocompleteMatchType::NAVSUGGEST, false }, 2878 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2879 kEmptyMatch, kEmptyMatch } }, 2880 { "a.com", "[\"a.com\",[\"https://b.com\"],[],[]," 2881 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2882 "\"google:suggestrelevance\":[9999]}]", 2883 { { "https://b.com", AutocompleteMatchType::NAVSUGGEST, false }, 2884 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2885 kEmptyMatch, kEmptyMatch } }, 2886 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[]," 2887 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2888 "\"google:suggestrelevance\":[9999]}]", 2889 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true }, 2890 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2891 kEmptyMatch, kEmptyMatch } }, 2892 { "a.com", "[\"a.com\",[\"https://a.com\"],[],[]," 2893 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2894 "\"google:suggestrelevance\":[9999]}]", 2895 { { "https://a.com", AutocompleteMatchType::NAVSUGGEST, true }, 2896 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2897 kEmptyMatch, kEmptyMatch } }, 2898 2899 // Ensure topmost inlineable SUGGEST matches are NOT allowed for URL 2900 // input. SearchProvider disregards search and verbatim suggested 2901 // relevances. 2902 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 2903 "{\"google:suggestrelevance\":[9999]}]", 2904 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2905 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2906 kEmptyMatch, kEmptyMatch } }, 2907 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 2908 "{\"google:suggestrelevance\":[9999]}]", 2909 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2910 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2911 kEmptyMatch, kEmptyMatch } }, 2912 2913 // Ensure the fallback mechanism allows inlinable NAVIGATION matches. 2914 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[]," 2915 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2916 "\"google:suggestrelevance\":[9999, 9998]}]", 2917 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true }, 2918 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2919 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2920 kEmptyMatch } }, 2921 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[]," 2922 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2923 "\"google:suggestrelevance\":[9998, 9997]," 2924 "\"google:verbatimrelevance\":9999}]", 2925 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true }, 2926 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2927 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2928 kEmptyMatch } }, 2929 2930 // Ensure topmost non-inlineable SUGGEST matches are allowed for URL 2931 // input assuming the top inlineable match is not a query (i.e., is a 2932 // NAVSUGGEST). 2933 { "a.com", "[\"a.com\",[\"info\"],[],[]," 2934 "{\"google:suggestrelevance\":[9999]}]", 2935 { { "info", AutocompleteMatchType::SEARCH_SUGGEST, false }, 2936 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2937 kEmptyMatch, kEmptyMatch } }, 2938 { "a.com", "[\"a.com\",[\"info\"],[],[]," 2939 "{\"google:suggestrelevance\":[9999]}]", 2940 { { "info", AutocompleteMatchType::SEARCH_SUGGEST, false }, 2941 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2942 kEmptyMatch, kEmptyMatch } }, 2943 }; 2944 2945 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2946 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 2947 net::TestURLFetcher* fetcher = 2948 test_factory_.GetFetcherByID( 2949 SearchProvider::kDefaultProviderURLFetcherID); 2950 ASSERT_TRUE(fetcher); 2951 fetcher->set_response_code(200); 2952 fetcher->SetResponseString(cases[i].json); 2953 fetcher->delegate()->OnURLFetchComplete(fetcher); 2954 RunTillProviderDone(); 2955 2956 size_t j = 0; 2957 const ACMatches& matches = provider_->matches(); 2958 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].output)); 2959 // Ensure that the returned matches equal the expectations. 2960 for (; j < matches.size(); ++j) { 2961 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents), 2962 matches[j].contents); 2963 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type); 2964 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match, 2965 matches[j].allowed_to_be_default_match); 2966 } 2967 // Ensure that no expected matches are missing. 2968 for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) { 2969 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents); 2970 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES, 2971 cases[i].output[j].match_type); 2972 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match); 2973 } 2974 } 2975} 2976 2977// A basic test that verifies the field trial triggered parsing logic. 2978TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) { 2979 QueryForInput(ASCIIToUTF16("foo"), false, false); 2980 2981 // Make sure the default providers suggest service was queried. 2982 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 2983 SearchProvider::kDefaultProviderURLFetcherID); 2984 ASSERT_TRUE(fetcher); 2985 2986 // Tell the SearchProvider the suggest query is done. 2987 fetcher->set_response_code(200); 2988 fetcher->SetResponseString( 2989 "[\"foo\",[\"foo bar\"],[\"\"],[]," 2990 "{\"google:suggesttype\":[\"QUERY\"]," 2991 "\"google:fieldtrialtriggered\":true}]"); 2992 fetcher->delegate()->OnURLFetchComplete(fetcher); 2993 fetcher = NULL; 2994 2995 // Run till the history results complete. 2996 RunTillProviderDone(); 2997 2998 { 2999 // Check for the match and field trial triggered bits. 3000 AutocompleteMatch match; 3001 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match)); 3002 ProvidersInfo providers_info; 3003 provider_->AddProviderInfo(&providers_info); 3004 ASSERT_EQ(1U, providers_info.size()); 3005 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 3006 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size()); 3007 } 3008 { 3009 // Reset the session and check that bits are reset. 3010 provider_->ResetSession(); 3011 ProvidersInfo providers_info; 3012 provider_->AddProviderInfo(&providers_info); 3013 ASSERT_EQ(1U, providers_info.size()); 3014 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 3015 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size()); 3016 } 3017} 3018 3019// Verifies inline autocompletion of navigational results. 3020TEST_F(SearchProviderTest, NavigationInline) { 3021 struct { 3022 const std::string input; 3023 const std::string url; 3024 // Test the expected fill_into_edit, which may drop "http://". 3025 // Some cases do not trim "http://" to match from the start of the scheme. 3026 const std::string fill_into_edit; 3027 const std::string inline_autocompletion; 3028 const bool allowed_to_be_default_match_in_regular_mode; 3029 const bool allowed_to_be_default_match_in_prevent_inline_mode; 3030 } cases[] = { 3031 // Do not inline matches that do not contain the input; trim http as needed. 3032 { "x", "http://www.abc.com", 3033 "www.abc.com", std::string(), false, false }, 3034 { "https:", "http://www.abc.com", 3035 "www.abc.com", std::string(), false, false }, 3036 { "http://www.abc.com/a", "http://www.abc.com", 3037 "http://www.abc.com", std::string(), false, 3038 false }, 3039 { "http://www.abc.com", "https://www.abc.com", 3040 "https://www.abc.com", std::string(), false, 3041 false }, 3042 { "http://abc.com", "ftp://abc.com", 3043 "ftp://abc.com", std::string(), false, 3044 false }, 3045 { "https://www.abc.com", "http://www.abc.com", 3046 "www.abc.com", std::string(), false, 3047 false }, 3048 { "ftp://abc.com", "http://abc.com", 3049 "abc.com", std::string(), false, 3050 false }, 3051 3052 // Do not inline matches with invalid input prefixes; trim http as needed. 3053 { "ttp", "http://www.abc.com", 3054 "www.abc.com", std::string(), false, false }, 3055 { "://w", "http://www.abc.com", 3056 "www.abc.com", std::string(), false, false }, 3057 { "ww.", "http://www.abc.com", 3058 "www.abc.com", std::string(), false, false }, 3059 { ".ab", "http://www.abc.com", 3060 "www.abc.com", std::string(), false, false }, 3061 { "bc", "http://www.abc.com", 3062 "www.abc.com", std::string(), false, false }, 3063 { ".com", "http://www.abc.com", 3064 "www.abc.com", std::string(), false, false }, 3065 3066 // Do not inline matches that omit input domain labels; trim http as needed. 3067 { "www.a", "http://a.com", 3068 "a.com", std::string(), false, false }, 3069 { "http://www.a", "http://a.com", 3070 "http://a.com", std::string(), false, false }, 3071 { "www.a", "ftp://a.com", 3072 "ftp://a.com", std::string(), false, false }, 3073 { "ftp://www.a", "ftp://a.com", 3074 "ftp://a.com", std::string(), false, false }, 3075 3076 // Input matching but with nothing to inline will not yield an offset, but 3077 // will be allowed to be default. 3078 { "abc.com", "http://www.abc.com", 3079 "www.abc.com", std::string(), true, true }, 3080 { "abc.com/", "http://www.abc.com", 3081 "www.abc.com", std::string(), true, true }, 3082 { "http://www.abc.com", "http://www.abc.com", 3083 "http://www.abc.com", std::string(), true, true }, 3084 { "http://www.abc.com/", "http://www.abc.com", 3085 "http://www.abc.com", std::string(), true, true }, 3086 3087 // Inline matches when the input is a leading substring of the scheme. 3088 { "h", "http://www.abc.com", 3089 "http://www.abc.com", "ttp://www.abc.com", true, false }, 3090 { "http", "http://www.abc.com", 3091 "http://www.abc.com", "://www.abc.com", true, false }, 3092 3093 // Inline matches when the input is a leading substring of the full URL. 3094 { "http:", "http://www.abc.com", 3095 "http://www.abc.com", "//www.abc.com", true, false }, 3096 { "http://w", "http://www.abc.com", 3097 "http://www.abc.com", "ww.abc.com", true, false }, 3098 { "http://www.", "http://www.abc.com", 3099 "http://www.abc.com", "abc.com", true, false }, 3100 { "http://www.ab", "http://www.abc.com", 3101 "http://www.abc.com", "c.com", true, false }, 3102 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 3103 "http://www.abc.com/path/file.htm?q=x#foo", 3104 "ath/file.htm?q=x#foo", 3105 true, false }, 3106 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 3107 "http://abc.com/path/file.htm?q=x#foo", 3108 "ath/file.htm?q=x#foo", 3109 true, false}, 3110 3111 // Inline matches with valid URLPrefixes; only trim "http://". 3112 { "w", "http://www.abc.com", 3113 "www.abc.com", "ww.abc.com", true, false }, 3114 { "www.a", "http://www.abc.com", 3115 "www.abc.com", "bc.com", true, false }, 3116 { "abc", "http://www.abc.com", 3117 "www.abc.com", ".com", true, false }, 3118 { "abc.c", "http://www.abc.com", 3119 "www.abc.com", "om", true, false }, 3120 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 3121 "www.abc.com/path/file.htm?q=x#foo", 3122 "ath/file.htm?q=x#foo", 3123 true, false }, 3124 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 3125 "abc.com/path/file.htm?q=x#foo", 3126 "ath/file.htm?q=x#foo", 3127 true, false }, 3128 3129 // Inline matches using the maximal URLPrefix components. 3130 { "h", "http://help.com", 3131 "help.com", "elp.com", true, false }, 3132 { "http", "http://http.com", 3133 "http.com", ".com", true, false }, 3134 { "h", "http://www.help.com", 3135 "www.help.com", "elp.com", true, false }, 3136 { "http", "http://www.http.com", 3137 "www.http.com", ".com", true, false }, 3138 { "w", "http://www.www.com", 3139 "www.www.com", "ww.com", true, false }, 3140 3141 // Test similar behavior for the ftp and https schemes. 3142 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 3143 "ftp://www.abc.com/path/file.htm?q=x#foo", 3144 "c.com/path/file.htm?q=x#foo", true, false }, 3145 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 3146 "ftp://www.abc.com/path/file.htm?q=x#foo", 3147 "c.com/path/file.htm?q=x#foo", true, false }, 3148 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 3149 "ftp://www.abc.com/path/file.htm?q=x#foo", 3150 "c.com/path/file.htm?q=x#foo", true, false }, 3151 { "ab", "ftp://abc.com/path/file.htm?q=x#foo", 3152 "ftp://abc.com/path/file.htm?q=x#foo", 3153 "c.com/path/file.htm?q=x#foo", true, false }, 3154 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 3155 "https://www.abc.com/path/file.htm?q=x#foo", 3156 "c.com/path/file.htm?q=x#foo", 3157 true, false }, 3158 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 3159 "https://www.abc.com/path/file.htm?q=x#foo", 3160 "c.com/path/file.htm?q=x#foo", true, false }, 3161 { "ab", "https://www.abc.com/path/file.htm?q=x#foo", 3162 "https://www.abc.com/path/file.htm?q=x#foo", 3163 "c.com/path/file.htm?q=x#foo", true, false }, 3164 { "ab", "https://abc.com/path/file.htm?q=x#foo", 3165 "https://abc.com/path/file.htm?q=x#foo", 3166 "c.com/path/file.htm?q=x#foo", true, false }, 3167 3168 // Forced query input should inline and retain the "?" prefix. 3169 { "?http://www.ab", "http://www.abc.com", 3170 "?http://www.abc.com", "c.com", true, false }, 3171 { "?www.ab", "http://www.abc.com", 3172 "?www.abc.com", "c.com", true, false }, 3173 { "?ab", "http://www.abc.com", 3174 "?www.abc.com", "c.com", true, false }, 3175 { "?abc.com", "http://www.abc.com", 3176 "?www.abc.com", "", true, true }, 3177 }; 3178 3179 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 3180 // First test regular mode. 3181 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 3182 AutocompleteMatch match( 3183 provider_->NavigationToMatch(SearchProvider::NavigationResult( 3184 *provider_.get(), GURL(cases[i].url), base::string16(), false, 0, 3185 false, ASCIIToUTF16(cases[i].input), std::string()))); 3186 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 3187 match.inline_autocompletion); 3188 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit); 3189 EXPECT_EQ(cases[i].allowed_to_be_default_match_in_regular_mode, 3190 match.allowed_to_be_default_match); 3191 3192 // Then test prevent-inline-autocomplete mode. 3193 QueryForInput(ASCIIToUTF16(cases[i].input), true, false); 3194 AutocompleteMatch match_prevent_inline( 3195 provider_->NavigationToMatch(SearchProvider::NavigationResult( 3196 *provider_.get(), GURL(cases[i].url), base::string16(), false, 0, 3197 false, ASCIIToUTF16(cases[i].input), std::string()))); 3198 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 3199 match_prevent_inline.inline_autocompletion); 3200 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), 3201 match_prevent_inline.fill_into_edit); 3202 EXPECT_EQ(cases[i].allowed_to_be_default_match_in_prevent_inline_mode, 3203 match_prevent_inline.allowed_to_be_default_match); 3204 } 3205} 3206 3207// Verifies that "http://" is not trimmed for input that is a leading substring. 3208TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) { 3209 const base::string16 input(ASCIIToUTF16("ht")); 3210 const base::string16 url(ASCIIToUTF16("http://a.com")); 3211 const SearchProvider::NavigationResult result( 3212 *provider_.get(), GURL(url), base::string16(), false, 0, false, 3213 input, std::string()); 3214 3215 // Check the offset and strings when inline autocompletion is allowed. 3216 QueryForInput(input, false, false); 3217 AutocompleteMatch match_inline(provider_->NavigationToMatch(result)); 3218 EXPECT_EQ(url, match_inline.fill_into_edit); 3219 EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion); 3220 EXPECT_TRUE(match_inline.allowed_to_be_default_match); 3221 EXPECT_EQ(url, match_inline.contents); 3222 3223 // Check the same strings when inline autocompletion is prevented. 3224 QueryForInput(input, true, false); 3225 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result)); 3226 EXPECT_EQ(url, match_prevent.fill_into_edit); 3227 EXPECT_FALSE(match_prevent.allowed_to_be_default_match); 3228 EXPECT_EQ(url, match_prevent.contents); 3229} 3230 3231// Verifies that input "w" marks a more significant domain label than "www.". 3232TEST_F(SearchProviderTest, NavigationInlineDomainClassify) { 3233 QueryForInput(ASCIIToUTF16("w"), false, false); 3234 AutocompleteMatch match( 3235 provider_->NavigationToMatch(SearchProvider::NavigationResult( 3236 *provider_.get(), GURL("http://www.wow.com"), base::string16(), false, 3237 0, false, ASCIIToUTF16("w"), std::string()))); 3238 EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion); 3239 EXPECT_TRUE(match.allowed_to_be_default_match); 3240 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit); 3241 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents); 3242 3243 // Ensure that the match for input "w" is marked on "wow" and not "www". 3244 ASSERT_EQ(3U, match.contents_class.size()); 3245 EXPECT_EQ(0U, match.contents_class[0].offset); 3246 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 3247 match.contents_class[0].style); 3248 EXPECT_EQ(4U, match.contents_class[1].offset); 3249 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL | 3250 AutocompleteMatch::ACMatchClassification::MATCH, 3251 match.contents_class[1].style); 3252 EXPECT_EQ(5U, match.contents_class[2].offset); 3253 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 3254 match.contents_class[2].style); 3255} 3256 3257TEST_F(SearchProviderTest, RemoveStaleResultsTest) { 3258 // TODO(mpearson): Consider expanding this test to explicitly cover 3259 // testing staleness for keyword results. 3260 struct { 3261 const std::string omnibox_input; 3262 const int verbatim_relevance; 3263 // These cached suggestions should already be sorted. 3264 // The particular number 5 as the length of the array is 3265 // unimportant; it's merely enough cached results to fully test 3266 // the functioning of RemoveAllStaleResults(). 3267 struct { 3268 const std::string suggestion; 3269 const bool is_navigation_result; 3270 const int relevance; 3271 // |expect_match| is true if this result should survive 3272 // RemoveAllStaleResults() filtering against |omnibox_input| below. 3273 const bool expect_match; 3274 } results[5]; 3275 } cases[] = { 3276 // Simple case: multiple query suggestions and no navsuggestions. 3277 // All query suggestions score less than search-what-you-typed and 3278 // thus none should be filtered because none will appear first. 3279 { "x", 1300, 3280 { { "food", false, 1299, true }, 3281 { "foobar", false, 1298, true }, 3282 { "crazy", false, 1297, true }, 3283 { "friend", false, 1296, true }, 3284 { kNotApplicable, false, 0, false } } }, 3285 3286 // Similarly simple cases, but the query suggestion appears first. 3287 { "f", 1200, 3288 { { "food", false, 1299, true }, 3289 { "foobar", false, 1298, true }, 3290 { "crazy", false, 1297, true }, 3291 { "friend", false, 1296, true }, 3292 { kNotApplicable, false, 0, false } } }, 3293 { "c", 1200, 3294 { { "food", false, 1299, false }, 3295 { "foobar", false, 1298, false }, 3296 { "crazy", false, 1297, true }, 3297 { "friend", false, 1296, true }, 3298 { kNotApplicable, false, 0, false } } }, 3299 { "x", 1200, 3300 { { "food", false, 1299, false }, 3301 { "foobar", false, 1298, false }, 3302 { "crazy", false, 1297, false }, 3303 { "friend", false, 1296, false }, 3304 { kNotApplicable, false, 0, false } } }, 3305 3306 // The same sort of cases, just using a mix of queries and navsuggestions. 3307 { "x", 1300, 3308 { { "http://food.com/", true, 1299, true }, 3309 { "foobar", false, 1298, true }, 3310 { "http://crazy.com/", true, 1297, true }, 3311 { "friend", false, 1296, true }, 3312 { "http://friend.com/", true, 1295, true } } }, 3313 { "f", 1200, 3314 { { "http://food.com/", true, 1299, true }, 3315 { "foobar", false, 1298, true }, 3316 { "http://crazy.com/", true, 1297, true }, 3317 { "friend", false, 1296, true }, 3318 { "http://friend.com/", true, 1295, true } } }, 3319 { "c", 1200, 3320 { { "http://food.com/", true, 1299, false }, 3321 { "foobar", false, 1298, false }, 3322 { "http://crazy.com/", true, 1297, true }, 3323 { "friend", false, 1296, true }, 3324 { "http://friend.com/", true, 1295, true } } }, 3325 { "x", 1200, 3326 { { "http://food.com/", true, 1299, false }, 3327 { "foobar", false, 1298, false }, 3328 { "http://crazy.com/", true, 1297, false }, 3329 { "friend", false, 1296, false }, 3330 { "http://friend.com/", true, 1295, false } } }, 3331 3332 // Run the three tests immediately above again, just with verbatim 3333 // suppressed. Note that in the last case, all results are filtered. 3334 // Because verbatim is also suppressed, SearchProvider will realize 3335 // in UpdateMatches() that it needs to restore verbatim to fulfill 3336 // its constraints. This restoration does not happen in 3337 // RemoveAllStaleResults() and hence is not tested here. This restoration 3338 // is tested in the DefaultFetcherSuggestRelevance test. 3339 { "f", 0, 3340 { { "http://food.com/", true, 1299, true }, 3341 { "foobar", false, 1298, true }, 3342 { "http://crazy.com/", true, 1297, true }, 3343 { "friend", false, 1296, true }, 3344 { "http://friend.com/", true, 1295, true } } }, 3345 { "c", 0, 3346 { { "http://food.com/", true, 1299, false }, 3347 { "foobar", false, 1298, false }, 3348 { "http://crazy.com/", true, 1297, true }, 3349 { "friend", false, 1296, true }, 3350 { "http://friend.com/", true, 1295, true } } }, 3351 { "x", 0, 3352 { { "http://food.com/", true, 1299, false }, 3353 { "foobar", false, 1298, false }, 3354 { "http://crazy.com/", true, 1297, false }, 3355 { "friend", false, 1296, false }, 3356 { "http://friend.com/", true, 1295, false } } }, 3357 3358 // The same sort of tests again, just with verbatim with a score 3359 // that would place it in between other suggestions. 3360 { "f", 1290, 3361 { { "http://food.com/", true, 1299, true }, 3362 { "foobar", false, 1288, true }, 3363 { "http://crazy.com/", true, 1277, true }, 3364 { "friend", false, 1266, true }, 3365 { "http://friend.com/", true, 1255, true } } }, 3366 { "c", 1290, 3367 { { "http://food.com/", true, 1299, false }, 3368 { "foobar", false, 1288, true }, 3369 { "http://crazy.com/", true, 1277, true }, 3370 { "friend", false, 1266, true }, 3371 { "http://friend.com/", true, 1255, true } } }, 3372 { "c", 1270, 3373 { { "http://food.com/", true, 1299, false }, 3374 { "foobar", false, 1288, false }, 3375 { "http://crazy.com/", true, 1277, true }, 3376 { "friend", false, 1266, true }, 3377 { "http://friend.com/", true, 1255, true } } }, 3378 { "x", 1280, 3379 { { "http://food.com/", true, 1299, false }, 3380 { "foobar", false, 1288, false }, 3381 { "http://crazy.com/", true, 1277, true }, 3382 { "friend", false, 1266, true }, 3383 { "http://friend.com/", true, 1255, true } } }, 3384 }; 3385 3386 std::map<std::string, std::string> params; 3387 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) + 3388 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleDisabled; 3389 ASSERT_TRUE(chrome_variations::AssociateVariationParams( 3390 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); 3391 base::FieldTrialList::CreateFieldTrial( 3392 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); 3393 3394 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 3395 // Initialize cached results for this test case. 3396 provider_->default_results_.verbatim_relevance = 3397 cases[i].verbatim_relevance; 3398 provider_->default_results_.navigation_results.clear(); 3399 provider_->default_results_.suggest_results.clear(); 3400 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) { 3401 const std::string& suggestion = cases[i].results[j].suggestion; 3402 if (suggestion == kNotApplicable) 3403 break; 3404 if (cases[i].results[j].is_navigation_result) { 3405 provider_->default_results_.navigation_results.push_back( 3406 SearchProvider::NavigationResult( 3407 *provider_.get(), GURL(suggestion), base::string16(), false, 3408 cases[i].results[j].relevance, false, 3409 ASCIIToUTF16(cases[i].omnibox_input), std::string())); 3410 } else { 3411 provider_->default_results_.suggest_results.push_back( 3412 SearchProvider::SuggestResult( 3413 ASCIIToUTF16(suggestion), AutocompleteMatchType::SEARCH_SUGGEST, 3414 ASCIIToUTF16(suggestion), base::string16(), base::string16(), 3415 std::string(), std::string(), false, 3416 cases[i].results[j].relevance, false, false, 3417 ASCIIToUTF16(cases[i].omnibox_input))); 3418 } 3419 } 3420 3421 provider_->input_ = AutocompleteInput( 3422 ASCIIToUTF16(cases[i].omnibox_input), base::string16::npos, 3423 base::string16(), GURL(), AutocompleteInput::INVALID_SPEC, false, false, 3424 true, AutocompleteInput::ALL_MATCHES); 3425 provider_->RemoveAllStaleResults(); 3426 3427 // Check cached results. 3428 SearchProvider::SuggestResults::const_iterator sug_it = 3429 provider_->default_results_.suggest_results.begin(); 3430 const SearchProvider::SuggestResults::const_iterator sug_end = 3431 provider_->default_results_.suggest_results.end(); 3432 SearchProvider::NavigationResults::const_iterator nav_it = 3433 provider_->default_results_.navigation_results.begin(); 3434 const SearchProvider::NavigationResults::const_iterator nav_end = 3435 provider_->default_results_.navigation_results.end(); 3436 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) { 3437 const std::string& suggestion = cases[i].results[j].suggestion; 3438 if (suggestion == kNotApplicable) 3439 continue; 3440 if (!cases[i].results[j].expect_match) 3441 continue; 3442 if (cases[i].results[j].is_navigation_result) { 3443 ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion; 3444 EXPECT_EQ(suggestion, nav_it->url().spec()); 3445 ++nav_it; 3446 } else { 3447 ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion; 3448 EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion()); 3449 ++sug_it; 3450 } 3451 } 3452 EXPECT_EQ(sug_end, sug_it); 3453 EXPECT_EQ(nav_end, nav_it); 3454 } 3455} 3456 3457#if !defined(OS_WIN) 3458// Verify entity suggestion parsing. 3459TEST_F(SearchProviderTest, ParseEntitySuggestion) { 3460 struct Match { 3461 std::string contents; 3462 std::string description; 3463 std::string query_params; 3464 std::string fill_into_edit; 3465 AutocompleteMatchType::Type type; 3466 }; 3467 const Match kEmptyMatch = { 3468 kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable, 3469 AutocompleteMatchType::NUM_TYPES}; 3470 3471 struct { 3472 const std::string input_text; 3473 const std::string response_json; 3474 const Match matches[5]; 3475 } cases[] = { 3476 // A query and an entity suggestion with different search terms. 3477 { "x", 3478 "[\"x\",[\"xy\", \"yy\"],[\"\",\"\"],[]," 3479 " {\"google:suggestdetail\":[{}," 3480 " {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}]," 3481 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]", 3482 { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 3483 { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST }, 3484 { "xy", "A", "p=v", "yy", 3485 AutocompleteMatchType::SEARCH_SUGGEST_ENTITY }, 3486 kEmptyMatch, 3487 kEmptyMatch 3488 }, 3489 }, 3490 // A query and an entity suggestion with same search terms. 3491 { "x", 3492 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[]," 3493 " {\"google:suggestdetail\":[{}," 3494 " {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}]," 3495 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]", 3496 { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 3497 { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST }, 3498 { "xy", "A", "p=v", "xy", 3499 AutocompleteMatchType::SEARCH_SUGGEST_ENTITY }, 3500 kEmptyMatch, 3501 kEmptyMatch 3502 }, 3503 }, 3504 }; 3505 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 3506 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false); 3507 3508 // Set up a default fetcher with provided results. 3509 net::TestURLFetcher* fetcher = 3510 test_factory_.GetFetcherByID( 3511 SearchProvider::kDefaultProviderURLFetcherID); 3512 ASSERT_TRUE(fetcher); 3513 fetcher->set_response_code(200); 3514 fetcher->SetResponseString(cases[i].response_json); 3515 fetcher->delegate()->OnURLFetchComplete(fetcher); 3516 3517 RunTillProviderDone(); 3518 3519 const ACMatches& matches = provider_->matches(); 3520 ASSERT_FALSE(matches.empty()); 3521 3522 SCOPED_TRACE("for input with json = " + cases[i].response_json); 3523 3524 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 3525 size_t j = 0; 3526 // Ensure that the returned matches equal the expectations. 3527 for (; j < matches.size(); ++j) { 3528 const Match& match = cases[i].matches[j]; 3529 SCOPED_TRACE(" and match index: " + base::IntToString(j)); 3530 EXPECT_EQ(match.contents, 3531 base::UTF16ToUTF8(matches[j].contents)); 3532 EXPECT_EQ(match.description, 3533 base::UTF16ToUTF8(matches[j].description)); 3534 EXPECT_EQ(match.query_params, 3535 matches[j].search_terms_args->suggest_query_params); 3536 EXPECT_EQ(match.fill_into_edit, 3537 base::UTF16ToUTF8(matches[j].fill_into_edit)); 3538 EXPECT_EQ(match.type, matches[j].type); 3539 } 3540 // Ensure that no expected matches are missing. 3541 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) { 3542 SCOPED_TRACE(" and match index: " + base::IntToString(j)); 3543 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable); 3544 EXPECT_EQ(cases[i].matches[j].description, kNotApplicable); 3545 EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable); 3546 EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable); 3547 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES); 3548 } 3549 } 3550} 3551#endif // !defined(OS_WIN) 3552 3553 3554// A basic test that verifies the prefetch metadata parsing logic. 3555TEST_F(SearchProviderTest, PrefetchMetadataParsing) { 3556 struct Match { 3557 std::string contents; 3558 bool allowed_to_be_prefetched; 3559 AutocompleteMatchType::Type type; 3560 bool from_keyword; 3561 }; 3562 const Match kEmptyMatch = { kNotApplicable, 3563 false, 3564 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 3565 false }; 3566 3567 struct { 3568 const std::string input_text; 3569 bool prefer_keyword_provider_results; 3570 const std::string default_provider_response_json; 3571 const std::string keyword_provider_response_json; 3572 const Match matches[5]; 3573 } cases[] = { 3574 // Default provider response does not have prefetch details. Ensure that the 3575 // suggestions are not marked as prefetch query. 3576 { "a", 3577 false, 3578 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 3579 std::string(), 3580 { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 3581 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, 3582 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, 3583 kEmptyMatch, 3584 kEmptyMatch 3585 }, 3586 }, 3587 // Ensure that default provider suggest response prefetch details are 3588 // parsed and recorded in AutocompleteMatch. 3589 { "ab", 3590 false, 3591 "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[]," 3592 "{\"google:clientdata\":{\"phi\": 0}," 3593 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"]," 3594 "\"google:suggestrelevance\":[999, 12, 1]}]", 3595 std::string(), 3596 { { "ab", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 3597 { "abc", true, AutocompleteMatchType::SEARCH_SUGGEST, false }, 3598 { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false }, 3599 { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false }, 3600 kEmptyMatch 3601 }, 3602 }, 3603 // Default provider suggest response has prefetch details. 3604 // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for 3605 // the same query string. Ensure that the prefetch details from 3606 // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match. 3607 { "ab", 3608 false, 3609 "[\"ab\",[\"ab\", \"http://ab.com\"],[],[]," 3610 "{\"google:clientdata\":{\"phi\": 0}," 3611 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 3612 "\"google:suggestrelevance\":[99, 98]}]", 3613 std::string(), 3614 { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 3615 {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false }, 3616 kEmptyMatch, 3617 kEmptyMatch, 3618 kEmptyMatch 3619 }, 3620 }, 3621 // Default provider response has prefetch details. We prefer keyword 3622 // provider results. Ensure that prefetch bit for a suggestion from the 3623 // default search provider does not get copied onto a higher-scoring match 3624 // for the same query string from the keyword provider. 3625 { "k a", 3626 true, 3627 "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0}," 3628 "\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," 3629 "\"google:suggestrelevance\":[9, 12]}]", 3630 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 3631 { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true}, 3632 { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 3633 { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, 3634 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true }, 3635 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true } 3636 }, 3637 } 3638 }; 3639 3640 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 3641 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, 3642 cases[i].prefer_keyword_provider_results); 3643 3644 // Set up a default fetcher with provided results. 3645 net::TestURLFetcher* fetcher = 3646 test_factory_.GetFetcherByID( 3647 SearchProvider::kDefaultProviderURLFetcherID); 3648 ASSERT_TRUE(fetcher); 3649 fetcher->set_response_code(200); 3650 fetcher->SetResponseString(cases[i].default_provider_response_json); 3651 fetcher->delegate()->OnURLFetchComplete(fetcher); 3652 3653 if (cases[i].prefer_keyword_provider_results) { 3654 // Set up a keyword fetcher with provided results. 3655 net::TestURLFetcher* keyword_fetcher = 3656 test_factory_.GetFetcherByID( 3657 SearchProvider::kKeywordProviderURLFetcherID); 3658 ASSERT_TRUE(keyword_fetcher); 3659 keyword_fetcher->set_response_code(200); 3660 keyword_fetcher->SetResponseString( 3661 cases[i].keyword_provider_response_json); 3662 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 3663 keyword_fetcher = NULL; 3664 } 3665 3666 RunTillProviderDone(); 3667 3668 const std::string description = 3669 "for input with json =" + cases[i].default_provider_response_json; 3670 const ACMatches& matches = provider_->matches(); 3671 // The top match must inline and score as highly as calculated verbatim. 3672 ASSERT_FALSE(matches.empty()); 3673 EXPECT_GE(matches[0].relevance, 1300); 3674 3675 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 3676 // Ensure that the returned matches equal the expectations. 3677 for (size_t j = 0; j < matches.size(); ++j) { 3678 SCOPED_TRACE(description); 3679 EXPECT_EQ(cases[i].matches[j].contents, 3680 base::UTF16ToUTF8(matches[j].contents)); 3681 EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched, 3682 SearchProvider::ShouldPrefetch(matches[j])); 3683 EXPECT_EQ(cases[i].matches[j].type, matches[j].type); 3684 EXPECT_EQ(cases[i].matches[j].from_keyword, 3685 matches[j].keyword == ASCIIToUTF16("k")); 3686 } 3687 } 3688} 3689 3690TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_InvalidResponse) { 3691 ClearAllResults(); 3692 3693 std::string input_str("abc"); 3694 QueryForInput(ASCIIToUTF16(input_str), false, false); 3695 3696 // Set up a default fetcher with provided results. 3697 net::TestURLFetcher* fetcher = 3698 test_factory_.GetFetcherByID( 3699 SearchProvider::kDefaultProviderURLFetcherID); 3700 ASSERT_TRUE(fetcher); 3701 fetcher->set_response_code(200); 3702 fetcher->SetResponseString("this is a bad non-json response"); 3703 fetcher->delegate()->OnURLFetchComplete(fetcher); 3704 3705 RunTillProviderDone(); 3706 3707 const ACMatches& matches = provider_->matches(); 3708 3709 // Should have exactly one "search what you typed" match 3710 ASSERT_TRUE(matches.size() == 1); 3711 EXPECT_EQ(input_str, base::UTF16ToUTF8(matches[0].contents)); 3712 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 3713 matches[0].type); 3714} 3715 3716// A basic test that verifies that the XSSI guarded JSON response is parsed 3717// correctly. 3718TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_ValidResponses) { 3719 struct Match { 3720 std::string contents; 3721 AutocompleteMatchType::Type type; 3722 }; 3723 const Match kEmptyMatch = { 3724 kNotApplicable, AutocompleteMatchType::NUM_TYPES 3725 }; 3726 3727 struct { 3728 const std::string input_text; 3729 const std::string default_provider_response_json; 3730 const Match matches[4]; 3731 } cases[] = { 3732 // No XSSI guard. 3733 { "a", 3734 "[\"a\",[\"b\", \"c\"],[],[]," 3735 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"]," 3736 "\"google:suggestrelevance\":[1, 2]}]", 3737 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 3738 { "c", AutocompleteMatchType::SEARCH_SUGGEST }, 3739 { "b", AutocompleteMatchType::SEARCH_SUGGEST }, 3740 kEmptyMatch, 3741 }, 3742 }, 3743 // Standard XSSI guard - )]}'\n. 3744 { "a", 3745 ")]}'\n[\"a\",[\"b\", \"c\"],[],[]," 3746 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"]," 3747 "\"google:suggestrelevance\":[1, 2]}]", 3748 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 3749 { "c", AutocompleteMatchType::SEARCH_SUGGEST }, 3750 { "b", AutocompleteMatchType::SEARCH_SUGGEST }, 3751 kEmptyMatch, 3752 }, 3753 }, 3754 // Modified XSSI guard - contains "[". 3755 { "a", 3756 ")]}'\n[)\"[\"a\",[\"b\", \"c\"],[],[]," 3757 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"]," 3758 "\"google:suggestrelevance\":[1, 2]}]", 3759 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 3760 { "c", AutocompleteMatchType::SEARCH_SUGGEST }, 3761 { "b", AutocompleteMatchType::SEARCH_SUGGEST }, 3762 kEmptyMatch, 3763 }, 3764 }, 3765 }; 3766 3767 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 3768 ClearAllResults(); 3769 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false); 3770 3771 // Set up a default fetcher with provided results. 3772 net::TestURLFetcher* fetcher = 3773 test_factory_.GetFetcherByID( 3774 SearchProvider::kDefaultProviderURLFetcherID); 3775 ASSERT_TRUE(fetcher); 3776 fetcher->set_response_code(200); 3777 fetcher->SetResponseString(cases[i].default_provider_response_json); 3778 fetcher->delegate()->OnURLFetchComplete(fetcher); 3779 3780 RunTillProviderDone(); 3781 3782 const ACMatches& matches = provider_->matches(); 3783 // The top match must inline and score as highly as calculated verbatim. 3784 ASSERT_FALSE(matches.empty()); 3785 EXPECT_GE(matches[0].relevance, 1300); 3786 3787 SCOPED_TRACE("for case: " + base::IntToString(i)); 3788 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 3789 size_t j = 0; 3790 // Ensure that the returned matches equal the expectations. 3791 for (; j < matches.size(); ++j) { 3792 SCOPED_TRACE("and match: " + base::IntToString(j)); 3793 EXPECT_EQ(cases[i].matches[j].contents, 3794 base::UTF16ToUTF8(matches[j].contents)); 3795 EXPECT_EQ(cases[i].matches[j].type, matches[j].type); 3796 } 3797 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) { 3798 SCOPED_TRACE("and match: " + base::IntToString(j)); 3799 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable); 3800 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES); 3801 } 3802 } 3803} 3804 3805// Test that deletion url gets set on an AutocompleteMatch when available for a 3806// personalized query. 3807TEST_F(SearchProviderTest, ParseDeletionUrl) { 3808 struct Match { 3809 std::string contents; 3810 std::string deletion_url; 3811 AutocompleteMatchType::Type type; 3812 }; 3813 3814 const Match kEmptyMatch = { 3815 kNotApplicable, "", AutocompleteMatchType::NUM_TYPES 3816 }; 3817 3818 const char url[] = "https://www.google.com/complete/deleteitems" 3819 "?delq=ab&client=chrome&deltok=xsrf123"; 3820 3821 struct { 3822 const std::string input_text; 3823 const std::string response_json; 3824 const Match matches[4]; 3825 } cases[] = { 3826 // A deletion URL on a personalized query should be reflected in the 3827 // resulting AutocompleteMatch. 3828 { "a", 3829 "[\"a\",[\"ab\", \"ac\"],[],[]," 3830 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"]," 3831 "\"google:suggestrelevance\":[1, 2]," 3832 "\"google:suggestdetail\":[{\"du\":" 3833 "\"https://www.google.com/complete/deleteitems?delq=ab&client=chrome" 3834 "&deltok=xsrf123\"}, {}]}]", 3835 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 3836 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST }, 3837 { "ab", url, AutocompleteMatchType::SEARCH_SUGGEST }, 3838 kEmptyMatch, 3839 }, 3840 }, 3841 // Personalized queries without deletion URLs shouldn't cause errors. 3842 { "a", 3843 "[\"a\",[\"ab\", \"ac\"],[],[]," 3844 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"]," 3845 "\"google:suggestrelevance\":[1, 2]," 3846 "\"google:suggestdetail\":[{}, {}]}]", 3847 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 3848 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST }, 3849 { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST }, 3850 kEmptyMatch, 3851 }, 3852 }, 3853 }; 3854 3855 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 3856 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false); 3857 3858 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 3859 SearchProvider::kDefaultProviderURLFetcherID); 3860 ASSERT_TRUE(fetcher); 3861 fetcher->set_response_code(200); 3862 fetcher->SetResponseString(cases[i].response_json); 3863 fetcher->delegate()->OnURLFetchComplete(fetcher); 3864 3865 RunTillProviderDone(); 3866 3867 const ACMatches& matches = provider_->matches(); 3868 ASSERT_FALSE(matches.empty()); 3869 3870 SCOPED_TRACE("for input with json = " + cases[i].response_json); 3871 3872 for (size_t j = 0; j < matches.size(); ++j) { 3873 const Match& match = cases[i].matches[j]; 3874 SCOPED_TRACE(" and match index: " + base::IntToString(j)); 3875 EXPECT_EQ(match.contents, base::UTF16ToUTF8(matches[j].contents)); 3876 EXPECT_EQ(match.deletion_url, matches[j].GetAdditionalInfo( 3877 "deletion_url")); 3878 } 3879 } 3880} 3881 3882TEST_F(SearchProviderTest, ReflectsBookmarkBarState) { 3883 profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, false); 3884 base::string16 term = term1_.substr(0, term1_.length() - 1); 3885 QueryForInput(term, true, false); 3886 ASSERT_FALSE(provider_->matches().empty()); 3887 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 3888 provider_->matches()[0].type); 3889 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL); 3890 EXPECT_FALSE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned); 3891 3892 profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, true); 3893 term = term1_.substr(0, term1_.length() - 1); 3894 QueryForInput(term, true, false); 3895 ASSERT_FALSE(provider_->matches().empty()); 3896 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 3897 provider_->matches()[0].type); 3898 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL); 3899 EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned); 3900} 3901 3902TEST_F(SearchProviderTest, CanSendURL) { 3903 TemplateURLData template_url_data; 3904 template_url_data.short_name = ASCIIToUTF16("t"); 3905 template_url_data.SetURL("http://www.google.com/{searchTerms}"); 3906 template_url_data.suggestions_url = "http://www.google.com/{searchTerms}"; 3907 template_url_data.instant_url = "http://does/not/exist?strk=1"; 3908 template_url_data.search_terms_replacement_key = "strk"; 3909 template_url_data.id = SEARCH_ENGINE_GOOGLE; 3910 TemplateURL google_template_url(&profile_, template_url_data); 3911 3912 // Create field trial. 3913 base::FieldTrial* field_trial = base::FieldTrialList::CreateFieldTrial( 3914 "AutocompleteDynamicTrial_2", "EnableZeroSuggest"); 3915 field_trial->group(); 3916 3917 // Not signed in. 3918 EXPECT_FALSE(SearchProvider::CanSendURL( 3919 GURL("http://www.google.com/search"), 3920 GURL("https://www.google.com/complete/search"), &google_template_url, 3921 AutocompleteInput::OTHER, &profile_)); 3922 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(&profile_); 3923 signin->SetAuthenticatedUsername("test"); 3924 3925 // All conditions should be met. 3926 EXPECT_TRUE(SearchProvider::CanSendURL( 3927 GURL("http://www.google.com/search"), 3928 GURL("https://www.google.com/complete/search"), &google_template_url, 3929 AutocompleteInput::OTHER, &profile_)); 3930 3931 // Not in field trial. 3932 ResetFieldTrialList(); 3933 EXPECT_FALSE(SearchProvider::CanSendURL( 3934 GURL("http://www.google.com/search"), 3935 GURL("https://www.google.com/complete/search"), &google_template_url, 3936 AutocompleteInput::OTHER, &profile_)); 3937 field_trial = base::FieldTrialList::CreateFieldTrial( 3938 "AutocompleteDynamicTrial_2", "EnableZeroSuggest"); 3939 field_trial->group(); 3940 3941 // Invalid page URL. 3942 EXPECT_FALSE(SearchProvider::CanSendURL( 3943 GURL("badpageurl"), 3944 GURL("https://www.google.com/complete/search"), &google_template_url, 3945 AutocompleteInput::OTHER, &profile_)); 3946 3947 // Invalid page classification. 3948 EXPECT_FALSE(SearchProvider::CanSendURL( 3949 GURL("http://www.google.com/search"), 3950 GURL("https://www.google.com/complete/search"), &google_template_url, 3951 AutocompleteInput::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS, 3952 &profile_)); 3953 3954 // Invalid page classification. 3955 EXPECT_FALSE(SearchProvider::CanSendURL( 3956 GURL("http://www.google.com/search"), 3957 GURL("https://www.google.com/complete/search"), &google_template_url, 3958 AutocompleteInput::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS, 3959 &profile_)); 3960 3961 // HTTPS page URL on same domain as provider. 3962 EXPECT_TRUE(SearchProvider::CanSendURL( 3963 GURL("https://www.google.com/search"), 3964 GURL("https://www.google.com/complete/search"), 3965 &google_template_url, AutocompleteInput::OTHER, &profile_)); 3966 3967 // Non-HTTP[S] page URL on same domain as provider. 3968 EXPECT_FALSE(SearchProvider::CanSendURL( 3969 GURL("ftp://www.google.com/search"), 3970 GURL("https://www.google.com/complete/search"), &google_template_url, 3971 AutocompleteInput::OTHER, &profile_)); 3972 3973 // Non-HTTP page URL on different domain. 3974 EXPECT_FALSE(SearchProvider::CanSendURL( 3975 GURL("https://www.notgoogle.com/search"), 3976 GURL("https://www.google.com/complete/search"), &google_template_url, 3977 AutocompleteInput::OTHER, &profile_)); 3978 3979 // Non-HTTPS provider. 3980 EXPECT_FALSE(SearchProvider::CanSendURL( 3981 GURL("http://www.google.com/search"), 3982 GURL("http://www.google.com/complete/search"), &google_template_url, 3983 AutocompleteInput::OTHER, &profile_)); 3984 3985 // Suggest disabled. 3986 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, false); 3987 EXPECT_FALSE(SearchProvider::CanSendURL( 3988 GURL("http://www.google.com/search"), 3989 GURL("https://www.google.com/complete/search"), &google_template_url, 3990 AutocompleteInput::OTHER, &profile_)); 3991 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true); 3992 3993 // Incognito. 3994 EXPECT_FALSE(SearchProvider::CanSendURL( 3995 GURL("http://www.google.com/search"), 3996 GURL("https://www.google.com/complete/search"), &google_template_url, 3997 AutocompleteInput::OTHER, profile_.GetOffTheRecordProfile())); 3998 3999 // Tab sync not enabled. 4000 profile_.GetPrefs()->SetBoolean(prefs::kSyncKeepEverythingSynced, false); 4001 profile_.GetPrefs()->SetBoolean(prefs::kSyncTabs, false); 4002 EXPECT_FALSE(SearchProvider::CanSendURL( 4003 GURL("http://www.google.com/search"), 4004 GURL("https://www.google.com/complete/search"), &google_template_url, 4005 AutocompleteInput::OTHER, &profile_)); 4006 profile_.GetPrefs()->SetBoolean(prefs::kSyncTabs, true); 4007 4008 // Tab sync is encrypted. 4009 ProfileSyncService* service = 4010 ProfileSyncServiceFactory::GetInstance()->GetForProfile(&profile_); 4011 syncer::ModelTypeSet encrypted_types = service->GetEncryptedDataTypes(); 4012 encrypted_types.Put(syncer::SESSIONS); 4013 service->OnEncryptedTypesChanged(encrypted_types, false); 4014 EXPECT_FALSE(SearchProvider::CanSendURL( 4015 GURL("http://www.google.com/search"), 4016 GURL("https://www.google.com/complete/search"), &google_template_url, 4017 AutocompleteInput::OTHER, &profile_)); 4018 encrypted_types.Remove(syncer::SESSIONS); 4019 service->OnEncryptedTypesChanged(encrypted_types, false); 4020 4021 // Check that there were no side effects from previous tests. 4022 EXPECT_TRUE(SearchProvider::CanSendURL( 4023 GURL("http://www.google.com/search"), 4024 GURL("https://www.google.com/complete/search"), &google_template_url, 4025 AutocompleteInput::OTHER, &profile_)); 4026} 4027 4028TEST_F(SearchProviderTest, TestDeleteMatch) { 4029 AutocompleteMatch match(provider_, 0, true, 4030 AutocompleteMatchType::SEARCH_SUGGEST); 4031 match.RecordAdditionalInfo( 4032 SearchProvider::kDeletionUrlKey, 4033 "https://www.google.com/complete/deleteitem?q=foo"); 4034 4035 // Test a successful deletion request. 4036 provider_->matches_.push_back(match); 4037 provider_->DeleteMatch(match); 4038 EXPECT_FALSE(provider_->deletion_handlers_.empty()); 4039 EXPECT_TRUE(provider_->matches_.empty()); 4040 // Set up a default fetcher with provided results. 4041 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 4042 SearchProvider::kDeletionURLFetcherID); 4043 ASSERT_TRUE(fetcher); 4044 fetcher->set_response_code(200); 4045 fetcher->delegate()->OnURLFetchComplete(fetcher); 4046 EXPECT_TRUE(provider_->deletion_handlers_.empty()); 4047 EXPECT_TRUE(provider_->is_success()); 4048 4049 // Test a failing deletion request. 4050 provider_->matches_.push_back(match); 4051 provider_->DeleteMatch(match); 4052 EXPECT_FALSE(provider_->deletion_handlers_.empty()); 4053 // Set up a default fetcher with provided results. 4054 fetcher = test_factory_.GetFetcherByID( 4055 SearchProvider::kDeletionURLFetcherID); 4056 ASSERT_TRUE(fetcher); 4057 fetcher->set_response_code(500); 4058 fetcher->delegate()->OnURLFetchComplete(fetcher); 4059 EXPECT_TRUE(provider_->deletion_handlers_.empty()); 4060 EXPECT_FALSE(provider_->is_success()); 4061} 4062 4063TEST_F(SearchProviderTest, TestDeleteHistoryQueryMatch) { 4064 GURL term_url( 4065 AddSearchToHistory(default_t_url_, ASCIIToUTF16("flash games"), 1)); 4066 profile_.BlockUntilHistoryProcessesPendingRequests(); 4067 4068 AutocompleteMatch games; 4069 QueryForInput(ASCIIToUTF16("fla"), false, false); 4070 profile_.BlockUntilHistoryProcessesPendingRequests(); 4071 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 4072 ASSERT_TRUE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games)); 4073 4074 size_t matches_before = provider_->matches().size(); 4075 provider_->DeleteMatch(games); 4076 EXPECT_EQ(matches_before - 1, provider_->matches().size()); 4077 4078 // Process history deletions. 4079 profile_.BlockUntilHistoryProcessesPendingRequests(); 4080 4081 // Check that the match is gone. 4082 QueryForInput(ASCIIToUTF16("fla"), false, false); 4083 profile_.BlockUntilHistoryProcessesPendingRequests(); 4084 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 4085 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games)); 4086} 4087 4088// Verifies that duplicates are preserved in AddMatchToMap(). 4089TEST_F(SearchProviderTest, CheckDuplicateMatchesSaved) { 4090 AddSearchToHistory(default_t_url_, ASCIIToUTF16("a"), 1); 4091 AddSearchToHistory(default_t_url_, ASCIIToUTF16("alpha"), 1); 4092 AddSearchToHistory(default_t_url_, ASCIIToUTF16("avid"), 1); 4093 4094 profile_.BlockUntilHistoryProcessesPendingRequests(); 4095 QueryForInput(ASCIIToUTF16("a"), false, false); 4096 4097 // Make sure the default provider's suggest service was queried. 4098 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 4099 SearchProvider::kDefaultProviderURLFetcherID); 4100 ASSERT_TRUE(fetcher); 4101 4102 // Tell the SearchProvider the suggest query is done. 4103 fetcher->set_response_code(200); 4104 fetcher->SetResponseString( 4105 "[\"a\",[\"a\", \"alpha\", \"avid\", \"apricot\"],[],[]," 4106 "{\"google:suggestrelevance\":[1450, 1200, 1150, 1100]," 4107 "\"google:verbatimrelevance\":1350}]"); 4108 fetcher->delegate()->OnURLFetchComplete(fetcher); 4109 fetcher = NULL; 4110 4111 // Run till the history results complete. 4112 RunTillProviderDone(); 4113 4114 AutocompleteMatch verbatim, match_alpha, match_apricot, match_avid; 4115 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim)); 4116 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("alpha"), &match_alpha)); 4117 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("apricot"), &match_apricot)); 4118 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("avid"), &match_avid)); 4119 4120 // Verbatim match duplicates are added such that each one has a higher 4121 // relevance than the previous one. 4122 EXPECT_EQ(2U, verbatim.duplicate_matches.size()); 4123 4124 // Other match duplicates are added in descending relevance order. 4125 EXPECT_EQ(1U, match_alpha.duplicate_matches.size()); 4126 EXPECT_EQ(1U, match_avid.duplicate_matches.size()); 4127 4128 EXPECT_EQ(0U, match_apricot.duplicate_matches.size()); 4129} 4130