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