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 "base/command_line.h" 8 #include "base/metrics/field_trial.h" 9 #include "base/prefs/pref_service.h" 10 #include "base/run_loop.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "base/time/time.h" 14 #include "build/build_config.h" 15 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" 16 #include "chrome/browser/autocomplete/autocomplete_controller.h" 17 #include "chrome/browser/autocomplete/autocomplete_input.h" 18 #include "chrome/browser/autocomplete/autocomplete_match.h" 19 #include "chrome/browser/autocomplete/autocomplete_provider.h" 20 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h" 21 #include "chrome/browser/autocomplete/history_url_provider.h" 22 #include "chrome/browser/history/history_service.h" 23 #include "chrome/browser/history/history_service_factory.h" 24 #include "chrome/browser/omnibox/omnibox_field_trial.h" 25 #include "chrome/browser/search/search.h" 26 #include "chrome/browser/search_engines/template_url.h" 27 #include "chrome/browser/search_engines/template_url_service.h" 28 #include "chrome/browser/search_engines/template_url_service_factory.h" 29 #include "chrome/common/chrome_switches.h" 30 #include "chrome/common/metrics/entropy_provider.h" 31 #include "chrome/common/metrics/variations/variations_util.h" 32 #include "chrome/common/pref_names.h" 33 #include "chrome/test/base/testing_browser_process.h" 34 #include "chrome/test/base/testing_profile.h" 35 #include "content/public/test/test_browser_thread_bundle.h" 36 #include "net/url_request/test_url_fetcher_factory.h" 37 #include "net/url_request/url_request_status.h" 38 #include "testing/gtest/include/gtest/gtest.h" 39 40 41 // SearchProviderTest --------------------------------------------------------- 42 43 // The following environment is configured for these tests: 44 // . The TemplateURL default_t_url_ is set as the default provider. 45 // . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This 46 // TemplateURL has a valid suggest and search URL. 47 // . The URL created by using the search term term1_ with default_t_url_ is 48 // added to history. 49 // . The URL created by using the search term keyword_term_ with keyword_t_url_ 50 // is added to history. 51 // . test_factory_ is set as the URLFetcherFactory. 52 class SearchProviderTest : public testing::Test, 53 public AutocompleteProviderListener { 54 public: 55 struct ResultInfo { 56 ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES) { 57 } 58 ResultInfo(GURL gurl, 59 AutocompleteMatch::Type result_type, 60 string16 fill_into_edit) 61 : gurl(gurl), 62 result_type(result_type), 63 fill_into_edit(fill_into_edit) { 64 } 65 66 const GURL gurl; 67 const AutocompleteMatch::Type result_type; 68 const string16 fill_into_edit; 69 }; 70 71 struct TestData { 72 const string16 input; 73 const size_t num_results; 74 const ResultInfo output[3]; 75 }; 76 77 SearchProviderTest() 78 : default_t_url_(NULL), 79 term1_(ASCIIToUTF16("term1")), 80 keyword_t_url_(NULL), 81 keyword_term_(ASCIIToUTF16("keyword")), 82 run_loop_(NULL) { 83 ResetFieldTrialList(); 84 } 85 86 // See description above class for what this registers. 87 virtual void SetUp() OVERRIDE; 88 virtual void TearDown() OVERRIDE; 89 90 void RunTest(TestData* cases, int num_cases, bool prefer_keyword); 91 92 protected: 93 // Needed for AutocompleteFieldTrial::ActivateStaticTrials(); 94 scoped_ptr<base::FieldTrialList> field_trial_list_; 95 96 // Default value used for testing. 97 static const std::string kNotApplicable; 98 99 // Adds a search for |term|, using the engine |t_url| to the history, and 100 // returns the URL for that search. 101 GURL AddSearchToHistory(TemplateURL* t_url, string16 term, int visit_count); 102 103 // Looks for a match in |provider_| with |contents| equal to |contents|. 104 // Sets |match| to it if found. Returns whether |match| was set. 105 bool FindMatchWithContents(const string16& contents, 106 AutocompleteMatch* match); 107 108 // Looks for a match in |provider_| with destination |url|. Sets |match| to 109 // it if found. Returns whether |match| was set. 110 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match); 111 112 // AutocompleteProviderListener: 113 // If we're waiting for the provider to finish, this exits the message loop. 114 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE; 115 116 // Runs a nested message loop until provider_ is done. The message loop is 117 // exited by way of OnProviderUpdate. 118 void RunTillProviderDone(); 119 120 // Invokes Start on provider_, then runs all pending tasks. 121 void QueryForInput(const string16& text, 122 bool prevent_inline_autocomplete, 123 bool prefer_keyword); 124 125 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is 126 // non-NULL, sets it to the "what you typed" entry for |text|. 127 void QueryForInputAndSetWYTMatch(const string16& text, 128 AutocompleteMatch* wyt_match); 129 130 // Notifies the URLFetcher for the suggest query corresponding to the default 131 // search provider that it's done. 132 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE. 133 void FinishDefaultSuggestQuery(); 134 135 void ResetFieldTrialList(); 136 137 // See description above class for details of these fields. 138 TemplateURL* default_t_url_; 139 const string16 term1_; 140 GURL term1_url_; 141 TemplateURL* keyword_t_url_; 142 const string16 keyword_term_; 143 GURL keyword_url_; 144 145 content::TestBrowserThreadBundle thread_bundle_; 146 147 // URLFetcherFactory implementation registered. 148 net::TestURLFetcherFactory test_factory_; 149 150 // Profile we use. 151 TestingProfile profile_; 152 153 // The provider. 154 scoped_refptr<SearchProvider> provider_; 155 156 // If non-NULL, OnProviderUpdate quits the current |run_loop_|. 157 base::RunLoop* run_loop_; 158 159 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest); 160 }; 161 162 // static 163 const std::string SearchProviderTest::kNotApplicable = "Not Applicable"; 164 165 void SearchProviderTest::SetUp() { 166 // Make sure that fetchers are automatically ungregistered upon destruction. 167 test_factory_.set_remove_fetcher_on_delete(true); 168 169 // We need both the history service and template url model loaded. 170 ASSERT_TRUE(profile_.CreateHistoryService(true, false)); 171 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 172 &profile_, &TemplateURLServiceFactory::BuildInstanceFor); 173 174 TemplateURLService* turl_model = 175 TemplateURLServiceFactory::GetForProfile(&profile_); 176 177 turl_model->Load(); 178 179 // Reset the default TemplateURL. 180 TemplateURLData data; 181 data.short_name = ASCIIToUTF16("t"); 182 data.SetURL("http://defaultturl/{searchTerms}"); 183 data.suggestions_url = "http://defaultturl2/{searchTerms}"; 184 data.instant_url = "http://does/not/exist?strk=1"; 185 data.search_terms_replacement_key = "strk"; 186 default_t_url_ = new TemplateURL(&profile_, data); 187 turl_model->Add(default_t_url_); 188 turl_model->SetDefaultSearchProvider(default_t_url_); 189 TemplateURLID default_provider_id = default_t_url_->id(); 190 ASSERT_NE(0, default_provider_id); 191 192 // Add url1, with search term term1_. 193 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1); 194 195 // Create another TemplateURL. 196 data.short_name = ASCIIToUTF16("k"); 197 data.SetKeyword(ASCIIToUTF16("k")); 198 data.SetURL("http://keyword/{searchTerms}"); 199 data.suggestions_url = "http://suggest_keyword/{searchTerms}"; 200 keyword_t_url_ = new TemplateURL(&profile_, data); 201 turl_model->Add(keyword_t_url_); 202 ASSERT_NE(0, keyword_t_url_->id()); 203 204 // Add a page and search term for keyword_t_url_. 205 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1); 206 207 // Keywords are updated by the InMemoryHistoryBackend only after the message 208 // has been processed on the history thread. Block until history processes all 209 // requests to ensure the InMemoryDatabase is the state we expect it. 210 profile_.BlockUntilHistoryProcessesPendingRequests(); 211 212 provider_ = new SearchProvider(this, &profile_); 213 provider_->kMinimumTimeBetweenSuggestQueriesMs = 0; 214 } 215 216 void SearchProviderTest::TearDown() { 217 base::RunLoop().RunUntilIdle(); 218 219 // Shutdown the provider before the profile. 220 provider_ = NULL; 221 } 222 223 void SearchProviderTest::RunTest(TestData* cases, 224 int num_cases, 225 bool prefer_keyword) { 226 ACMatches matches; 227 for (int i = 0; i < num_cases; ++i) { 228 AutocompleteInput input(cases[i].input, string16::npos, string16(), GURL(), 229 AutocompleteInput::INVALID_SPEC, false, 230 prefer_keyword, true, 231 AutocompleteInput::ALL_MATCHES); 232 provider_->Start(input, false); 233 matches = provider_->matches(); 234 string16 diagnostic_details = ASCIIToUTF16("Input was: ") + cases[i].input + 235 ASCIIToUTF16("; prefer_keyword was: ") + 236 (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false")); 237 EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details; 238 if (matches.size() == cases[i].num_results) { 239 for (size_t j = 0; j < cases[i].num_results; ++j) { 240 EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) << 241 diagnostic_details; 242 EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) << 243 diagnostic_details; 244 EXPECT_EQ(cases[i].output[j].fill_into_edit, 245 matches[j].fill_into_edit) << 246 diagnostic_details; 247 // All callers that use this helper function at the moment produce 248 // matches that are always allowed to be the default match. 249 EXPECT_TRUE(matches[j].allowed_to_be_default_match); 250 } 251 } 252 } 253 } 254 255 void SearchProviderTest::OnProviderUpdate(bool updated_matches) { 256 if (run_loop_ && provider_->done()) { 257 run_loop_->Quit(); 258 run_loop_ = NULL; 259 } 260 } 261 262 void SearchProviderTest::RunTillProviderDone() { 263 if (provider_->done()) 264 return; 265 266 base::RunLoop run_loop; 267 run_loop_ = &run_loop; 268 run_loop.Run(); 269 } 270 271 void SearchProviderTest::QueryForInput(const string16& text, 272 bool prevent_inline_autocomplete, 273 bool prefer_keyword) { 274 // Start a query. 275 AutocompleteInput input(text, string16::npos, string16(), GURL(), 276 AutocompleteInput::INVALID_SPEC, 277 prevent_inline_autocomplete, prefer_keyword, true, 278 AutocompleteInput::ALL_MATCHES); 279 provider_->Start(input, false); 280 281 // RunUntilIdle so that the task scheduled by SearchProvider to create the 282 // URLFetchers runs. 283 base::RunLoop().RunUntilIdle(); 284 } 285 286 void SearchProviderTest::QueryForInputAndSetWYTMatch( 287 const string16& text, 288 AutocompleteMatch* wyt_match) { 289 QueryForInput(text, false, false); 290 profile_.BlockUntilHistoryProcessesPendingRequests(); 291 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 292 if (!wyt_match) 293 return; 294 ASSERT_GE(provider_->matches().size(), 1u); 295 EXPECT_TRUE(FindMatchWithDestination(GURL( 296 default_t_url_->url_ref().ReplaceSearchTerms( 297 TemplateURLRef::SearchTermsArgs(text))), 298 wyt_match)); 299 } 300 301 GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url, 302 string16 term, 303 int visit_count) { 304 HistoryService* history = 305 HistoryServiceFactory::GetForProfile(&profile_, 306 Profile::EXPLICIT_ACCESS); 307 GURL search(t_url->url_ref().ReplaceSearchTerms( 308 TemplateURLRef::SearchTermsArgs(term))); 309 static base::Time last_added_time; 310 last_added_time = std::max(base::Time::Now(), 311 last_added_time + base::TimeDelta::FromMicroseconds(1)); 312 history->AddPageWithDetails(search, string16(), visit_count, visit_count, 313 last_added_time, false, history::SOURCE_BROWSED); 314 history->SetKeywordSearchTermsForURL(search, t_url->id(), term); 315 return search; 316 } 317 318 bool SearchProviderTest::FindMatchWithContents(const string16& contents, 319 AutocompleteMatch* match) { 320 for (ACMatches::const_iterator i = provider_->matches().begin(); 321 i != provider_->matches().end(); ++i) { 322 if (i->contents == contents) { 323 *match = *i; 324 return true; 325 } 326 } 327 return false; 328 } 329 330 bool SearchProviderTest::FindMatchWithDestination(const GURL& url, 331 AutocompleteMatch* match) { 332 for (ACMatches::const_iterator i = provider_->matches().begin(); 333 i != provider_->matches().end(); ++i) { 334 if (i->destination_url == url) { 335 *match = *i; 336 return true; 337 } 338 } 339 return false; 340 } 341 342 void SearchProviderTest::FinishDefaultSuggestQuery() { 343 net::TestURLFetcher* default_fetcher = 344 test_factory_.GetFetcherByID( 345 SearchProvider::kDefaultProviderURLFetcherID); 346 ASSERT_TRUE(default_fetcher); 347 348 // Tell the SearchProvider the default suggest query is done. 349 default_fetcher->set_response_code(200); 350 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 351 } 352 353 void SearchProviderTest::ResetFieldTrialList() { 354 // Destroy the existing FieldTrialList before creating a new one to avoid 355 // a DCHECK. 356 field_trial_list_.reset(); 357 field_trial_list_.reset(new base::FieldTrialList( 358 new metrics::SHA1EntropyProvider("foo"))); 359 chrome_variations::testing::ClearAllVariationParams(); 360 base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial( 361 "AutocompleteDynamicTrial_0", "DefaultGroup"); 362 trial->group(); 363 } 364 365 // Actual Tests --------------------------------------------------------------- 366 367 // Make sure we query history for the default provider and a URLFetcher is 368 // created for the default provider suggest results. 369 TEST_F(SearchProviderTest, QueryDefaultProvider) { 370 string16 term = term1_.substr(0, term1_.length() - 1); 371 QueryForInput(term, false, false); 372 373 // Make sure the default providers suggest service was queried. 374 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 375 SearchProvider::kDefaultProviderURLFetcherID); 376 ASSERT_TRUE(fetcher); 377 378 // And the URL matches what we expected. 379 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms( 380 TemplateURLRef::SearchTermsArgs(term))); 381 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url); 382 383 // Tell the SearchProvider the suggest query is done. 384 fetcher->set_response_code(200); 385 fetcher->delegate()->OnURLFetchComplete(fetcher); 386 fetcher = NULL; 387 388 // Run till the history results complete. 389 RunTillProviderDone(); 390 391 // The SearchProvider is done. Make sure it has a result for the history 392 // term term1. 393 AutocompleteMatch term1_match; 394 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match)); 395 // Term1 should not have a description, it's set later. 396 EXPECT_TRUE(term1_match.description.empty()); 397 398 AutocompleteMatch wyt_match; 399 EXPECT_TRUE(FindMatchWithDestination( 400 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 401 TemplateURLRef::SearchTermsArgs(term))), &wyt_match)); 402 EXPECT_TRUE(wyt_match.description.empty()); 403 404 // The match for term1 should be more relevant than the what you typed match. 405 EXPECT_GT(term1_match.relevance, wyt_match.relevance); 406 // This longer match should be inlineable. 407 EXPECT_TRUE(term1_match.allowed_to_be_default_match); 408 // The what you typed match should be too, of course. 409 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 410 } 411 412 TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) { 413 string16 term = term1_.substr(0, term1_.length() - 1); 414 QueryForInput(term, true, false); 415 416 ASSERT_FALSE(provider_->matches().empty()); 417 ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 418 provider_->matches()[0].type); 419 EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match); 420 } 421 422 // Issues a query that matches the registered keyword and makes sure history 423 // is queried as well as URLFetchers getting created. 424 TEST_F(SearchProviderTest, QueryKeywordProvider) { 425 string16 term = keyword_term_.substr(0, keyword_term_.length() - 1); 426 QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term, 427 false, 428 false); 429 430 // Make sure the default providers suggest service was queried. 431 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID( 432 SearchProvider::kDefaultProviderURLFetcherID); 433 ASSERT_TRUE(default_fetcher); 434 435 // Tell the SearchProvider the default suggest query is done. 436 default_fetcher->set_response_code(200); 437 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 438 default_fetcher = NULL; 439 440 // Make sure the keyword providers suggest service was queried. 441 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID( 442 SearchProvider::kKeywordProviderURLFetcherID); 443 ASSERT_TRUE(keyword_fetcher); 444 445 // And the URL matches what we expected. 446 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms( 447 TemplateURLRef::SearchTermsArgs(term))); 448 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url); 449 450 // Tell the SearchProvider the keyword suggest query is done. 451 keyword_fetcher->set_response_code(200); 452 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 453 keyword_fetcher = NULL; 454 455 // Run till the history results complete. 456 RunTillProviderDone(); 457 458 // The SearchProvider is done. Make sure it has a result for the history 459 // term keyword. 460 AutocompleteMatch match; 461 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match)); 462 463 // The match should have an associated keyword. 464 EXPECT_FALSE(match.keyword.empty()); 465 466 // The fill into edit should contain the keyword. 467 EXPECT_EQ(keyword_t_url_->keyword() + char16(' ') + keyword_term_, 468 match.fill_into_edit); 469 } 470 471 TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) { 472 // None of the following input strings should be sent to the suggest server, 473 // because they may contain private data. 474 const char* inputs[] = { 475 "username:password", 476 "http://username:password", 477 "https://username:password", 478 "username:password@hostname", 479 "http://username:password@hostname/", 480 "file://filename", 481 "data://data", 482 "unknownscheme:anything", 483 "http://hostname/?query=q", 484 "http://hostname/path#ref", 485 "http://hostname/path #ref", 486 "https://hostname/path", 487 }; 488 489 for (size_t i = 0; i < arraysize(inputs); ++i) { 490 QueryForInput(ASCIIToUTF16(inputs[i]), false, false); 491 // Make sure the default provider's suggest service was not queried. 492 ASSERT_TRUE(test_factory_.GetFetcherByID( 493 SearchProvider::kDefaultProviderURLFetcherID) == NULL); 494 // Run till the history results complete. 495 RunTillProviderDone(); 496 } 497 } 498 499 TEST_F(SearchProviderTest, SendNonPrivateDataToSuggest) { 500 // All of the following input strings should be sent to the suggest server, 501 // because they should not get caught by the private data checks. 502 const char* inputs[] = { 503 "query", 504 "query with spaces", 505 "http://hostname", 506 "http://hostname/path", 507 "http://hostname #ref", 508 "www.hostname.com #ref", 509 "https://hostname", 510 "#hashtag", 511 "foo https://hostname/path" 512 }; 513 514 profile_.BlockUntilHistoryProcessesPendingRequests(); 515 for (size_t i = 0; i < arraysize(inputs); ++i) { 516 QueryForInput(ASCIIToUTF16(inputs[i]), false, false); 517 // Make sure the default provider's suggest service was queried. 518 ASSERT_TRUE(test_factory_.GetFetcherByID( 519 SearchProvider::kDefaultProviderURLFetcherID) != NULL); 520 } 521 } 522 523 TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) { 524 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse( 525 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor); 526 GURL url = AddSearchToHistory(default_t_url_, 527 ASCIIToUTF16("docs.google.com"), 1); 528 529 // Add the term as a url. 530 HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)-> 531 AddPageWithDetails(GURL("http://docs.google.com"), string16(), 1, 1, 532 base::Time::Now(), false, history::SOURCE_BROWSED); 533 profile_.BlockUntilHistoryProcessesPendingRequests(); 534 535 AutocompleteMatch wyt_match; 536 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"), 537 &wyt_match)); 538 539 // There should be two matches, one for what you typed, the other for 540 // 'docs.google.com'. The search term should have a lower priority than the 541 // what you typed match. 542 ASSERT_EQ(2u, provider_->matches().size()); 543 AutocompleteMatch term_match; 544 EXPECT_TRUE(FindMatchWithDestination(url, &term_match)); 545 EXPECT_GT(wyt_match.relevance, term_match.relevance); 546 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 547 EXPECT_TRUE(term_match.allowed_to_be_default_match); 548 } 549 550 // A multiword search with one visit should not autocomplete until multiple 551 // words are typed. 552 TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) { 553 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"), 554 1)); 555 profile_.BlockUntilHistoryProcessesPendingRequests(); 556 557 AutocompleteMatch wyt_match; 558 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"), 559 &wyt_match)); 560 ASSERT_EQ(2u, provider_->matches().size()); 561 AutocompleteMatch term_match; 562 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 563 EXPECT_GT(wyt_match.relevance, term_match.relevance); 564 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 565 EXPECT_TRUE(term_match.allowed_to_be_default_match); 566 567 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"), 568 &wyt_match)); 569 ASSERT_EQ(2u, provider_->matches().size()); 570 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 571 EXPECT_GT(term_match.relevance, wyt_match.relevance); 572 EXPECT_TRUE(term_match.allowed_to_be_default_match); 573 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 574 } 575 576 // A multiword search with more than one visit should autocomplete immediately. 577 TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) { 578 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 579 2)); 580 profile_.BlockUntilHistoryProcessesPendingRequests(); 581 582 AutocompleteMatch wyt_match; 583 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"), 584 &wyt_match)); 585 ASSERT_EQ(2u, provider_->matches().size()); 586 AutocompleteMatch term_match; 587 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 588 EXPECT_GT(term_match.relevance, wyt_match.relevance); 589 EXPECT_TRUE(term_match.allowed_to_be_default_match); 590 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 591 } 592 593 // Autocompletion should work at a word boundary after a space. 594 TEST_F(SearchProviderTest, AutocompleteAfterSpace) { 595 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 596 2)); 597 profile_.BlockUntilHistoryProcessesPendingRequests(); 598 599 AutocompleteMatch wyt_match; 600 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "), 601 &wyt_match)); 602 ASSERT_EQ(2u, provider_->matches().size()); 603 AutocompleteMatch term_match; 604 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 605 EXPECT_GT(term_match.relevance, wyt_match.relevance); 606 EXPECT_TRUE(term_match.allowed_to_be_default_match); 607 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 608 } 609 610 // Newer multiword searches should score more highly than older ones. 611 TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) { 612 GURL term_url_a(AddSearchToHistory(default_t_url_, 613 ASCIIToUTF16("three searches aaa"), 1)); 614 GURL term_url_b(AddSearchToHistory(default_t_url_, 615 ASCIIToUTF16("three searches bbb"), 1)); 616 profile_.BlockUntilHistoryProcessesPendingRequests(); 617 618 AutocompleteMatch wyt_match; 619 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"), 620 &wyt_match)); 621 ASSERT_EQ(3u, provider_->matches().size()); 622 AutocompleteMatch term_match_a; 623 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 624 AutocompleteMatch term_match_b; 625 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 626 EXPECT_GT(term_match_b.relevance, term_match_a.relevance); 627 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 628 EXPECT_TRUE(term_match_b.allowed_to_be_default_match); 629 EXPECT_TRUE(term_match_a.allowed_to_be_default_match); 630 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 631 } 632 633 // An autocompleted multiword search should not be replaced by a different 634 // autocompletion while the user is still typing a valid prefix. 635 TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) { 636 GURL term_url_a(AddSearchToHistory(default_t_url_, 637 ASCIIToUTF16("four searches aaa"), 2)); 638 GURL term_url_b(AddSearchToHistory(default_t_url_, 639 ASCIIToUTF16("four searches bbb"), 1)); 640 profile_.BlockUntilHistoryProcessesPendingRequests(); 641 642 AutocompleteMatch wyt_match; 643 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"), 644 &wyt_match)); 645 ASSERT_EQ(3u, provider_->matches().size()); 646 AutocompleteMatch term_match_a; 647 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 648 AutocompleteMatch term_match_b; 649 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 650 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 651 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 652 EXPECT_TRUE(term_match_a.allowed_to_be_default_match); 653 EXPECT_TRUE(term_match_b.allowed_to_be_default_match); 654 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 655 656 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"), 657 &wyt_match)); 658 ASSERT_EQ(3u, provider_->matches().size()); 659 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 660 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 661 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 662 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 663 EXPECT_TRUE(term_match_a.allowed_to_be_default_match); 664 EXPECT_TRUE(term_match_b.allowed_to_be_default_match); 665 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 666 } 667 668 // Non-completable multiword searches should not crowd out single-word searches. 669 TEST_F(SearchProviderTest, DontCrowdOutSingleWords) { 670 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1)); 671 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1); 672 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1); 673 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1); 674 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1); 675 profile_.BlockUntilHistoryProcessesPendingRequests(); 676 677 AutocompleteMatch wyt_match; 678 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"), 679 &wyt_match)); 680 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size()); 681 AutocompleteMatch term_match; 682 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 683 EXPECT_GT(term_match.relevance, wyt_match.relevance); 684 EXPECT_TRUE(term_match.allowed_to_be_default_match); 685 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 686 } 687 688 // Inline autocomplete matches regardless of case differences from the input. 689 TEST_F(SearchProviderTest, InlineMixedCaseMatches) { 690 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1)); 691 profile_.BlockUntilHistoryProcessesPendingRequests(); 692 693 AutocompleteMatch wyt_match; 694 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"), 695 &wyt_match)); 696 ASSERT_EQ(2u, provider_->matches().size()); 697 AutocompleteMatch term_match; 698 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 699 EXPECT_GT(term_match.relevance, wyt_match.relevance); 700 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit); 701 EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion); 702 EXPECT_TRUE(term_match.allowed_to_be_default_match); 703 } 704 705 // Verifies AutocompleteControllers return results (including keyword 706 // results) in the right order and set descriptions for them correctly. 707 TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) { 708 // Add an entry that corresponds to a keyword search with 'term2'. 709 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1); 710 profile_.BlockUntilHistoryProcessesPendingRequests(); 711 712 AutocompleteController controller(&profile_, NULL, 713 AutocompleteProvider::TYPE_SEARCH); 714 controller.Start(AutocompleteInput( 715 ASCIIToUTF16("k t"), string16::npos, string16(), GURL(), 716 AutocompleteInput::INVALID_SPEC, false, false, true, 717 AutocompleteInput::ALL_MATCHES)); 718 const AutocompleteResult& result = controller.result(); 719 720 // There should be three matches, one for the keyword history, one for 721 // keyword provider's what-you-typed, and one for the default provider's 722 // what you typed, in that order. 723 ASSERT_EQ(3u, result.size()); 724 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type); 725 EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE, 726 result.match_at(1).type); 727 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 728 result.match_at(2).type); 729 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance); 730 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance); 731 EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match); 732 EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match); 733 EXPECT_TRUE(result.match_at(2).allowed_to_be_default_match); 734 735 // The two keyword results should come with the keyword we expect. 736 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword); 737 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword); 738 // The default provider has a different keyword. (We don't explicitly 739 // set it during this test, so all we do is assert that it's different.) 740 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword); 741 742 // The top result will always have a description. The third result, 743 // coming from a different provider than the first two, should also. 744 // Whether the second result has one doesn't matter much. (If it was 745 // missing, people would infer that it's the same search provider as 746 // the one above it.) 747 EXPECT_FALSE(result.match_at(0).description.empty()); 748 EXPECT_FALSE(result.match_at(2).description.empty()); 749 EXPECT_NE(result.match_at(0).description, result.match_at(2).description); 750 } 751 752 TEST_F(SearchProviderTest, KeywordVerbatim) { 753 TestData cases[] = { 754 // Test a simple keyword input. 755 { ASCIIToUTF16("k foo"), 2, 756 { ResultInfo(GURL("http://keyword/foo"), 757 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 758 ASCIIToUTF16("k foo")), 759 ResultInfo(GURL("http://defaultturl/k%20foo"), 760 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 761 ASCIIToUTF16("k foo") ) } }, 762 763 // Make sure extra whitespace after the keyword doesn't change the 764 // keyword verbatim query. 765 { ASCIIToUTF16("k foo"), 2, 766 { ResultInfo(GURL("http://keyword/foo"), 767 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 768 ASCIIToUTF16("k foo")), 769 ResultInfo(GURL("http://defaultturl/k%20%20%20foo"), 770 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 771 ASCIIToUTF16("k foo")) } }, 772 // Leading whitespace should be stripped before SearchProvider gets the 773 // input; hence there are no tests here about how it handles those inputs. 774 775 // But whitespace elsewhere in the query string should matter to both 776 // matches. 777 { ASCIIToUTF16("k foo bar"), 2, 778 { ResultInfo(GURL("http://keyword/foo%20%20bar"), 779 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 780 ASCIIToUTF16("k foo bar")), 781 ResultInfo(GURL("http://defaultturl/k%20%20foo%20%20bar"), 782 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 783 ASCIIToUTF16("k foo bar")) } }, 784 // Note in the above test case we don't test trailing whitespace because 785 // SearchProvider still doesn't handle this well. See related bugs: 786 // 102690, 99239, 164635. 787 788 // Keywords can be prefixed by certain things that should get ignored 789 // when constructing the keyword match. 790 { ASCIIToUTF16("www.k foo"), 2, 791 { ResultInfo(GURL("http://keyword/foo"), 792 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 793 ASCIIToUTF16("k foo")), 794 ResultInfo(GURL("http://defaultturl/www.k%20foo"), 795 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 796 ASCIIToUTF16("www.k foo")) } }, 797 { ASCIIToUTF16("http://k foo"), 2, 798 { ResultInfo(GURL("http://keyword/foo"), 799 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 800 ASCIIToUTF16("k foo")), 801 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"), 802 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 803 ASCIIToUTF16("http://k foo")) } }, 804 { ASCIIToUTF16("http://www.k foo"), 2, 805 { ResultInfo(GURL("http://keyword/foo"), 806 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 807 ASCIIToUTF16("k foo")), 808 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"), 809 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 810 ASCIIToUTF16("http://www.k foo")) } }, 811 812 // A keyword with no remaining input shouldn't get a keyword 813 // verbatim match. 814 { ASCIIToUTF16("k"), 1, 815 { ResultInfo(GURL("http://defaultturl/k"), 816 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 817 ASCIIToUTF16("k")) } }, 818 { ASCIIToUTF16("k "), 1, 819 { ResultInfo(GURL("http://defaultturl/k%20"), 820 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 821 ASCIIToUTF16("k ")) } } 822 823 // The fact that verbatim queries to keyword are handled by KeywordProvider 824 // not SearchProvider is tested in 825 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc. 826 }; 827 828 // Test not in keyword mode. 829 RunTest(cases, arraysize(cases), false); 830 831 // Test in keyword mode. (Both modes should give the same result.) 832 RunTest(cases, arraysize(cases), true); 833 } 834 835 // Ensures command-line flags are reflected in the URLs the search provider 836 // generates. 837 TEST_F(SearchProviderTest, CommandLineOverrides) { 838 TemplateURLService* turl_model = 839 TemplateURLServiceFactory::GetForProfile(&profile_); 840 841 TemplateURLData data; 842 data.short_name = ASCIIToUTF16("default"); 843 data.SetKeyword(data.short_name); 844 data.SetURL("{google:baseURL}{searchTerms}"); 845 default_t_url_ = new TemplateURL(&profile_, data); 846 turl_model->Add(default_t_url_); 847 turl_model->SetDefaultSearchProvider(default_t_url_); 848 849 CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL, 850 "http://www.bar.com/"); 851 CommandLine::ForCurrentProcess()->AppendSwitchASCII( 852 switches::kExtraSearchQueryParams, "a=b"); 853 854 TestData cases[] = { 855 { ASCIIToUTF16("k a"), 2, 856 { ResultInfo(GURL("http://keyword/a"), 857 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 858 ASCIIToUTF16("k a")), 859 ResultInfo(GURL("http://www.bar.com/k%20a?a=b"), 860 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 861 ASCIIToUTF16("k a")) } }, 862 }; 863 864 RunTest(cases, arraysize(cases), false); 865 } 866 867 // Verifies Navsuggest results don't set a TemplateURL, which Instant relies on. 868 // Also verifies that just the *first* navigational result is listed as a match 869 // if suggested relevance scores were not sent. 870 TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) { 871 QueryForInput(ASCIIToUTF16("a.c"), false, false); 872 873 // Make sure the default providers suggest service was queried. 874 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 875 SearchProvider::kDefaultProviderURLFetcherID); 876 ASSERT_TRUE(fetcher); 877 878 // Tell the SearchProvider the suggest query is done. 879 fetcher->set_response_code(200); 880 fetcher->SetResponseString( 881 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[]," 882 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]"); 883 fetcher->delegate()->OnURLFetchComplete(fetcher); 884 fetcher = NULL; 885 886 // Run till the history results complete. 887 RunTillProviderDone(); 888 889 // Make sure the only match is 'a.com' and it doesn't have a template_url. 890 AutocompleteMatch nav_match; 891 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match)); 892 EXPECT_TRUE(nav_match.keyword.empty()); 893 EXPECT_TRUE(nav_match.allowed_to_be_default_match); 894 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match)); 895 } 896 897 // Verifies that the most relevant suggest results are added properly. 898 TEST_F(SearchProviderTest, SuggestRelevance) { 899 QueryForInput(ASCIIToUTF16("a"), false, false); 900 901 // Make sure the default provider's suggest service was queried. 902 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 903 SearchProvider::kDefaultProviderURLFetcherID); 904 ASSERT_TRUE(fetcher); 905 906 // Tell the SearchProvider the suggest query is done. 907 fetcher->set_response_code(200); 908 fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]"); 909 fetcher->delegate()->OnURLFetchComplete(fetcher); 910 fetcher = NULL; 911 912 // Run till the history results complete. 913 RunTillProviderDone(); 914 915 // Check the expected verbatim and (first 3) suggestions' relative relevances. 916 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4; 917 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim)); 918 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1)); 919 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2)); 920 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3)); 921 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4)); 922 EXPECT_GT(verbatim.relevance, match_a1.relevance); 923 EXPECT_GT(match_a1.relevance, match_a2.relevance); 924 EXPECT_GT(match_a2.relevance, match_a3.relevance); 925 EXPECT_TRUE(verbatim.allowed_to_be_default_match); 926 EXPECT_TRUE(match_a1.allowed_to_be_default_match); 927 EXPECT_TRUE(match_a2.allowed_to_be_default_match); 928 EXPECT_TRUE(match_a3.allowed_to_be_default_match); 929 } 930 931 // Verifies that suggest results with relevance scores are added 932 // properly when using the default fetcher. When adding a new test 933 // case to this test, please consider adding it to the tests in 934 // KeywordFetcherSuggestRelevance below. 935 TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) { 936 struct DefaultFetcherMatch { 937 std::string contents; 938 bool allowed_to_be_default_match; 939 }; 940 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false }; 941 struct { 942 const std::string json; 943 const DefaultFetcherMatch matches[4]; 944 const std::string inline_autocompletion; 945 } cases[] = { 946 // Ensure that suggestrelevance scores reorder matches. 947 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 948 { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch }, 949 std::string() }, 950 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 951 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 952 "\"google:suggestrelevance\":[1, 2]}]", 953 { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch }, 954 std::string() }, 955 956 // Without suggested relevance scores, we should only allow one 957 // navsuggest result to be be displayed. 958 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 959 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 960 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch }, 961 std::string() }, 962 963 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 964 // Negative values will have no effect; the calculated value will be used. 965 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 966 "\"google:suggestrelevance\":[9998]}]", 967 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch }, 968 std::string() }, 969 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 970 "\"google:suggestrelevance\":[9999]}]", 971 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 972 "1" }, 973 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 974 "\"google:suggestrelevance\":[9999]}]", 975 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 976 "1" }, 977 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 978 "\"google:suggestrelevance\":[9999]}]", 979 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 980 "1" }, 981 { "[\"a\",[\"http://a.com\"],[],[]," 982 "{\"google:suggesttype\":[\"NAVIGATION\"]," 983 "\"google:verbatimrelevance\":9999," 984 "\"google:suggestrelevance\":[9998]}]", 985 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch }, 986 std::string() }, 987 { "[\"a\",[\"http://a.com\"],[],[]," 988 "{\"google:suggesttype\":[\"NAVIGATION\"]," 989 "\"google:verbatimrelevance\":9998," 990 "\"google:suggestrelevance\":[9999]}]", 991 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 992 ".com" }, 993 { "[\"a\",[\"http://a.com\"],[],[]," 994 "{\"google:suggesttype\":[\"NAVIGATION\"]," 995 "\"google:verbatimrelevance\":0," 996 "\"google:suggestrelevance\":[9999]}]", 997 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 998 ".com" }, 999 { "[\"a\",[\"http://a.com\"],[],[]," 1000 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1001 "\"google:verbatimrelevance\":-1," 1002 "\"google:suggestrelevance\":[9999]}]", 1003 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1004 ".com" }, 1005 1006 // Ensure that both types of relevance scores reorder matches together. 1007 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1008 "\"google:verbatimrelevance\":9998}]", 1009 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch }, 1010 "1" }, 1011 1012 // Ensure that only inlinable matches may be ranked as the highest result. 1013 // Ignore all suggested relevance scores if this constraint is violated. 1014 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1015 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch }, 1016 std::string() }, 1017 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1018 "\"google:verbatimrelevance\":0}]", 1019 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch }, 1020 std::string() }, 1021 { "[\"a\",[\"http://b.com\"],[],[]," 1022 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1023 "\"google:suggestrelevance\":[9999]}]", 1024 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch }, 1025 std::string() }, 1026 { "[\"a\",[\"http://b.com\"],[],[]," 1027 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1028 "\"google:suggestrelevance\":[9999]," 1029 "\"google:verbatimrelevance\":0}]", 1030 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch }, 1031 std::string() }, 1032 1033 // Ensure that the top result is ranked as highly as calculated verbatim. 1034 // Ignore the suggested verbatim relevance if this constraint is violated. 1035 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1036 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1037 std::string() }, 1038 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1039 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1040 std::string() }, 1041 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1042 "\"google:verbatimrelevance\":0}]", 1043 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1044 std::string() }, 1045 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1046 "\"google:verbatimrelevance\":0}]", 1047 { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch }, 1048 std::string() }, 1049 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1050 "\"google:verbatimrelevance\":2}]", 1051 { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch }, 1052 std::string() }, 1053 { "[\"a\",[\"http://a.com\"],[],[]," 1054 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1055 "\"google:suggestrelevance\":[1]," 1056 "\"google:verbatimrelevance\":0}]", 1057 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch }, 1058 std::string() }, 1059 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1060 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1061 "\"google:suggestrelevance\":[1, 2]," 1062 "\"google:verbatimrelevance\":0}]", 1063 { { "a", true }, { "a2.com", true }, { "a1.com", true }, kEmptyMatch }, 1064 std::string() }, 1065 1066 // Ensure that all suggestions are considered, regardless of order. 1067 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1068 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1069 { { "a", true }, { "h", false }, { "g", false }, { "f", false } }, 1070 std::string() }, 1071 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1072 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1073 "\"http://h.com\"],[],[]," 1074 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1075 "\"NAVIGATION\", \"NAVIGATION\"," 1076 "\"NAVIGATION\", \"NAVIGATION\"," 1077 "\"NAVIGATION\"]," 1078 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1079 { { "a", true }, { "h.com", false }, { "g.com", false }, 1080 { "f.com", false } }, 1081 std::string() }, 1082 1083 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1084 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1085 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch }, 1086 std::string() }, 1087 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1088 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1089 std::string() }, 1090 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1091 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1092 "\"google:suggestrelevance\":[1]}]", 1093 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch }, 1094 std::string() }, 1095 { "[\"a\",[\"http://a1.com\"],[],[]," 1096 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1097 "\"google:suggestrelevance\":[9999, 1]}]", 1098 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch }, 1099 std::string() }, 1100 1101 // Ensure that all 'verbatim' results are merged with their maximum score. 1102 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1103 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1104 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch }, 1105 "2" }, 1106 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1107 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1108 "\"google:verbatimrelevance\":0}]", 1109 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch }, 1110 "2" }, 1111 1112 // Ensure that verbatim is always generated without other suggestions. 1113 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 1114 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1115 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1116 std::string() }, 1117 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1118 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1119 std::string() }, 1120 }; 1121 1122 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1123 QueryForInput(ASCIIToUTF16("a"), false, false); 1124 net::TestURLFetcher* fetcher = 1125 test_factory_.GetFetcherByID( 1126 SearchProvider::kDefaultProviderURLFetcherID); 1127 ASSERT_TRUE(fetcher); 1128 fetcher->set_response_code(200); 1129 fetcher->SetResponseString(cases[i].json); 1130 fetcher->delegate()->OnURLFetchComplete(fetcher); 1131 RunTillProviderDone(); 1132 1133 const std::string description = "for input with json=" + cases[i].json; 1134 const ACMatches& matches = provider_->matches(); 1135 // The top match must inline and score as highly as calculated verbatim. 1136 ASSERT_FALSE(matches.empty()); 1137 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1138 matches[0].inline_autocompletion) << description; 1139 EXPECT_GE(matches[0].relevance, 1300) << description; 1140 1141 size_t j = 0; 1142 // Ensure that the returned matches equal the expectations. 1143 for (; j < matches.size(); ++j) { 1144 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1145 matches[j].contents) << description; 1146 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 1147 matches[j].allowed_to_be_default_match) << description; 1148 } 1149 // Ensure that no expected matches are missing. 1150 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1151 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1152 "Case # " << i << " " << description; 1153 } 1154 } 1155 1156 // This test is like DefaultFetcherSuggestRelevance above except it enables 1157 // the field trial that causes the omnibox to be willing to reorder matches 1158 // to guarantee the top result is a legal default match. This field trial 1159 // causes SearchProvider to allow some constraints to be violated that it 1160 // wouldn't normally because the omnibox will fix the problems later. 1161 TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevanceWithReorder) { 1162 struct DefaultFetcherMatch { 1163 std::string contents; 1164 bool allowed_to_be_default_match; 1165 }; 1166 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false }; 1167 struct { 1168 const std::string json; 1169 const DefaultFetcherMatch matches[4]; 1170 const std::string inline_autocompletion; 1171 } cases[] = { 1172 // Ensure that suggestrelevance scores reorder matches. 1173 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1174 { { "a", true }, { "c", false }, { "b", false }, 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 std::string() }, 1181 1182 // Without suggested relevance scores, we should only allow one 1183 // navsuggest result to be be displayed. 1184 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1185 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1186 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch }, 1187 std::string() }, 1188 1189 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1190 // Negative values will have no effect; the calculated value will be used. 1191 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1192 "\"google:suggestrelevance\":[9998]}]", 1193 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1194 std::string() }, 1195 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1196 "\"google:suggestrelevance\":[9999]}]", 1197 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1198 "1" }, 1199 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1200 "\"google:suggestrelevance\":[9999]}]", 1201 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1202 "1" }, 1203 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1204 "\"google:suggestrelevance\":[9999]}]", 1205 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1206 "1" }, 1207 { "[\"a\",[\"http://a.com\"],[],[]," 1208 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1209 "\"google:verbatimrelevance\":9999," 1210 "\"google:suggestrelevance\":[9998]}]", 1211 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch }, 1212 std::string() }, 1213 { "[\"a\",[\"http://a.com\"],[],[]," 1214 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1215 "\"google:verbatimrelevance\":9998," 1216 "\"google:suggestrelevance\":[9999]}]", 1217 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1218 ".com" }, 1219 { "[\"a\",[\"http://a.com\"],[],[]," 1220 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1221 "\"google:verbatimrelevance\":0," 1222 "\"google:suggestrelevance\":[9999]}]", 1223 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1224 ".com" }, 1225 { "[\"a\",[\"http://a.com\"],[],[]," 1226 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1227 "\"google:verbatimrelevance\":-1," 1228 "\"google:suggestrelevance\":[9999]}]", 1229 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1230 ".com" }, 1231 1232 // Ensure that both types of relevance scores reorder matches together. 1233 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1234 "\"google:verbatimrelevance\":9998}]", 1235 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch }, 1236 "1" }, 1237 1238 // Allow non-inlineable matches to be the highest-scoring match but, 1239 // if the result set lacks a single inlineable result, abandon suggested 1240 // relevance scores entirely. 1241 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1242 { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1243 std::string() }, 1244 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1245 "\"google:verbatimrelevance\":0}]", 1246 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch }, 1247 std::string() }, 1248 { "[\"a\",[\"http://b.com\"],[],[]," 1249 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1250 "\"google:suggestrelevance\":[9999]}]", 1251 { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1252 std::string() }, 1253 { "[\"a\",[\"http://b.com\"],[],[]," 1254 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1255 "\"google:suggestrelevance\":[9999]," 1256 "\"google:verbatimrelevance\":0}]", 1257 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch }, 1258 std::string() }, 1259 1260 // Allow low-scoring matches. 1261 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1262 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1263 "1" }, 1264 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1265 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1266 "1" }, 1267 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1268 "\"google:verbatimrelevance\":0}]", 1269 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1270 "1" }, 1271 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1272 "\"google:verbatimrelevance\":0}]", 1273 { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1274 "2" }, 1275 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1276 "\"google:verbatimrelevance\":2}]", 1277 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch }, 1278 "2" }, 1279 { "[\"a\",[\"http://a.com\"],[],[]," 1280 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1281 "\"google:suggestrelevance\":[1]," 1282 "\"google:verbatimrelevance\":0}]", 1283 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1284 ".com" }, 1285 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1286 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1287 "\"google:suggestrelevance\":[1, 2]," 1288 "\"google:verbatimrelevance\":0}]", 1289 { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch }, 1290 "2.com" }, 1291 1292 // Ensure that all suggestions are considered, regardless of order. 1293 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1294 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1295 { { "a", true }, { "h", false }, { "g", false }, { "f", false } }, 1296 std::string() }, 1297 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1298 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1299 "\"http://h.com\"],[],[]," 1300 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1301 "\"NAVIGATION\", \"NAVIGATION\"," 1302 "\"NAVIGATION\", \"NAVIGATION\"," 1303 "\"NAVIGATION\"]," 1304 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1305 { { "a", true }, { "h.com", false }, { "g.com", false }, 1306 { "f.com", false } }, 1307 std::string() }, 1308 1309 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1310 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1311 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch }, 1312 std::string() }, 1313 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1314 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1315 std::string() }, 1316 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1317 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1318 "\"google:suggestrelevance\":[1]}]", 1319 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch }, 1320 std::string() }, 1321 { "[\"a\",[\"http://a1.com\"],[],[]," 1322 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1323 "\"google:suggestrelevance\":[9999, 1]}]", 1324 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch }, 1325 std::string() }, 1326 1327 // Ensure that all 'verbatim' results are merged with their maximum score. 1328 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1329 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1330 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch }, 1331 "2" }, 1332 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1333 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1334 "\"google:verbatimrelevance\":0}]", 1335 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch }, 1336 "2" }, 1337 1338 // Ensure that verbatim is always generated without other suggestions. 1339 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 1340 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1341 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1342 std::string() }, 1343 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1344 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1345 std::string() }, 1346 }; 1347 1348 std::map<std::string, std::string> params; 1349 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) + 1350 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleEnabled; 1351 ASSERT_TRUE(chrome_variations::AssociateVariationParams( 1352 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); 1353 base::FieldTrialList::CreateFieldTrial( 1354 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); 1355 1356 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1357 QueryForInput(ASCIIToUTF16("a"), false, false); 1358 net::TestURLFetcher* fetcher = 1359 test_factory_.GetFetcherByID( 1360 SearchProvider::kDefaultProviderURLFetcherID); 1361 ASSERT_TRUE(fetcher); 1362 fetcher->set_response_code(200); 1363 fetcher->SetResponseString(cases[i].json); 1364 fetcher->delegate()->OnURLFetchComplete(fetcher); 1365 RunTillProviderDone(); 1366 1367 const std::string description = "for input with json=" + cases[i].json; 1368 const ACMatches& matches = provider_->matches(); 1369 // The top match must inline and score as highly as calculated verbatim. 1370 ASSERT_FALSE(matches.empty()); 1371 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1372 matches[0].inline_autocompletion) << description; 1373 1374 size_t j = 0; 1375 // Ensure that the returned matches equal the expectations. 1376 for (; j < matches.size(); ++j) { 1377 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1378 matches[j].contents) << description; 1379 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 1380 matches[j].allowed_to_be_default_match) << description; 1381 } 1382 // Ensure that no expected matches are missing. 1383 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1384 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1385 "Case # " << i << " " << description; 1386 } 1387 } 1388 1389 // Verifies that suggest results with relevance scores are added 1390 // properly when using the keyword fetcher. This is similar to the 1391 // test DefaultFetcherSuggestRelevance above but this uses inputs that 1392 // trigger keyword suggestions (i.e., "k a" rather than "a") and has 1393 // different expectations (because now the results are a mix of 1394 // keyword suggestions and default provider suggestions). When a new 1395 // test is added to this TEST_F, please consider if it would be 1396 // appropriate to add to DefaultFetcherSuggestRelevance as well. 1397 TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) { 1398 struct KeywordFetcherMatch { 1399 std::string contents; 1400 bool from_keyword; 1401 bool allowed_to_be_default_match; 1402 }; 1403 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false }; 1404 struct { 1405 const std::string json; 1406 const KeywordFetcherMatch matches[5]; 1407 const std::string inline_autocompletion; 1408 } cases[] = { 1409 // Ensure that suggest relevance scores reorder matches and that 1410 // the keyword verbatim (lacking a suggested verbatim score) beats 1411 // the default provider verbatim. 1412 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1413 { { "a", true, true }, 1414 { "k a", false, true }, 1415 { "c", true, false }, 1416 { "b", true, false }, 1417 kEmptyMatch }, 1418 std::string() }, 1419 // Again, check that relevance scores reorder matches, just this 1420 // time with navigation matches. This also checks that with 1421 // suggested relevance scores we allow multiple navsuggest results. 1422 // It's odd that navsuggest results that come from a keyword 1423 // provider are marked as not a keyword result. I think this 1424 // comes from them not going to a keyword search engine). 1425 // TODO(mpearson): Investigate the implications (if any) of 1426 // tagging these results appropriately. If so, do it because it 1427 // makes more sense. 1428 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[]," 1429 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1430 "\"google:suggestrelevance\":[1301, 1302, 1303]}]", 1431 { { "a", true, true }, 1432 { "d", true, false }, 1433 { "c.com", false, false }, 1434 { "b.com", false, false }, 1435 { "k a", false, true }, }, 1436 std::string() }, 1437 1438 // Without suggested relevance scores, we should only allow one 1439 // navsuggest result to be be displayed. 1440 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1441 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1442 { { "a", true, true }, 1443 { "b.com", false, false }, 1444 { "k a", false, true }, 1445 kEmptyMatch, kEmptyMatch }, 1446 std::string() }, 1447 1448 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1449 // Negative values will have no effect; the calculated value will be used. 1450 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1451 "\"google:suggestrelevance\":[9998]}]", 1452 { { "a", true, true }, 1453 { "a1", true, true }, 1454 { "k a", false, true }, 1455 kEmptyMatch, kEmptyMatch }, 1456 std::string() }, 1457 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1458 "\"google:suggestrelevance\":[9999]}]", 1459 { { "a1", true, true }, 1460 { "a", true, true }, 1461 { "k a", false, true }, 1462 kEmptyMatch, kEmptyMatch }, 1463 "1" }, 1464 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1465 "\"google:suggestrelevance\":[9999]}]", 1466 { { "a1", true, true }, 1467 { "k a", false, true }, 1468 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1469 "1" }, 1470 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1471 "\"google:suggestrelevance\":[9999]}]", 1472 { { "a1", true, true }, 1473 { "a", true, true }, 1474 { "k a", false, true }, 1475 kEmptyMatch, kEmptyMatch }, 1476 "1" }, 1477 { "[\"a\",[\"http://a.com\"],[],[]," 1478 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1479 "\"google:verbatimrelevance\":9999," 1480 "\"google:suggestrelevance\":[9998]}]", 1481 { { "a", true, true }, 1482 { "a.com", false, true }, 1483 { "k a", false, true }, 1484 kEmptyMatch, kEmptyMatch }, 1485 std::string() }, 1486 1487 // Ensure that both types of relevance scores reorder matches together. 1488 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1489 "\"google:verbatimrelevance\":9998}]", 1490 { { "a1", true, true }, 1491 { "a", true, true }, 1492 { "a2", true, true }, 1493 { "k a", false, true }, 1494 kEmptyMatch }, 1495 "1" }, 1496 1497 // Ensure that only inlinable matches may be ranked as the highest result. 1498 // Ignore all suggested relevance scores if this constraint is violated. 1499 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1500 { { "a", true, true }, 1501 { "b", true, false }, 1502 { "k a", false, true }, 1503 kEmptyMatch, kEmptyMatch }, 1504 std::string() }, 1505 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1506 "\"google:verbatimrelevance\":0}]", 1507 { { "a", true, true }, 1508 { "b", true, false }, 1509 { "k a", false, true }, 1510 kEmptyMatch, kEmptyMatch }, 1511 std::string() }, 1512 { "[\"a\",[\"http://b.com\"],[],[]," 1513 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1514 "\"google:suggestrelevance\":[9999]}]", 1515 { { "a", true, true }, 1516 { "b.com", false, false }, 1517 { "k a", false, true }, 1518 kEmptyMatch, kEmptyMatch }, 1519 std::string() }, 1520 { "[\"a\",[\"http://b.com\"],[],[]," 1521 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1522 "\"google:suggestrelevance\":[9999]," 1523 "\"google:verbatimrelevance\":0}]", 1524 { { "a", true, true }, 1525 { "b.com", false, false }, 1526 { "k a", false, true }, 1527 kEmptyMatch, kEmptyMatch }, 1528 std::string() }, 1529 1530 // Ensure that the top result is ranked as highly as calculated verbatim. 1531 // Ignore the suggested verbatim relevance if this constraint is violated. 1532 // Note that keyword suggestions by default (not in suggested relevance 1533 // mode) score more highly than the default verbatim. 1534 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1535 { { "a", true, true }, 1536 { "a1", true, true }, 1537 { "k a", false, true }, 1538 kEmptyMatch, kEmptyMatch }, 1539 std::string() }, 1540 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1541 { { "a", true, true }, 1542 { "a1", true, true }, 1543 { "k a", false, true }, 1544 kEmptyMatch, kEmptyMatch}, 1545 std::string() }, 1546 // Continuing the same category of tests, but make sure we keep the 1547 // suggested relevance scores even as we discard the verbatim relevance 1548 // scores. 1549 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1550 "\"google:verbatimrelevance\":0}]", 1551 { { "a", true, true }, 1552 { "k a", false, true }, 1553 { "a1", true, true }, 1554 kEmptyMatch, kEmptyMatch}, 1555 std::string() }, 1556 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1557 "\"google:verbatimrelevance\":0}]", 1558 { { "a", true, true }, 1559 { "k a", false, true }, 1560 { "a2", true, true }, 1561 { "a1", true, true }, 1562 kEmptyMatch }, 1563 std::string() }, 1564 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1565 "\"google:verbatimrelevance\":2}]", 1566 { { "a", true, true }, 1567 { "k a", false, true }, 1568 { "a2", true, true }, 1569 { "a1", true, true }, 1570 kEmptyMatch }, 1571 std::string() }, 1572 1573 // Ensure that all suggestions are considered, regardless of order. 1574 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1575 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1576 { { "a", true, true }, 1577 { "k a", false, true }, 1578 { "h", true, false }, 1579 { "g", true, false }, 1580 { "f", true, false } }, 1581 std::string() }, 1582 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1583 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1584 "\"http://h.com\"],[],[]," 1585 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1586 "\"NAVIGATION\", \"NAVIGATION\"," 1587 "\"NAVIGATION\", \"NAVIGATION\"," 1588 "\"NAVIGATION\"]," 1589 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1590 { { "a", true, true }, 1591 { "k a", false, true }, 1592 { "h.com", false, false }, 1593 { "g.com", false, false }, 1594 { "f.com", false, false } }, 1595 std::string() }, 1596 1597 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1598 // Note that keyword suggestions by default (not in suggested relevance 1599 // mode) score more highly than the default verbatim. 1600 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1601 { { "a", true, true }, 1602 { "a1", true, true }, 1603 { "a2", true, true }, 1604 { "k a", false, true }, 1605 kEmptyMatch }, 1606 std::string() }, 1607 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1608 { { "a", true, true }, 1609 { "a1", true, true }, 1610 { "k a", false, true }, 1611 kEmptyMatch, kEmptyMatch}, 1612 std::string() }, 1613 // In this case, ignored the suggested relevance scores means we keep 1614 // only one navsuggest result. 1615 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1616 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1617 "\"google:suggestrelevance\":[1]}]", 1618 { { "a", true, true }, 1619 { "a1.com", false, true }, 1620 { "k a", false, true }, 1621 kEmptyMatch, kEmptyMatch}, 1622 std::string() }, 1623 { "[\"a\",[\"http://a1.com\"],[],[]," 1624 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1625 "\"google:suggestrelevance\":[9999, 1]}]", 1626 { { "a", true, true }, 1627 { "a1.com", false, true }, 1628 { "k a", false, true }, 1629 kEmptyMatch, kEmptyMatch}, 1630 std::string() }, 1631 1632 // Ensure that all 'verbatim' results are merged with their maximum score. 1633 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1634 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1635 { { "a2", true, true }, 1636 { "a", true, true }, 1637 { "a1", true, true }, 1638 { "k a", false, true }, 1639 kEmptyMatch }, 1640 "2" }, 1641 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1642 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1643 "\"google:verbatimrelevance\":0}]", 1644 { { "a2", true, true }, 1645 { "a", true, true }, 1646 { "a1", true, true }, 1647 { "k a", false, true }, 1648 kEmptyMatch }, 1649 "2" }, 1650 1651 // Ensure that verbatim is always generated without other suggestions. 1652 // TODO(mpearson): Ensure the value of verbatimrelevance is respected 1653 // (except when suggested relevances are ignored). 1654 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1655 { { "a", true, true }, 1656 { "k a", false, true }, 1657 kEmptyMatch, kEmptyMatch, kEmptyMatch}, 1658 std::string() }, 1659 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1660 { { "a", true, true }, 1661 { "k a", false, true }, 1662 kEmptyMatch, kEmptyMatch, kEmptyMatch}, 1663 std::string() }, 1664 1665 // Check that navsuggestions will be demoted below queries. 1666 // (Navsuggestions are not allowed to appear first.) In the process, 1667 // make sure the navsuggestions still remain in the same order. 1668 // First, check the situation where navsuggest scores more than verbatim 1669 // and there are no query suggestions. 1670 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1671 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1672 "\"google:verbatimrelevance\":9990," 1673 "\"google:suggestrelevance\":[9998, 9999]}]", 1674 { { "a", true, true }, 1675 { "a2.com", false, true }, 1676 { "a1.com", false, true }, 1677 { "k a", false, true }, 1678 kEmptyMatch }, 1679 std::string() }, 1680 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1681 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1682 "\"google:verbatimrelevance\":9990," 1683 "\"google:suggestrelevance\":[9999, 9998]}]", 1684 { { "a", true, true }, 1685 { "a1.com", false, true }, 1686 { "a2.com", false, true }, 1687 { "k a", false, true }, 1688 kEmptyMatch }, 1689 std::string() }, 1690 // Check when navsuggest scores more than verbatim and there is query 1691 // suggestion but it scores lower. 1692 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1693 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1694 "\"google:verbatimrelevance\":9990," 1695 "\"google:suggestrelevance\":[9998, 9999, 1300]}]", 1696 { { "a", true, true }, 1697 { "a2.com", false, true }, 1698 { "a1.com", false, true }, 1699 { "a3", true, true }, 1700 { "k a", false, true } }, 1701 std::string() }, 1702 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1703 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1704 "\"google:verbatimrelevance\":9990," 1705 "\"google:suggestrelevance\":[9999, 9998, 1300]}]", 1706 { { "a", true, true }, 1707 { "a1.com", false, true }, 1708 { "a2.com", false, true }, 1709 { "a3", true, true }, 1710 { "k a", false, true } }, 1711 std::string() }, 1712 // Check when navsuggest scores more than a query suggestion. There is 1713 // a verbatim but it scores lower. 1714 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1715 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1716 "\"google:verbatimrelevance\":9990," 1717 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1718 { { "a3", true, true }, 1719 { "a2.com", false, true }, 1720 { "a1.com", false, true }, 1721 { "a", true, true }, 1722 { "k a", false, true } }, 1723 "3" }, 1724 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1725 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1726 "\"google:verbatimrelevance\":9990," 1727 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1728 { { "a3", true, true }, 1729 { "a1.com", false, true }, 1730 { "a2.com", false, true }, 1731 { "a", true, true }, 1732 { "k a", false, true } }, 1733 "3" }, 1734 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1735 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1736 "\"google:verbatimrelevance\":0," 1737 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1738 { { "a3", true, true }, 1739 { "a2.com", false, true }, 1740 { "a1.com", false, true }, 1741 { "k a", false, true }, 1742 kEmptyMatch }, 1743 "3" }, 1744 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1745 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1746 "\"google:verbatimrelevance\":0," 1747 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1748 { { "a3", true, true }, 1749 { "a1.com", false, true }, 1750 { "a2.com", false, true }, 1751 { "k a", false, true }, 1752 kEmptyMatch }, 1753 "3" }, 1754 // Check when there is neither verbatim nor a query suggestion that, 1755 // because we can't demote navsuggestions below a query suggestion, 1756 // we abandon suggested relevance scores entirely. One consequence is 1757 // that this means we restore the keyword verbatim match. Note 1758 // that in this case of abandoning suggested relevance scores, we still 1759 // keep the navsuggestions in the same order, but we revert to only allowing 1760 // one navigation to appear because the scores are completely local. 1761 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1762 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1763 "\"google:verbatimrelevance\":0," 1764 "\"google:suggestrelevance\":[9998, 9999]}]", 1765 { { "a", true, true }, 1766 { "a2.com", false, true }, 1767 { "k a", false, true }, 1768 kEmptyMatch, kEmptyMatch}, 1769 std::string() }, 1770 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1771 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1772 "\"google:verbatimrelevance\":0," 1773 "\"google:suggestrelevance\":[9999, 9998]}]", 1774 { { "a", true, true }, 1775 { "a1.com", false, true }, 1776 { "k a", false, true }, 1777 kEmptyMatch, kEmptyMatch}, 1778 std::string() }, 1779 // More checks that everything works when it's not necessary to demote. 1780 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1781 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1782 "\"google:verbatimrelevance\":9990," 1783 "\"google:suggestrelevance\":[9997, 9998, 9999]}]", 1784 { { "a3", true, true }, 1785 { "a2.com", false, true }, 1786 { "a1.com", false, true }, 1787 { "a", true, true }, 1788 { "k a", false, true } }, 1789 "3" }, 1790 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1791 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1792 "\"google:verbatimrelevance\":9990," 1793 "\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1794 { { "a3", true, true }, 1795 { "a1.com", false, true }, 1796 { "a2.com", false, true }, 1797 { "a", true, true }, 1798 { "k a", false, true } }, 1799 "3" }, 1800 }; 1801 1802 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1803 QueryForInput(ASCIIToUTF16("k a"), false, true); 1804 1805 // Set up a default fetcher with no results. 1806 net::TestURLFetcher* default_fetcher = 1807 test_factory_.GetFetcherByID( 1808 SearchProvider::kDefaultProviderURLFetcherID); 1809 ASSERT_TRUE(default_fetcher); 1810 default_fetcher->set_response_code(200); 1811 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 1812 default_fetcher = NULL; 1813 1814 // Set up a keyword fetcher with provided results. 1815 net::TestURLFetcher* keyword_fetcher = 1816 test_factory_.GetFetcherByID( 1817 SearchProvider::kKeywordProviderURLFetcherID); 1818 ASSERT_TRUE(keyword_fetcher); 1819 keyword_fetcher->set_response_code(200); 1820 keyword_fetcher->SetResponseString(cases[i].json); 1821 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 1822 keyword_fetcher = NULL; 1823 RunTillProviderDone(); 1824 1825 const std::string description = "for input with json=" + cases[i].json; 1826 const ACMatches& matches = provider_->matches(); 1827 // The top match must inline and score as highly as calculated verbatim. 1828 ASSERT_FALSE(matches.empty()); 1829 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1830 matches[0].inline_autocompletion) << description; 1831 EXPECT_GE(matches[0].relevance, 1300) << description; 1832 1833 size_t j = 0; 1834 // Ensure that the returned matches equal the expectations. 1835 for (; j < matches.size(); ++j) { 1836 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1837 matches[j].contents) << description; 1838 EXPECT_EQ(cases[i].matches[j].from_keyword, 1839 matches[j].keyword == ASCIIToUTF16("k")) << description; 1840 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 1841 matches[j].allowed_to_be_default_match) << description; 1842 } 1843 // Ensure that no expected matches are missing. 1844 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1845 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1846 "Case # " << i << " " << description; 1847 } 1848 } 1849 1850 TEST_F(SearchProviderTest, LocalAndRemoteRelevances) { 1851 // Enable Instant Extended in order to allow an increased number of 1852 // suggestions. 1853 chrome::EnableInstantExtendedAPIForTesting(); 1854 1855 // We hardcode the string "term1" below, so ensure that the search term that 1856 // got added to history already is that string. 1857 ASSERT_EQ(ASCIIToUTF16("term1"), term1_); 1858 string16 term = term1_.substr(0, term1_.length() - 1); 1859 1860 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2); 1861 profile_.BlockUntilHistoryProcessesPendingRequests(); 1862 1863 struct { 1864 const string16 input; 1865 const std::string json; 1866 const std::string matches[6]; 1867 } cases[] = { 1868 // The history results outscore the default verbatim score. term2 has more 1869 // visits so it outscores term1. The suggestions are still returned since 1870 // they're server-scored. 1871 { term, 1872 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 1873 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 1874 "\"google:suggestrelevance\":[1, 2, 3]}]", 1875 { "term2", "term1", "term", "a3", "a2", "a1" } }, 1876 // Because we already have three suggestions by the time we see the history 1877 // results, they don't get returned. 1878 { term, 1879 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 1880 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 1881 "\"google:verbatimrelevance\":1450," 1882 "\"google:suggestrelevance\":[1440, 1430, 1420]}]", 1883 { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } }, 1884 // If we only have two suggestions, we have room for a history result. 1885 { term, 1886 "[\"term\",[\"a1\", \"a2\"],[],[]," 1887 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," 1888 "\"google:verbatimrelevance\":1450," 1889 "\"google:suggestrelevance\":[1430, 1410]}]", 1890 { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } }, 1891 // If we have more than three suggestions, they should all be returned as 1892 // long as we have enough total space for them. 1893 { term, 1894 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 1895 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 1896 "\"google:verbatimrelevance\":1450," 1897 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]", 1898 { "term", "a1", "a2", "a3", "a4", kNotApplicable } }, 1899 { term, 1900 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[]," 1901 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"," 1902 "\"QUERY\", \"QUERY\"]," 1903 "\"google:verbatimrelevance\":1450," 1904 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]", 1905 { "term", "a1", "a2", "a3", "a4", "a5" } }, 1906 { term, 1907 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 1908 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 1909 "\"google:verbatimrelevance\":1450," 1910 "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]", 1911 { "term", "a1", "a2", "term2", "a3", "a4" } }, 1912 // When the input looks like a URL, we disallow having a query as the 1913 // highest-ranking result. If the query was provided by a suggestion, we 1914 // reset the suggest scores to enforce this (see 1915 // SearchProvider::UpdateMatches()). Even if we reset the suggest scores, 1916 // however, we should still allow navsuggestions to be treated as 1917 // server-provided. 1918 { ASCIIToUTF16("a.com"), 1919 "[\"a.com\",[\"a1\", \"a2\", \"a.com/1\", \"a.com/2\"],[],[]," 1920 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"NAVIGATION\"," 1921 "\"NAVIGATION\"]," 1922 // A verbatim query for URL-like input scores 850, so the navigation 1923 // scores here should bracket it. 1924 "\"google:suggestrelevance\":[9999, 9998, 900, 800]}]", 1925 { "a.com/1", "a.com", "a.com/2", "a1", kNotApplicable, kNotApplicable } }, 1926 }; 1927 1928 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1929 QueryForInput(cases[i].input, false, false); 1930 net::TestURLFetcher* fetcher = 1931 test_factory_.GetFetcherByID( 1932 SearchProvider::kDefaultProviderURLFetcherID); 1933 ASSERT_TRUE(fetcher); 1934 fetcher->set_response_code(200); 1935 fetcher->SetResponseString(cases[i].json); 1936 fetcher->delegate()->OnURLFetchComplete(fetcher); 1937 RunTillProviderDone(); 1938 1939 const std::string description = "for input with json=" + cases[i].json; 1940 const ACMatches& matches = provider_->matches(); 1941 1942 // Ensure no extra matches are present. 1943 ASSERT_LE(matches.size(), 6U); 1944 1945 size_t j = 0; 1946 // Ensure that the returned matches equal the expectations. 1947 for (; j < matches.size(); ++j) 1948 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), 1949 matches[j].contents) << description; 1950 // Ensure that no expected matches are missing. 1951 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1952 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) << 1953 "Case # " << i << " " << description; 1954 } 1955 } 1956 1957 // Verifies suggest relevance behavior for URL input. 1958 TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) { 1959 struct DefaultFetcherUrlInputMatch { 1960 const std::string match_contents; 1961 AutocompleteMatch::Type match_type; 1962 bool allowed_to_be_default_match; 1963 }; 1964 const DefaultFetcherUrlInputMatch kEmptyMatch = 1965 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false }; 1966 struct { 1967 const std::string input; 1968 const std::string json; 1969 const DefaultFetcherUrlInputMatch output[4]; 1970 } cases[] = { 1971 // Ensure topmost NAVIGATION matches are allowed for URL input. 1972 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[]," 1973 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1974 "\"google:suggestrelevance\":[9999]}]", 1975 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true }, 1976 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 1977 kEmptyMatch, kEmptyMatch } }, 1978 1979 // Ensure topmost SUGGEST matches are not allowed for URL input. 1980 // SearchProvider disregards search and verbatim suggested relevances. 1981 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 1982 "{\"google:suggestrelevance\":[9999]}]", 1983 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 1984 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 1985 kEmptyMatch, kEmptyMatch } }, 1986 { "a.com", "[\"a.com\",[\"a.com/a\"],[],[]," 1987 "{\"google:suggestrelevance\":[9999]}]", 1988 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 1989 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 1990 kEmptyMatch, kEmptyMatch } }, 1991 1992 // Ensure the fallback mechanism allows inlinable NAVIGATION matches. 1993 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 1994 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1995 "\"google:suggestrelevance\":[9999, 9998]}]", 1996 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true }, 1997 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 1998 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 1999 kEmptyMatch } }, 2000 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 2001 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2002 "\"google:suggestrelevance\":[9998, 9997]," 2003 "\"google:verbatimrelevance\":9999}]", 2004 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true }, 2005 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2006 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2007 kEmptyMatch } }, 2008 2009 // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches. 2010 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 2011 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2012 "\"google:suggestrelevance\":[9999, 9998]}]", 2013 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2014 { "abc.com", AutocompleteMatchType::NAVSUGGEST, false }, 2015 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2016 kEmptyMatch } }, 2017 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 2018 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2019 "\"google:suggestrelevance\":[9998, 9997]," 2020 "\"google:verbatimrelevance\":9999}]", 2021 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2022 { "abc.com", AutocompleteMatchType::NAVSUGGEST, false }, 2023 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2024 kEmptyMatch } }, 2025 }; 2026 2027 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2028 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 2029 net::TestURLFetcher* fetcher = 2030 test_factory_.GetFetcherByID( 2031 SearchProvider::kDefaultProviderURLFetcherID); 2032 ASSERT_TRUE(fetcher); 2033 fetcher->set_response_code(200); 2034 fetcher->SetResponseString(cases[i].json); 2035 fetcher->delegate()->OnURLFetchComplete(fetcher); 2036 RunTillProviderDone(); 2037 2038 size_t j = 0; 2039 const ACMatches& matches = provider_->matches(); 2040 // Ensure that the returned matches equal the expectations. 2041 for (; j < matches.size(); ++j) { 2042 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents), 2043 matches[j].contents); 2044 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type); 2045 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match, 2046 matches[j].allowed_to_be_default_match); 2047 } 2048 // Ensure that no expected matches are missing. 2049 for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) { 2050 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents); 2051 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES, 2052 cases[i].output[j].match_type); 2053 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match); 2054 } 2055 } 2056 } 2057 2058 // A basic test that verifies the field trial triggered parsing logic. 2059 TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) { 2060 QueryForInput(ASCIIToUTF16("foo"), false, false); 2061 2062 // Make sure the default providers suggest service was queried. 2063 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 2064 SearchProvider::kDefaultProviderURLFetcherID); 2065 ASSERT_TRUE(fetcher); 2066 2067 // Tell the SearchProvider the suggest query is done. 2068 fetcher->set_response_code(200); 2069 fetcher->SetResponseString( 2070 "[\"foo\",[\"foo bar\"],[\"\"],[]," 2071 "{\"google:suggesttype\":[\"QUERY\"]," 2072 "\"google:fieldtrialtriggered\":true}]"); 2073 fetcher->delegate()->OnURLFetchComplete(fetcher); 2074 fetcher = NULL; 2075 2076 // Run till the history results complete. 2077 RunTillProviderDone(); 2078 2079 { 2080 // Check for the match and field trial triggered bits. 2081 AutocompleteMatch match; 2082 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match)); 2083 ProvidersInfo providers_info; 2084 provider_->AddProviderInfo(&providers_info); 2085 ASSERT_EQ(1U, providers_info.size()); 2086 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 2087 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size()); 2088 } 2089 { 2090 // Reset the session and check that bits are reset. 2091 provider_->ResetSession(); 2092 ProvidersInfo providers_info; 2093 provider_->AddProviderInfo(&providers_info); 2094 ASSERT_EQ(1U, providers_info.size()); 2095 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 2096 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size()); 2097 } 2098 } 2099 2100 // Verifies inline autocompletion of navigational results. 2101 TEST_F(SearchProviderTest, NavigationInline) { 2102 struct { 2103 const std::string input; 2104 const std::string url; 2105 // Test the expected fill_into_edit, which may drop "http://". 2106 // Some cases do not trim "http://" to match from the start of the scheme. 2107 const std::string fill_into_edit; 2108 const std::string inline_autocompletion; 2109 const bool allowed_to_be_default_match; 2110 } cases[] = { 2111 // Do not inline matches that do not contain the input; trim http as needed. 2112 { "x", "http://www.abc.com", 2113 "www.abc.com", std::string(), false }, 2114 { "https:", "http://www.abc.com", 2115 "www.abc.com", std::string(), false }, 2116 { "abc.com/", "http://www.abc.com", 2117 "www.abc.com", std::string(), false }, 2118 { "http://www.abc.com/a", "http://www.abc.com", 2119 "http://www.abc.com", std::string(), false }, 2120 { "http://www.abc.com", "https://www.abc.com", 2121 "https://www.abc.com", std::string(), false }, 2122 { "http://abc.com", "ftp://abc.com", 2123 "ftp://abc.com", std::string(), false }, 2124 { "https://www.abc.com", "http://www.abc.com", 2125 "www.abc.com", std::string(), false }, 2126 { "ftp://abc.com", "http://abc.com", 2127 "abc.com", std::string(), false }, 2128 2129 // Do not inline matches with invalid input prefixes; trim http as needed. 2130 { "ttp", "http://www.abc.com", 2131 "www.abc.com", std::string(), false }, 2132 { "://w", "http://www.abc.com", 2133 "www.abc.com", std::string(), false }, 2134 { "ww.", "http://www.abc.com", 2135 "www.abc.com", std::string(), false }, 2136 { ".ab", "http://www.abc.com", 2137 "www.abc.com", std::string(), false }, 2138 { "bc", "http://www.abc.com", 2139 "www.abc.com", std::string(), false }, 2140 { ".com", "http://www.abc.com", 2141 "www.abc.com", std::string(), false }, 2142 2143 // Do not inline matches that omit input domain labels; trim http as needed. 2144 { "www.a", "http://a.com", 2145 "a.com", std::string(), false }, 2146 { "http://www.a", "http://a.com", 2147 "http://a.com", std::string(), false }, 2148 { "www.a", "ftp://a.com", 2149 "ftp://a.com", std::string(), false }, 2150 { "ftp://www.a", "ftp://a.com", 2151 "ftp://a.com", std::string(), false }, 2152 2153 // Input matching but with nothing to inline will not yield an offset. 2154 { "abc.com", "http://www.abc.com", 2155 "www.abc.com", std::string(), false }, 2156 { "http://www.abc.com", "http://www.abc.com", 2157 "http://www.abc.com", std::string(), false }, 2158 2159 // Inline matches when the input is a leading substring of the scheme. 2160 { "h", "http://www.abc.com", 2161 "http://www.abc.com", "ttp://www.abc.com", true }, 2162 { "http", "http://www.abc.com", 2163 "http://www.abc.com", "://www.abc.com", true }, 2164 2165 // Inline matches when the input is a leading substring of the full URL. 2166 { "http:", "http://www.abc.com", 2167 "http://www.abc.com", "//www.abc.com", true }, 2168 { "http://w", "http://www.abc.com", 2169 "http://www.abc.com", "ww.abc.com", true }, 2170 { "http://www.", "http://www.abc.com", 2171 "http://www.abc.com", "abc.com", true }, 2172 { "http://www.ab", "http://www.abc.com", 2173 "http://www.abc.com", "c.com", true }, 2174 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 2175 "http://www.abc.com/path/file.htm?q=x#foo", 2176 "ath/file.htm?q=x#foo", 2177 true }, 2178 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 2179 "http://abc.com/path/file.htm?q=x#foo", 2180 "ath/file.htm?q=x#foo", true}, 2181 2182 // Inline matches with valid URLPrefixes; only trim "http://". 2183 { "w", "http://www.abc.com", 2184 "www.abc.com", "ww.abc.com", true }, 2185 { "www.a", "http://www.abc.com", 2186 "www.abc.com", "bc.com", true }, 2187 { "abc", "http://www.abc.com", 2188 "www.abc.com", ".com", true }, 2189 { "abc.c", "http://www.abc.com", 2190 "www.abc.com", "om", true }, 2191 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 2192 "www.abc.com/path/file.htm?q=x#foo", 2193 "ath/file.htm?q=x#foo", true }, 2194 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 2195 "abc.com/path/file.htm?q=x#foo", 2196 "ath/file.htm?q=x#foo", true }, 2197 2198 // Inline matches using the maximal URLPrefix components. 2199 { "h", "http://help.com", 2200 "help.com", "elp.com", true }, 2201 { "http", "http://http.com", 2202 "http.com", ".com", true }, 2203 { "h", "http://www.help.com", 2204 "www.help.com", "elp.com", true }, 2205 { "http", "http://www.http.com", 2206 "www.http.com", ".com", true }, 2207 { "w", "http://www.www.com", 2208 "www.www.com", "ww.com", true }, 2209 2210 // Test similar behavior for the ftp and https schemes. 2211 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 2212 "ftp://www.abc.com/path/file.htm?q=x#foo", 2213 "c.com/path/file.htm?q=x#foo", true }, 2214 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 2215 "ftp://www.abc.com/path/file.htm?q=x#foo", 2216 "c.com/path/file.htm?q=x#foo", true }, 2217 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 2218 "ftp://www.abc.com/path/file.htm?q=x#foo", 2219 "c.com/path/file.htm?q=x#foo", true }, 2220 { "ab", "ftp://abc.com/path/file.htm?q=x#foo", 2221 "ftp://abc.com/path/file.htm?q=x#foo", 2222 "c.com/path/file.htm?q=x#foo", true }, 2223 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 2224 "https://www.abc.com/path/file.htm?q=x#foo", 2225 "c.com/path/file.htm?q=x#foo", true }, 2226 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 2227 "https://www.abc.com/path/file.htm?q=x#foo", 2228 "c.com/path/file.htm?q=x#foo", true }, 2229 { "ab", "https://www.abc.com/path/file.htm?q=x#foo", 2230 "https://www.abc.com/path/file.htm?q=x#foo", 2231 "c.com/path/file.htm?q=x#foo", true }, 2232 { "ab", "https://abc.com/path/file.htm?q=x#foo", 2233 "https://abc.com/path/file.htm?q=x#foo", 2234 "c.com/path/file.htm?q=x#foo", true }, 2235 2236 // Forced query input should inline and retain the "?" prefix. 2237 { "?http://www.ab", "http://www.abc.com", 2238 "?http://www.abc.com", "c.com", true }, 2239 { "?www.ab", "http://www.abc.com", 2240 "?www.abc.com", "c.com", true }, 2241 { "?ab", "http://www.abc.com", 2242 "?www.abc.com", "c.com", true }, 2243 }; 2244 2245 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2246 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 2247 AutocompleteMatch match( 2248 provider_->NavigationToMatch(SearchProvider::NavigationResult( 2249 *provider_.get(), GURL(cases[i].url), string16(), false, 0, 2250 false))); 2251 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 2252 match.inline_autocompletion); 2253 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit); 2254 EXPECT_EQ(cases[i].allowed_to_be_default_match, 2255 match.allowed_to_be_default_match); 2256 } 2257 } 2258 2259 // Verifies that "http://" is not trimmed for input that is a leading substring. 2260 TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) { 2261 const string16 input(ASCIIToUTF16("ht")); 2262 const string16 url(ASCIIToUTF16("http://a.com")); 2263 const SearchProvider::NavigationResult result( 2264 *provider_.get(), GURL(url), string16(), false, 0, false); 2265 2266 // Check the offset and strings when inline autocompletion is allowed. 2267 QueryForInput(input, false, false); 2268 AutocompleteMatch match_inline(provider_->NavigationToMatch(result)); 2269 EXPECT_EQ(url, match_inline.fill_into_edit); 2270 EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion); 2271 EXPECT_TRUE(match_inline.allowed_to_be_default_match); 2272 EXPECT_EQ(url, match_inline.contents); 2273 2274 // Check the same offset and strings when inline autocompletion is prevented. 2275 QueryForInput(input, true, false); 2276 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result)); 2277 EXPECT_EQ(url, match_prevent.fill_into_edit); 2278 EXPECT_TRUE(match_prevent.inline_autocompletion.empty()); 2279 EXPECT_FALSE(match_prevent.allowed_to_be_default_match); 2280 EXPECT_EQ(url, match_prevent.contents); 2281 } 2282 2283 // Verifies that input "w" marks a more significant domain label than "www.". 2284 TEST_F(SearchProviderTest, NavigationInlineDomainClassify) { 2285 QueryForInput(ASCIIToUTF16("w"), false, false); 2286 AutocompleteMatch match( 2287 provider_->NavigationToMatch(SearchProvider::NavigationResult( 2288 *provider_.get(), GURL("http://www.wow.com"), string16(), false, 0, 2289 false))); 2290 EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion); 2291 EXPECT_TRUE(match.allowed_to_be_default_match); 2292 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit); 2293 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents); 2294 2295 // Ensure that the match for input "w" is marked on "wow" and not "www". 2296 ASSERT_EQ(3U, match.contents_class.size()); 2297 EXPECT_EQ(0U, match.contents_class[0].offset); 2298 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 2299 match.contents_class[0].style); 2300 EXPECT_EQ(4U, match.contents_class[1].offset); 2301 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL | 2302 AutocompleteMatch::ACMatchClassification::MATCH, 2303 match.contents_class[1].style); 2304 EXPECT_EQ(5U, match.contents_class[2].offset); 2305 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 2306 match.contents_class[2].style); 2307 } 2308 2309 TEST_F(SearchProviderTest, RemoveStaleResultsTest) { 2310 // TODO(mpearson): Consider expanding this test to explicitly cover 2311 // testing staleness for keyword results. 2312 struct { 2313 const std::string omnibox_input; 2314 const int verbatim_relevance; 2315 // These cached suggestions should already be sorted. 2316 // The particular number 5 as the length of the array is 2317 // unimportant; it's merely enough cached results to fully test 2318 // the functioning of RemoveAllStaleResults(). 2319 struct { 2320 const std::string suggestion; 2321 const bool is_navigation_result; 2322 const int relevance; 2323 // |expect_match| is true if this result should survive 2324 // RemoveAllStaleResults() filtering against |omnibox_input| below. 2325 const bool expect_match; 2326 } results[5]; 2327 } cases[] = { 2328 // Simple case: multiple query suggestions and no navsuggestions. 2329 // All query suggestions score less than search-what-you-typed and 2330 // thus none should be filtered because none will appear first. 2331 { "x", 1300, 2332 { { "food", false, 1299, true }, 2333 { "foobar", false, 1298, true }, 2334 { "crazy", false, 1297, true }, 2335 { "friend", false, 1296, true }, 2336 { kNotApplicable, false, 0, false } } }, 2337 2338 // Similarly simple cases, but the query suggestion appears first. 2339 { "f", 1200, 2340 { { "food", false, 1299, true }, 2341 { "foobar", false, 1298, true }, 2342 { "crazy", false, 1297, true }, 2343 { "friend", false, 1296, true }, 2344 { kNotApplicable, false, 0, false } } }, 2345 { "c", 1200, 2346 { { "food", false, 1299, false }, 2347 { "foobar", false, 1298, false }, 2348 { "crazy", false, 1297, true }, 2349 { "friend", false, 1296, true }, 2350 { kNotApplicable, false, 0, false } } }, 2351 { "x", 1200, 2352 { { "food", false, 1299, false }, 2353 { "foobar", false, 1298, false }, 2354 { "crazy", false, 1297, false }, 2355 { "friend", false, 1296, false }, 2356 { kNotApplicable, false, 0, false } } }, 2357 2358 // The same sort of cases, just using a mix of queries and navsuggestions. 2359 { "x", 1300, 2360 { { "http://food.com/", true, 1299, true }, 2361 { "foobar", false, 1298, true }, 2362 { "http://crazy.com/", true, 1297, true }, 2363 { "friend", false, 1296, true }, 2364 { "http://friend.com/", true, 1295, true } } }, 2365 { "f", 1200, 2366 { { "http://food.com/", true, 1299, true }, 2367 { "foobar", false, 1298, true }, 2368 { "http://crazy.com/", true, 1297, true }, 2369 { "friend", false, 1296, true }, 2370 { "http://friend.com/", true, 1295, true } } }, 2371 { "c", 1200, 2372 { { "http://food.com/", true, 1299, false }, 2373 { "foobar", false, 1298, false }, 2374 { "http://crazy.com/", true, 1297, true }, 2375 { "friend", false, 1296, true }, 2376 { "http://friend.com/", true, 1295, true } } }, 2377 { "x", 1200, 2378 { { "http://food.com/", true, 1299, false }, 2379 { "foobar", false, 1298, false }, 2380 { "http://crazy.com/", true, 1297, false }, 2381 { "friend", false, 1296, false }, 2382 { "http://friend.com/", true, 1295, false } } }, 2383 2384 // Run the three tests immediately above again, just with verbatim 2385 // suppressed. Note that in the last case, all results are filtered. 2386 // Because verbatim is also suppressed, SearchProvider will realize 2387 // in UpdateMatches() that it needs to restore verbatim to fulfill 2388 // its constraints. This restoration does not happen in 2389 // RemoveAllStaleResults() and hence is not tested here. This restoration 2390 // is tested in the DefaultFetcherSuggestRelevance test. 2391 { "f", 0, 2392 { { "http://food.com/", true, 1299, true }, 2393 { "foobar", false, 1298, true }, 2394 { "http://crazy.com/", true, 1297, true }, 2395 { "friend", false, 1296, true }, 2396 { "http://friend.com/", true, 1295, true } } }, 2397 { "c", 0, 2398 { { "http://food.com/", true, 1299, false }, 2399 { "foobar", false, 1298, false }, 2400 { "http://crazy.com/", true, 1297, true }, 2401 { "friend", false, 1296, true }, 2402 { "http://friend.com/", true, 1295, true } } }, 2403 { "x", 0, 2404 { { "http://food.com/", true, 1299, false }, 2405 { "foobar", false, 1298, false }, 2406 { "http://crazy.com/", true, 1297, false }, 2407 { "friend", false, 1296, false }, 2408 { "http://friend.com/", true, 1295, false } } }, 2409 2410 // The same sort of tests again, just with verbatim with a score 2411 // that would place it in between other suggestions. 2412 { "f", 1290, 2413 { { "http://food.com/", true, 1299, true }, 2414 { "foobar", false, 1288, true }, 2415 { "http://crazy.com/", true, 1277, true }, 2416 { "friend", false, 1266, true }, 2417 { "http://friend.com/", true, 1255, true } } }, 2418 { "c", 1290, 2419 { { "http://food.com/", true, 1299, false }, 2420 { "foobar", false, 1288, true }, 2421 { "http://crazy.com/", true, 1277, true }, 2422 { "friend", false, 1266, true }, 2423 { "http://friend.com/", true, 1255, true } } }, 2424 { "c", 1270, 2425 { { "http://food.com/", true, 1299, false }, 2426 { "foobar", false, 1288, false }, 2427 { "http://crazy.com/", true, 1277, true }, 2428 { "friend", false, 1266, true }, 2429 { "http://friend.com/", true, 1255, true } } }, 2430 { "x", 1280, 2431 { { "http://food.com/", true, 1299, false }, 2432 { "foobar", false, 1288, false }, 2433 { "http://crazy.com/", true, 1277, true }, 2434 { "friend", false, 1266, true }, 2435 { "http://friend.com/", true, 1255, true } } }, 2436 }; 2437 2438 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2439 // Initialize cached results for this test case. 2440 provider_->default_results_.verbatim_relevance = 2441 cases[i].verbatim_relevance; 2442 provider_->default_results_.navigation_results.clear(); 2443 provider_->default_results_.suggest_results.clear(); 2444 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) { 2445 const std::string& suggestion = cases[i].results[j].suggestion; 2446 if (suggestion == kNotApplicable) 2447 break; 2448 if (cases[i].results[j].is_navigation_result) { 2449 provider_->default_results_.navigation_results.push_back( 2450 SearchProvider::NavigationResult( 2451 *provider_.get(), GURL(suggestion), string16(), false, 2452 cases[i].results[j].relevance, false)); 2453 } else { 2454 provider_->default_results_.suggest_results.push_back( 2455 SearchProvider::SuggestResult(ASCIIToUTF16(suggestion), false, 2456 cases[i].results[j].relevance, 2457 false)); 2458 } 2459 } 2460 2461 provider_->input_ = AutocompleteInput( 2462 ASCIIToUTF16(cases[i].omnibox_input), string16::npos, string16(), 2463 GURL(), AutocompleteInput::INVALID_SPEC, false, false, true, 2464 AutocompleteInput::ALL_MATCHES); 2465 provider_->RemoveAllStaleResults(); 2466 2467 // Check cached results. 2468 SearchProvider::SuggestResults::const_iterator sug_it = 2469 provider_->default_results_.suggest_results.begin(); 2470 const SearchProvider::SuggestResults::const_iterator sug_end = 2471 provider_->default_results_.suggest_results.end(); 2472 SearchProvider::NavigationResults::const_iterator nav_it = 2473 provider_->default_results_.navigation_results.begin(); 2474 const SearchProvider::NavigationResults::const_iterator nav_end = 2475 provider_->default_results_.navigation_results.end(); 2476 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) { 2477 const std::string& suggestion = cases[i].results[j].suggestion; 2478 if (suggestion == kNotApplicable) 2479 continue; 2480 if (!cases[i].results[j].expect_match) 2481 continue; 2482 if (cases[i].results[j].is_navigation_result) { 2483 ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion; 2484 EXPECT_EQ(suggestion, nav_it->url().spec()); 2485 ++nav_it; 2486 } else { 2487 ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion; 2488 EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion()); 2489 ++sug_it; 2490 } 2491 } 2492 EXPECT_EQ(sug_end, sug_it); 2493 EXPECT_EQ(nav_end, nav_it); 2494 } 2495 } 2496