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