search_provider_unittest.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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 1007 // Ensure that the top result is ranked as highly as calculated verbatim. 1008 // Ignore the suggested verbatim relevance if this constraint is violated. 1009 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1010 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1011 std::string() }, 1012 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1013 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1014 std::string() }, 1015 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1016 "\"google:verbatimrelevance\":0}]", 1017 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1018 std::string() }, 1019 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1020 "\"google:verbatimrelevance\":0}]", 1021 { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch }, 1022 std::string() }, 1023 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1024 "\"google:verbatimrelevance\":2}]", 1025 { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch }, 1026 std::string() }, 1027 { "[\"a\",[\"http://a.com\"],[],[]," 1028 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1029 "\"google:suggestrelevance\":[1]," 1030 "\"google:verbatimrelevance\":0}]", 1031 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch }, 1032 std::string() }, 1033 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1034 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1035 "\"google:suggestrelevance\":[1, 2]," 1036 "\"google:verbatimrelevance\":0}]", 1037 { { "a", true }, { "a2.com", true }, { "a1.com", true }, kEmptyMatch }, 1038 std::string() }, 1039 1040 // Ensure that all suggestions are considered, regardless of order. 1041 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1042 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1043 { { "a", true }, { "h", false }, { "g", false }, { "f", false } }, 1044 std::string() }, 1045 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1046 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1047 "\"http://h.com\"],[],[]," 1048 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1049 "\"NAVIGATION\", \"NAVIGATION\"," 1050 "\"NAVIGATION\", \"NAVIGATION\"," 1051 "\"NAVIGATION\"]," 1052 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1053 { { "a", true }, { "h.com", false }, { "g.com", false }, 1054 { "f.com", false } }, 1055 std::string() }, 1056 1057 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1058 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1059 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch }, 1060 std::string() }, 1061 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1062 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1063 std::string() }, 1064 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1065 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1066 "\"google:suggestrelevance\":[1]}]", 1067 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch }, 1068 std::string() }, 1069 { "[\"a\",[\"http://a1.com\"],[],[]," 1070 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1071 "\"google:suggestrelevance\":[9999, 1]}]", 1072 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch }, 1073 std::string() }, 1074 1075 // Ensure that all 'verbatim' results are merged with their maximum score. 1076 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1077 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1078 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch }, 1079 "2" }, 1080 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1081 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1082 "\"google:verbatimrelevance\":0}]", 1083 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch }, 1084 "2" }, 1085 1086 // Ensure that verbatim is always generated without other suggestions. 1087 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 1088 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1089 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1090 std::string() }, 1091 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1092 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1093 std::string() }, 1094 }; 1095 1096 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1097 QueryForInput(ASCIIToUTF16("a"), false, false); 1098 net::TestURLFetcher* fetcher = 1099 test_factory_.GetFetcherByID( 1100 SearchProvider::kDefaultProviderURLFetcherID); 1101 ASSERT_TRUE(fetcher); 1102 fetcher->set_response_code(200); 1103 fetcher->SetResponseString(cases[i].json); 1104 fetcher->delegate()->OnURLFetchComplete(fetcher); 1105 RunTillProviderDone(); 1106 1107 const std::string description = "for input with json=" + cases[i].json; 1108 const ACMatches& matches = provider_->matches(); 1109 // The top match must inline and score as highly as calculated verbatim. 1110 ASSERT_FALSE(matches.empty()); 1111 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1112 matches[0].inline_autocompletion) << description; 1113 EXPECT_GE(matches[0].relevance, 1300) << description; 1114 1115 size_t j = 0; 1116 // Ensure that the returned matches equal the expectations. 1117 for (; j < matches.size(); ++j) { 1118 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1119 matches[j].contents) << description; 1120 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 1121 matches[j].allowed_to_be_default_match) << description; 1122 } 1123 // Ensure that no expected matches are missing. 1124 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1125 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1126 "Case # " << i << " " << description; 1127 } 1128} 1129 1130// This test is like DefaultFetcherSuggestRelevance above except it enables 1131// the field trial that causes the omnibox to be willing to reorder matches 1132// to guarantee the top result is a legal default match. This field trial 1133// causes SearchProvider to allow some constraints to be violated that it 1134// wouldn't normally because the omnibox will fix the problems later. 1135TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevanceWithReorder) { 1136 struct DefaultFetcherMatch { 1137 std::string contents; 1138 bool allowed_to_be_default_match; 1139 }; 1140 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false }; 1141 struct { 1142 const std::string json; 1143 const DefaultFetcherMatch matches[4]; 1144 const std::string inline_autocompletion; 1145 } cases[] = { 1146 // Ensure that suggestrelevance scores reorder matches. 1147 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1148 { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch }, 1149 std::string() }, 1150 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1151 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1152 "\"google:suggestrelevance\":[1, 2]}]", 1153 { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch }, 1154 std::string() }, 1155 1156 // Without suggested relevance scores, we should only allow one 1157 // navsuggest result to be be displayed. 1158 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1159 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1160 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch }, 1161 std::string() }, 1162 1163 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1164 // Negative values will have no effect; the calculated value will be used. 1165 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1166 "\"google:suggestrelevance\":[9998]}]", 1167 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1168 std::string() }, 1169 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1170 "\"google:suggestrelevance\":[9999]}]", 1171 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1172 "1" }, 1173 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1174 "\"google:suggestrelevance\":[9999]}]", 1175 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1176 "1" }, 1177 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1178 "\"google:suggestrelevance\":[9999]}]", 1179 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1180 "1" }, 1181 { "[\"a\",[\"http://a.com\"],[],[]," 1182 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1183 "\"google:verbatimrelevance\":9999," 1184 "\"google:suggestrelevance\":[9998]}]", 1185 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch }, 1186 std::string() }, 1187 { "[\"a\",[\"http://a.com\"],[],[]," 1188 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1189 "\"google:verbatimrelevance\":9998," 1190 "\"google:suggestrelevance\":[9999]}]", 1191 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1192 ".com" }, 1193 { "[\"a\",[\"http://a.com\"],[],[]," 1194 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1195 "\"google:verbatimrelevance\":0," 1196 "\"google:suggestrelevance\":[9999]}]", 1197 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1198 ".com" }, 1199 { "[\"a\",[\"http://a.com\"],[],[]," 1200 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1201 "\"google:verbatimrelevance\":-1," 1202 "\"google:suggestrelevance\":[9999]}]", 1203 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1204 ".com" }, 1205 1206 // Ensure that both types of relevance scores reorder matches together. 1207 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1208 "\"google:verbatimrelevance\":9998}]", 1209 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch }, 1210 "1" }, 1211 1212 // Allow non-inlineable matches to be the highest-scoring match but, 1213 // if the result set lacks a single inlineable result, abandon suggested 1214 // relevance scores entirely. 1215 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1216 { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1217 std::string() }, 1218 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1219 "\"google:verbatimrelevance\":0}]", 1220 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch }, 1221 std::string() }, 1222 { "[\"a\",[\"http://b.com\"],[],[]," 1223 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1224 "\"google:suggestrelevance\":[9999]}]", 1225 { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1226 std::string() }, 1227 { "[\"a\",[\"http://b.com\"],[],[]," 1228 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1229 "\"google:suggestrelevance\":[9999]," 1230 "\"google:verbatimrelevance\":0}]", 1231 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch }, 1232 std::string() }, 1233 1234 // Allow low-scoring matches. 1235 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1236 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1237 "1" }, 1238 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1239 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch }, 1240 "1" }, 1241 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1242 "\"google:verbatimrelevance\":0}]", 1243 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1244 "1" }, 1245 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1246 "\"google:verbatimrelevance\":0}]", 1247 { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1248 "2" }, 1249 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1250 "\"google:verbatimrelevance\":2}]", 1251 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch }, 1252 "2" }, 1253 { "[\"a\",[\"http://a.com\"],[],[]," 1254 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1255 "\"google:suggestrelevance\":[1]," 1256 "\"google:verbatimrelevance\":0}]", 1257 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1258 ".com" }, 1259 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1260 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1261 "\"google:suggestrelevance\":[1, 2]," 1262 "\"google:verbatimrelevance\":0}]", 1263 { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch }, 1264 "2.com" }, 1265 1266 // Ensure that all suggestions are considered, regardless of order. 1267 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1268 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1269 { { "a", true }, { "h", false }, { "g", false }, { "f", false } }, 1270 std::string() }, 1271 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1272 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1273 "\"http://h.com\"],[],[]," 1274 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1275 "\"NAVIGATION\", \"NAVIGATION\"," 1276 "\"NAVIGATION\", \"NAVIGATION\"," 1277 "\"NAVIGATION\"]," 1278 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1279 { { "a", true }, { "h.com", false }, { "g.com", false }, 1280 { "f.com", false } }, 1281 std::string() }, 1282 1283 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1284 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1285 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch }, 1286 std::string() }, 1287 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1288 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch }, 1289 std::string() }, 1290 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1291 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1292 "\"google:suggestrelevance\":[1]}]", 1293 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch }, 1294 std::string() }, 1295 { "[\"a\",[\"http://a1.com\"],[],[]," 1296 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1297 "\"google:suggestrelevance\":[9999, 1]}]", 1298 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch }, 1299 std::string() }, 1300 1301 // Ensure that all 'verbatim' results are merged with their maximum score. 1302 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1303 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1304 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch }, 1305 "2" }, 1306 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1307 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1308 "\"google:verbatimrelevance\":0}]", 1309 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch }, 1310 "2" }, 1311 1312 // Ensure that verbatim is always generated without other suggestions. 1313 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 1314 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1315 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1316 std::string() }, 1317 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1318 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1319 std::string() }, 1320 }; 1321 1322 std::map<std::string, std::string> params; 1323 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) + 1324 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleEnabled; 1325 ASSERT_TRUE(chrome_variations::AssociateVariationParams( 1326 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); 1327 base::FieldTrialList::CreateFieldTrial( 1328 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); 1329 1330 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1331 QueryForInput(ASCIIToUTF16("a"), false, false); 1332 net::TestURLFetcher* fetcher = 1333 test_factory_.GetFetcherByID( 1334 SearchProvider::kDefaultProviderURLFetcherID); 1335 ASSERT_TRUE(fetcher); 1336 fetcher->set_response_code(200); 1337 fetcher->SetResponseString(cases[i].json); 1338 fetcher->delegate()->OnURLFetchComplete(fetcher); 1339 RunTillProviderDone(); 1340 1341 const std::string description = "for input with json=" + cases[i].json; 1342 const ACMatches& matches = provider_->matches(); 1343 // The top match must inline and score as highly as calculated verbatim. 1344 ASSERT_FALSE(matches.empty()); 1345 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1346 matches[0].inline_autocompletion) << description; 1347 1348 size_t j = 0; 1349 // Ensure that the returned matches equal the expectations. 1350 for (; j < matches.size(); ++j) { 1351 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1352 matches[j].contents) << description; 1353 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 1354 matches[j].allowed_to_be_default_match) << description; 1355 } 1356 // Ensure that no expected matches are missing. 1357 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1358 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1359 "Case # " << i << " " << description; 1360 } 1361} 1362 1363// Verifies that suggest results with relevance scores are added 1364// properly when using the keyword fetcher. This is similar to the 1365// test DefaultFetcherSuggestRelevance above but this uses inputs that 1366// trigger keyword suggestions (i.e., "k a" rather than "a") and has 1367// different expectations (because now the results are a mix of 1368// keyword suggestions and default provider suggestions). When a new 1369// test is added to this TEST_F, please consider if it would be 1370// appropriate to add to DefaultFetcherSuggestRelevance as well. 1371TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) { 1372 struct KeywordFetcherMatch { 1373 std::string contents; 1374 bool from_keyword; 1375 bool allowed_to_be_default_match; 1376 }; 1377 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false }; 1378 struct { 1379 const std::string json; 1380 const KeywordFetcherMatch matches[5]; 1381 const std::string inline_autocompletion; 1382 } cases[] = { 1383 // Ensure that suggest relevance scores reorder matches and that 1384 // the keyword verbatim (lacking a suggested verbatim score) beats 1385 // the default provider verbatim. 1386 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1387 { { "a", true, true }, 1388 { "k a", false, true }, 1389 { "c", true, false }, 1390 { "b", true, false }, 1391 kEmptyMatch }, 1392 std::string() }, 1393 // Again, check that relevance scores reorder matches, just this 1394 // time with navigation matches. This also checks that with 1395 // suggested relevance scores we allow multiple navsuggest results. 1396 // It's odd that navsuggest results that come from a keyword 1397 // provider are marked as not a keyword result. I think this 1398 // comes from them not going to a keyword search engine). 1399 // TODO(mpearson): Investigate the implications (if any) of 1400 // tagging these results appropriately. If so, do it because it 1401 // makes more sense. 1402 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[]," 1403 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1404 "\"google:suggestrelevance\":[1301, 1302, 1303]}]", 1405 { { "a", true, true }, 1406 { "d", true, false }, 1407 { "c.com", false, false }, 1408 { "b.com", false, false }, 1409 { "k a", false, true }, }, 1410 std::string() }, 1411 1412 // Without suggested relevance scores, we should only allow one 1413 // navsuggest result to be be displayed. 1414 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1415 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1416 { { "a", true, true }, 1417 { "b.com", false, false }, 1418 { "k a", false, true }, 1419 kEmptyMatch, kEmptyMatch }, 1420 std::string() }, 1421 1422 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1423 // Negative values will have no effect; the calculated value will be used. 1424 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1425 "\"google:suggestrelevance\":[9998]}]", 1426 { { "a", true, true }, 1427 { "a1", true, true }, 1428 { "k a", false, true }, 1429 kEmptyMatch, kEmptyMatch }, 1430 std::string() }, 1431 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1432 "\"google:suggestrelevance\":[9999]}]", 1433 { { "a1", true, true }, 1434 { "a", true, true }, 1435 { "k a", false, true }, 1436 kEmptyMatch, kEmptyMatch }, 1437 "1" }, 1438 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1439 "\"google:suggestrelevance\":[9999]}]", 1440 { { "a1", true, true }, 1441 { "k a", false, true }, 1442 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1443 "1" }, 1444 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1445 "\"google:suggestrelevance\":[9999]}]", 1446 { { "a1", true, true }, 1447 { "a", true, true }, 1448 { "k a", false, true }, 1449 kEmptyMatch, kEmptyMatch }, 1450 "1" }, 1451 { "[\"a\",[\"http://a.com\"],[],[]," 1452 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1453 "\"google:verbatimrelevance\":9999," 1454 "\"google:suggestrelevance\":[9998]}]", 1455 { { "a", true, true }, 1456 { "a.com", false, true }, 1457 { "k a", false, true }, 1458 kEmptyMatch, kEmptyMatch }, 1459 std::string() }, 1460 1461 // Ensure that both types of relevance scores reorder matches together. 1462 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1463 "\"google:verbatimrelevance\":9998}]", 1464 { { "a1", true, true }, 1465 { "a", true, true }, 1466 { "a2", true, true }, 1467 { "k a", false, true }, 1468 kEmptyMatch }, 1469 "1" }, 1470 1471 // Ensure that only inlinable matches may be ranked as the highest result. 1472 // Ignore all suggested relevance scores if this constraint is violated. 1473 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1474 { { "a", true, true }, 1475 { "b", true, false }, 1476 { "k a", false, true }, 1477 kEmptyMatch, kEmptyMatch }, 1478 std::string() }, 1479 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1480 "\"google:verbatimrelevance\":0}]", 1481 { { "a", true, true }, 1482 { "b", true, false }, 1483 { "k a", false, true }, 1484 kEmptyMatch, kEmptyMatch }, 1485 std::string() }, 1486 { "[\"a\",[\"http://b.com\"],[],[]," 1487 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1488 "\"google:suggestrelevance\":[9999]}]", 1489 { { "a", true, true }, 1490 { "b.com", false, false }, 1491 { "k a", false, true }, 1492 kEmptyMatch, kEmptyMatch }, 1493 std::string() }, 1494 { "[\"a\",[\"http://b.com\"],[],[]," 1495 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1496 "\"google:suggestrelevance\":[9999]," 1497 "\"google:verbatimrelevance\":0}]", 1498 { { "a", true, true }, 1499 { "b.com", false, false }, 1500 { "k a", false, true }, 1501 kEmptyMatch, kEmptyMatch }, 1502 std::string() }, 1503 1504 // Ensure that the top result is ranked as highly as calculated verbatim. 1505 // Ignore the suggested verbatim relevance if this constraint is violated. 1506 // Note that keyword suggestions by default (not in suggested relevance 1507 // mode) score more highly than the default verbatim. 1508 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1509 { { "a", true, true }, 1510 { "a1", true, true }, 1511 { "k a", false, true }, 1512 kEmptyMatch, kEmptyMatch }, 1513 std::string() }, 1514 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1515 { { "a", true, true }, 1516 { "a1", true, true }, 1517 { "k a", false, true }, 1518 kEmptyMatch, kEmptyMatch}, 1519 std::string() }, 1520 // Continuing the same category of tests, but make sure we keep the 1521 // suggested relevance scores even as we discard the verbatim relevance 1522 // scores. 1523 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1524 "\"google:verbatimrelevance\":0}]", 1525 { { "a", true, true }, 1526 { "k a", false, true }, 1527 { "a1", true, true }, 1528 kEmptyMatch, kEmptyMatch}, 1529 std::string() }, 1530 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1531 "\"google:verbatimrelevance\":0}]", 1532 { { "a", true, true }, 1533 { "k a", false, true }, 1534 { "a2", true, true }, 1535 { "a1", true, true }, 1536 kEmptyMatch }, 1537 std::string() }, 1538 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1539 "\"google:verbatimrelevance\":2}]", 1540 { { "a", true, true }, 1541 { "k a", false, true }, 1542 { "a2", true, true }, 1543 { "a1", true, true }, 1544 kEmptyMatch }, 1545 std::string() }, 1546 1547 // Ensure that all suggestions are considered, regardless of order. 1548 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1549 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1550 { { "a", true, true }, 1551 { "k a", false, true }, 1552 { "h", true, false }, 1553 { "g", true, false }, 1554 { "f", true, false } }, 1555 std::string() }, 1556 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1557 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1558 "\"http://h.com\"],[],[]," 1559 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1560 "\"NAVIGATION\", \"NAVIGATION\"," 1561 "\"NAVIGATION\", \"NAVIGATION\"," 1562 "\"NAVIGATION\"]," 1563 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1564 { { "a", true, true }, 1565 { "k a", false, true }, 1566 { "h.com", false, false }, 1567 { "g.com", false, false }, 1568 { "f.com", false, false } }, 1569 std::string() }, 1570 1571 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1572 // Note that keyword suggestions by default (not in suggested relevance 1573 // mode) score more highly than the default verbatim. 1574 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1575 { { "a", true, true }, 1576 { "a1", true, true }, 1577 { "a2", true, true }, 1578 { "k a", false, true }, 1579 kEmptyMatch }, 1580 std::string() }, 1581 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1582 { { "a", true, true }, 1583 { "a1", true, true }, 1584 { "k a", false, true }, 1585 kEmptyMatch, kEmptyMatch}, 1586 std::string() }, 1587 // In this case, ignored the suggested relevance scores means we keep 1588 // only one navsuggest result. 1589 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1590 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1591 "\"google:suggestrelevance\":[1]}]", 1592 { { "a", true, true }, 1593 { "a1.com", false, true }, 1594 { "k a", false, true }, 1595 kEmptyMatch, kEmptyMatch}, 1596 std::string() }, 1597 { "[\"a\",[\"http://a1.com\"],[],[]," 1598 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1599 "\"google:suggestrelevance\":[9999, 1]}]", 1600 { { "a", true, true }, 1601 { "a1.com", false, true }, 1602 { "k a", false, true }, 1603 kEmptyMatch, kEmptyMatch}, 1604 std::string() }, 1605 1606 // Ensure that all 'verbatim' results are merged with their maximum score. 1607 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1608 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1609 { { "a2", true, true }, 1610 { "a", true, true }, 1611 { "a1", true, true }, 1612 { "k a", false, true }, 1613 kEmptyMatch }, 1614 "2" }, 1615 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1616 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1617 "\"google:verbatimrelevance\":0}]", 1618 { { "a2", true, true }, 1619 { "a", true, true }, 1620 { "a1", true, true }, 1621 { "k a", false, true }, 1622 kEmptyMatch }, 1623 "2" }, 1624 1625 // Ensure that verbatim is always generated without other suggestions. 1626 // TODO(mpearson): Ensure the value of verbatimrelevance is respected 1627 // (except when suggested relevances are ignored). 1628 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1629 { { "a", true, true }, 1630 { "k a", false, true }, 1631 kEmptyMatch, kEmptyMatch, kEmptyMatch}, 1632 std::string() }, 1633 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1634 { { "a", true, true }, 1635 { "k a", false, true }, 1636 kEmptyMatch, kEmptyMatch, kEmptyMatch}, 1637 std::string() }, 1638 1639 // Check that navsuggestions will be demoted below queries. 1640 // (Navsuggestions are not allowed to appear first.) In the process, 1641 // make sure the navsuggestions still remain in the same order. 1642 // First, check the situation where navsuggest scores more than verbatim 1643 // and there are no query suggestions. 1644 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1645 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1646 "\"google:verbatimrelevance\":9990," 1647 "\"google:suggestrelevance\":[9998, 9999]}]", 1648 { { "a", true, true }, 1649 { "a2.com", false, true }, 1650 { "a1.com", false, true }, 1651 { "k a", false, true }, 1652 kEmptyMatch }, 1653 std::string() }, 1654 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1655 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1656 "\"google:verbatimrelevance\":9990," 1657 "\"google:suggestrelevance\":[9999, 9998]}]", 1658 { { "a", true, true }, 1659 { "a1.com", false, true }, 1660 { "a2.com", false, true }, 1661 { "k a", false, true }, 1662 kEmptyMatch }, 1663 std::string() }, 1664 // Check when navsuggest scores more than verbatim and there is query 1665 // suggestion but it scores lower. 1666 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1667 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1668 "\"google:verbatimrelevance\":9990," 1669 "\"google:suggestrelevance\":[9998, 9999, 1300]}]", 1670 { { "a", true, true }, 1671 { "a2.com", false, true }, 1672 { "a1.com", false, true }, 1673 { "a3", true, true }, 1674 { "k a", false, true } }, 1675 std::string() }, 1676 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1677 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1678 "\"google:verbatimrelevance\":9990," 1679 "\"google:suggestrelevance\":[9999, 9998, 1300]}]", 1680 { { "a", true, true }, 1681 { "a1.com", false, true }, 1682 { "a2.com", false, true }, 1683 { "a3", true, true }, 1684 { "k a", false, true } }, 1685 std::string() }, 1686 // Check when navsuggest scores more than a query suggestion. There is 1687 // a verbatim but it scores lower. 1688 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1689 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1690 "\"google:verbatimrelevance\":9990," 1691 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1692 { { "a3", true, true }, 1693 { "a2.com", false, true }, 1694 { "a1.com", false, true }, 1695 { "a", true, true }, 1696 { "k a", false, true } }, 1697 "3" }, 1698 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1699 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1700 "\"google:verbatimrelevance\":9990," 1701 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1702 { { "a3", true, true }, 1703 { "a1.com", false, true }, 1704 { "a2.com", false, true }, 1705 { "a", true, true }, 1706 { "k a", false, true } }, 1707 "3" }, 1708 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1709 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1710 "\"google:verbatimrelevance\":0," 1711 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1712 { { "a3", true, true }, 1713 { "a2.com", false, true }, 1714 { "a1.com", false, true }, 1715 { "k a", false, true }, 1716 kEmptyMatch }, 1717 "3" }, 1718 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1719 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1720 "\"google:verbatimrelevance\":0," 1721 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1722 { { "a3", true, true }, 1723 { "a1.com", false, true }, 1724 { "a2.com", false, true }, 1725 { "k a", false, true }, 1726 kEmptyMatch }, 1727 "3" }, 1728 // Check when there is neither verbatim nor a query suggestion that, 1729 // because we can't demote navsuggestions below a query suggestion, 1730 // we abandon suggested relevance scores entirely. One consequence is 1731 // that this means we restore the keyword verbatim match. Note 1732 // that in this case of abandoning suggested relevance scores, we still 1733 // keep the navsuggestions in the same order, but we revert to only allowing 1734 // one navigation to appear because the scores are completely local. 1735 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1736 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1737 "\"google:verbatimrelevance\":0," 1738 "\"google:suggestrelevance\":[9998, 9999]}]", 1739 { { "a", true, true }, 1740 { "a2.com", false, true }, 1741 { "k a", false, true }, 1742 kEmptyMatch, kEmptyMatch}, 1743 std::string() }, 1744 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1745 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1746 "\"google:verbatimrelevance\":0," 1747 "\"google:suggestrelevance\":[9999, 9998]}]", 1748 { { "a", true, true }, 1749 { "a1.com", false, true }, 1750 { "k a", false, true }, 1751 kEmptyMatch, kEmptyMatch}, 1752 std::string() }, 1753 // More checks that everything works when it's not necessary to demote. 1754 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1755 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1756 "\"google:verbatimrelevance\":9990," 1757 "\"google:suggestrelevance\":[9997, 9998, 9999]}]", 1758 { { "a3", true, true }, 1759 { "a2.com", false, true }, 1760 { "a1.com", false, true }, 1761 { "a", true, true }, 1762 { "k a", false, true } }, 1763 "3" }, 1764 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1765 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1766 "\"google:verbatimrelevance\":9990," 1767 "\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1768 { { "a3", true, true }, 1769 { "a1.com", false, true }, 1770 { "a2.com", false, true }, 1771 { "a", true, true }, 1772 { "k a", false, true } }, 1773 "3" }, 1774 }; 1775 1776 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1777 QueryForInput(ASCIIToUTF16("k a"), false, true); 1778 1779 // Set up a default fetcher with no results. 1780 net::TestURLFetcher* default_fetcher = 1781 test_factory_.GetFetcherByID( 1782 SearchProvider::kDefaultProviderURLFetcherID); 1783 ASSERT_TRUE(default_fetcher); 1784 default_fetcher->set_response_code(200); 1785 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 1786 default_fetcher = NULL; 1787 1788 // Set up a keyword fetcher with provided results. 1789 net::TestURLFetcher* keyword_fetcher = 1790 test_factory_.GetFetcherByID( 1791 SearchProvider::kKeywordProviderURLFetcherID); 1792 ASSERT_TRUE(keyword_fetcher); 1793 keyword_fetcher->set_response_code(200); 1794 keyword_fetcher->SetResponseString(cases[i].json); 1795 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 1796 keyword_fetcher = NULL; 1797 RunTillProviderDone(); 1798 1799 const std::string description = "for input with json=" + cases[i].json; 1800 const ACMatches& matches = provider_->matches(); 1801 // The top match must inline and score as highly as calculated verbatim. 1802 ASSERT_FALSE(matches.empty()); 1803 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1804 matches[0].inline_autocompletion) << description; 1805 EXPECT_GE(matches[0].relevance, 1300) << description; 1806 1807 size_t j = 0; 1808 // Ensure that the returned matches equal the expectations. 1809 for (; j < matches.size(); ++j) { 1810 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1811 matches[j].contents) << description; 1812 EXPECT_EQ(cases[i].matches[j].from_keyword, 1813 matches[j].keyword == ASCIIToUTF16("k")) << description; 1814 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 1815 matches[j].allowed_to_be_default_match) << description; 1816 } 1817 // Ensure that no expected matches are missing. 1818 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1819 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1820 "Case # " << i << " " << description; 1821 } 1822} 1823 1824TEST_F(SearchProviderTest, LocalAndRemoteRelevances) { 1825 // Enable Instant Extended in order to allow an increased number of 1826 // suggestions. 1827 chrome::EnableInstantExtendedAPIForTesting(); 1828 1829 // We hardcode the string "term1" below, so ensure that the search term that 1830 // got added to history already is that string. 1831 ASSERT_EQ(ASCIIToUTF16("term1"), term1_); 1832 string16 term = term1_.substr(0, term1_.length() - 1); 1833 1834 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2); 1835 profile_.BlockUntilHistoryProcessesPendingRequests(); 1836 1837 struct { 1838 const string16 input; 1839 const std::string json; 1840 const std::string matches[6]; 1841 } cases[] = { 1842 // The history results outscore the default verbatim score. term2 has more 1843 // visits so it outscores term1. The suggestions are still returned since 1844 // they're server-scored. 1845 { term, 1846 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 1847 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 1848 "\"google:suggestrelevance\":[1, 2, 3]}]", 1849 { "term2", "term1", "term", "a3", "a2", "a1" } }, 1850 // Because we already have three suggestions by the time we see the history 1851 // results, they don't get returned. 1852 { term, 1853 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 1854 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 1855 "\"google:verbatimrelevance\":1450," 1856 "\"google:suggestrelevance\":[1440, 1430, 1420]}]", 1857 { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } }, 1858 // If we only have two suggestions, we have room for a history result. 1859 { term, 1860 "[\"term\",[\"a1\", \"a2\"],[],[]," 1861 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," 1862 "\"google:verbatimrelevance\":1450," 1863 "\"google:suggestrelevance\":[1430, 1410]}]", 1864 { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } }, 1865 // If we have more than three suggestions, they should all be returned as 1866 // long as we have enough total space for them. 1867 { term, 1868 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 1869 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 1870 "\"google:verbatimrelevance\":1450," 1871 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]", 1872 { "term", "a1", "a2", "a3", "a4", kNotApplicable } }, 1873 { term, 1874 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[]," 1875 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"," 1876 "\"QUERY\", \"QUERY\"]," 1877 "\"google:verbatimrelevance\":1450," 1878 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]", 1879 { "term", "a1", "a2", "a3", "a4", "a5" } }, 1880 { term, 1881 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 1882 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 1883 "\"google:verbatimrelevance\":1450," 1884 "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]", 1885 { "term", "a1", "a2", "term2", "a3", "a4" } }, 1886 // When the input looks like a URL, we disallow having a query as the 1887 // highest-ranking result. If the query was provided by a suggestion, we 1888 // reset the suggest scores to enforce this (see 1889 // SearchProvider::UpdateMatches()). Even if we reset the suggest scores, 1890 // however, we should still allow navsuggestions to be treated as 1891 // server-provided. 1892 { ASCIIToUTF16("a.com"), 1893 "[\"a.com\",[\"a1\", \"a2\", \"a.com/1\", \"a.com/2\"],[],[]," 1894 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"NAVIGATION\"," 1895 "\"NAVIGATION\"]," 1896 // A verbatim query for URL-like input scores 850, so the navigation 1897 // scores here should bracket it. 1898 "\"google:suggestrelevance\":[9999, 9998, 900, 800]}]", 1899 { "a.com/1", "a.com", "a.com/2", "a1", kNotApplicable, kNotApplicable } }, 1900 }; 1901 1902 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1903 QueryForInput(cases[i].input, false, false); 1904 net::TestURLFetcher* fetcher = 1905 test_factory_.GetFetcherByID( 1906 SearchProvider::kDefaultProviderURLFetcherID); 1907 ASSERT_TRUE(fetcher); 1908 fetcher->set_response_code(200); 1909 fetcher->SetResponseString(cases[i].json); 1910 fetcher->delegate()->OnURLFetchComplete(fetcher); 1911 RunTillProviderDone(); 1912 1913 const std::string description = "for input with json=" + cases[i].json; 1914 const ACMatches& matches = provider_->matches(); 1915 1916 // Ensure no extra matches are present. 1917 ASSERT_LE(matches.size(), 6U); 1918 1919 size_t j = 0; 1920 // Ensure that the returned matches equal the expectations. 1921 for (; j < matches.size(); ++j) 1922 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), 1923 matches[j].contents) << description; 1924 // Ensure that no expected matches are missing. 1925 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1926 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) << 1927 "Case # " << i << " " << description; 1928 } 1929} 1930 1931// Verifies suggest relevance behavior for URL input. 1932TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) { 1933 struct DefaultFetcherUrlInputMatch { 1934 const std::string match_contents; 1935 AutocompleteMatch::Type match_type; 1936 bool allowed_to_be_default_match; 1937 }; 1938 const DefaultFetcherUrlInputMatch kEmptyMatch = 1939 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false }; 1940 struct { 1941 const std::string input; 1942 const std::string json; 1943 const DefaultFetcherUrlInputMatch output[4]; 1944 } cases[] = { 1945 // Ensure topmost NAVIGATION matches are allowed for URL input. 1946 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[]," 1947 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1948 "\"google:suggestrelevance\":[9999]}]", 1949 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true }, 1950 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 1951 kEmptyMatch, kEmptyMatch } }, 1952 1953 // Ensure topmost SUGGEST matches are not allowed for URL input. 1954 // SearchProvider disregards search and verbatim suggested relevances. 1955 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 1956 "{\"google:suggestrelevance\":[9999]}]", 1957 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 1958 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 1959 kEmptyMatch, kEmptyMatch } }, 1960 { "a.com", "[\"a.com\",[\"a.com/a\"],[],[]," 1961 "{\"google:suggestrelevance\":[9999]}]", 1962 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 1963 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 1964 kEmptyMatch, kEmptyMatch } }, 1965 1966 // Ensure the fallback mechanism allows inlinable NAVIGATION matches. 1967 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 1968 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1969 "\"google:suggestrelevance\":[9999, 9998]}]", 1970 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true }, 1971 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 1972 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 1973 kEmptyMatch } }, 1974 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 1975 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1976 "\"google:suggestrelevance\":[9998, 9997]," 1977 "\"google:verbatimrelevance\":9999}]", 1978 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true }, 1979 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 1980 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 1981 kEmptyMatch } }, 1982 1983 // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches. 1984 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 1985 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1986 "\"google:suggestrelevance\":[9999, 9998]}]", 1987 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 1988 { "abc.com", AutocompleteMatchType::NAVSUGGEST, false }, 1989 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 1990 kEmptyMatch } }, 1991 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 1992 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1993 "\"google:suggestrelevance\":[9998, 9997]," 1994 "\"google:verbatimrelevance\":9999}]", 1995 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 1996 { "abc.com", AutocompleteMatchType::NAVSUGGEST, false }, 1997 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true }, 1998 kEmptyMatch } }, 1999 }; 2000 2001 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2002 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 2003 net::TestURLFetcher* fetcher = 2004 test_factory_.GetFetcherByID( 2005 SearchProvider::kDefaultProviderURLFetcherID); 2006 ASSERT_TRUE(fetcher); 2007 fetcher->set_response_code(200); 2008 fetcher->SetResponseString(cases[i].json); 2009 fetcher->delegate()->OnURLFetchComplete(fetcher); 2010 RunTillProviderDone(); 2011 2012 size_t j = 0; 2013 const ACMatches& matches = provider_->matches(); 2014 // Ensure that the returned matches equal the expectations. 2015 for (; j < matches.size(); ++j) { 2016 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents), 2017 matches[j].contents); 2018 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type); 2019 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match, 2020 matches[j].allowed_to_be_default_match); 2021 } 2022 // Ensure that no expected matches are missing. 2023 for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) { 2024 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents); 2025 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES, 2026 cases[i].output[j].match_type); 2027 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match); 2028 } 2029 } 2030} 2031 2032// A basic test that verifies the field trial triggered parsing logic. 2033TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) { 2034 QueryForInput(ASCIIToUTF16("foo"), false, false); 2035 2036 // Make sure the default providers suggest service was queried. 2037 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 2038 SearchProvider::kDefaultProviderURLFetcherID); 2039 ASSERT_TRUE(fetcher); 2040 2041 // Tell the SearchProvider the suggest query is done. 2042 fetcher->set_response_code(200); 2043 fetcher->SetResponseString( 2044 "[\"foo\",[\"foo bar\"],[\"\"],[]," 2045 "{\"google:suggesttype\":[\"QUERY\"]," 2046 "\"google:fieldtrialtriggered\":true}]"); 2047 fetcher->delegate()->OnURLFetchComplete(fetcher); 2048 fetcher = NULL; 2049 2050 // Run till the history results complete. 2051 RunTillProviderDone(); 2052 2053 { 2054 // Check for the match and field trial triggered bits. 2055 AutocompleteMatch match; 2056 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match)); 2057 ProvidersInfo providers_info; 2058 provider_->AddProviderInfo(&providers_info); 2059 ASSERT_EQ(1U, providers_info.size()); 2060 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 2061 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size()); 2062 } 2063 { 2064 // Reset the session and check that bits are reset. 2065 provider_->ResetSession(); 2066 ProvidersInfo providers_info; 2067 provider_->AddProviderInfo(&providers_info); 2068 ASSERT_EQ(1U, providers_info.size()); 2069 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 2070 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size()); 2071 } 2072} 2073 2074// Verifies inline autocompletion of navigational results. 2075TEST_F(SearchProviderTest, NavigationInline) { 2076 struct { 2077 const std::string input; 2078 const std::string url; 2079 // Test the expected fill_into_edit, which may drop "http://". 2080 // Some cases do not trim "http://" to match from the start of the scheme. 2081 const std::string fill_into_edit; 2082 const std::string inline_autocompletion; 2083 const bool allowed_to_be_default_match; 2084 } cases[] = { 2085 // Do not inline matches that do not contain the input; trim http as needed. 2086 { "x", "http://www.abc.com", 2087 "www.abc.com", std::string(), false }, 2088 { "https:", "http://www.abc.com", 2089 "www.abc.com", std::string(), false }, 2090 { "abc.com/", "http://www.abc.com", 2091 "www.abc.com", std::string(), false }, 2092 { "http://www.abc.com/a", "http://www.abc.com", 2093 "http://www.abc.com", std::string(), false }, 2094 { "http://www.abc.com", "https://www.abc.com", 2095 "https://www.abc.com", std::string(), false }, 2096 { "http://abc.com", "ftp://abc.com", 2097 "ftp://abc.com", std::string(), false }, 2098 { "https://www.abc.com", "http://www.abc.com", 2099 "www.abc.com", std::string(), false }, 2100 { "ftp://abc.com", "http://abc.com", 2101 "abc.com", std::string(), false }, 2102 2103 // Do not inline matches with invalid input prefixes; trim http as needed. 2104 { "ttp", "http://www.abc.com", 2105 "www.abc.com", std::string(), false }, 2106 { "://w", "http://www.abc.com", 2107 "www.abc.com", std::string(), false }, 2108 { "ww.", "http://www.abc.com", 2109 "www.abc.com", std::string(), false }, 2110 { ".ab", "http://www.abc.com", 2111 "www.abc.com", std::string(), false }, 2112 { "bc", "http://www.abc.com", 2113 "www.abc.com", std::string(), false }, 2114 { ".com", "http://www.abc.com", 2115 "www.abc.com", std::string(), false }, 2116 2117 // Do not inline matches that omit input domain labels; trim http as needed. 2118 { "www.a", "http://a.com", 2119 "a.com", std::string(), false }, 2120 { "http://www.a", "http://a.com", 2121 "http://a.com", std::string(), false }, 2122 { "www.a", "ftp://a.com", 2123 "ftp://a.com", std::string(), false }, 2124 { "ftp://www.a", "ftp://a.com", 2125 "ftp://a.com", std::string(), false }, 2126 2127 // Input matching but with nothing to inline will not yield an offset. 2128 { "abc.com", "http://www.abc.com", 2129 "www.abc.com", std::string(), false }, 2130 { "http://www.abc.com", "http://www.abc.com", 2131 "http://www.abc.com", std::string(), false }, 2132 2133 // Inline matches when the input is a leading substring of the scheme. 2134 { "h", "http://www.abc.com", 2135 "http://www.abc.com", "ttp://www.abc.com", true }, 2136 { "http", "http://www.abc.com", 2137 "http://www.abc.com", "://www.abc.com", true }, 2138 2139 // Inline matches when the input is a leading substring of the full URL. 2140 { "http:", "http://www.abc.com", 2141 "http://www.abc.com", "//www.abc.com", true }, 2142 { "http://w", "http://www.abc.com", 2143 "http://www.abc.com", "ww.abc.com", true }, 2144 { "http://www.", "http://www.abc.com", 2145 "http://www.abc.com", "abc.com", true }, 2146 { "http://www.ab", "http://www.abc.com", 2147 "http://www.abc.com", "c.com", true }, 2148 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 2149 "http://www.abc.com/path/file.htm?q=x#foo", 2150 "ath/file.htm?q=x#foo", 2151 true }, 2152 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 2153 "http://abc.com/path/file.htm?q=x#foo", 2154 "ath/file.htm?q=x#foo", true}, 2155 2156 // Inline matches with valid URLPrefixes; only trim "http://". 2157 { "w", "http://www.abc.com", 2158 "www.abc.com", "ww.abc.com", true }, 2159 { "www.a", "http://www.abc.com", 2160 "www.abc.com", "bc.com", true }, 2161 { "abc", "http://www.abc.com", 2162 "www.abc.com", ".com", true }, 2163 { "abc.c", "http://www.abc.com", 2164 "www.abc.com", "om", true }, 2165 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 2166 "www.abc.com/path/file.htm?q=x#foo", 2167 "ath/file.htm?q=x#foo", true }, 2168 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 2169 "abc.com/path/file.htm?q=x#foo", 2170 "ath/file.htm?q=x#foo", true }, 2171 2172 // Inline matches using the maximal URLPrefix components. 2173 { "h", "http://help.com", 2174 "help.com", "elp.com", true }, 2175 { "http", "http://http.com", 2176 "http.com", ".com", true }, 2177 { "h", "http://www.help.com", 2178 "www.help.com", "elp.com", true }, 2179 { "http", "http://www.http.com", 2180 "www.http.com", ".com", true }, 2181 { "w", "http://www.www.com", 2182 "www.www.com", "ww.com", true }, 2183 2184 // Test similar behavior for the ftp and https schemes. 2185 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 2186 "ftp://www.abc.com/path/file.htm?q=x#foo", 2187 "c.com/path/file.htm?q=x#foo", true }, 2188 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 2189 "ftp://www.abc.com/path/file.htm?q=x#foo", 2190 "c.com/path/file.htm?q=x#foo", true }, 2191 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 2192 "ftp://www.abc.com/path/file.htm?q=x#foo", 2193 "c.com/path/file.htm?q=x#foo", true }, 2194 { "ab", "ftp://abc.com/path/file.htm?q=x#foo", 2195 "ftp://abc.com/path/file.htm?q=x#foo", 2196 "c.com/path/file.htm?q=x#foo", true }, 2197 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 2198 "https://www.abc.com/path/file.htm?q=x#foo", 2199 "c.com/path/file.htm?q=x#foo", true }, 2200 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 2201 "https://www.abc.com/path/file.htm?q=x#foo", 2202 "c.com/path/file.htm?q=x#foo", true }, 2203 { "ab", "https://www.abc.com/path/file.htm?q=x#foo", 2204 "https://www.abc.com/path/file.htm?q=x#foo", 2205 "c.com/path/file.htm?q=x#foo", true }, 2206 { "ab", "https://abc.com/path/file.htm?q=x#foo", 2207 "https://abc.com/path/file.htm?q=x#foo", 2208 "c.com/path/file.htm?q=x#foo", true }, 2209 2210 // Forced query input should inline and retain the "?" prefix. 2211 { "?http://www.ab", "http://www.abc.com", 2212 "?http://www.abc.com", "c.com", true }, 2213 { "?www.ab", "http://www.abc.com", 2214 "?www.abc.com", "c.com", true }, 2215 { "?ab", "http://www.abc.com", 2216 "?www.abc.com", "c.com", true }, 2217 }; 2218 2219 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2220 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 2221 AutocompleteMatch match( 2222 provider_->NavigationToMatch(SearchProvider::NavigationResult( 2223 *provider_.get(), GURL(cases[i].url), string16(), false, 0, 2224 false))); 2225 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 2226 match.inline_autocompletion); 2227 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit); 2228 EXPECT_EQ(cases[i].allowed_to_be_default_match, 2229 match.allowed_to_be_default_match); 2230 } 2231} 2232 2233// Verifies that "http://" is not trimmed for input that is a leading substring. 2234TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) { 2235 const string16 input(ASCIIToUTF16("ht")); 2236 const string16 url(ASCIIToUTF16("http://a.com")); 2237 const SearchProvider::NavigationResult result( 2238 *provider_.get(), GURL(url), string16(), false, 0, false); 2239 2240 // Check the offset and strings when inline autocompletion is allowed. 2241 QueryForInput(input, false, false); 2242 AutocompleteMatch match_inline(provider_->NavigationToMatch(result)); 2243 EXPECT_EQ(url, match_inline.fill_into_edit); 2244 EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion); 2245 EXPECT_TRUE(match_inline.allowed_to_be_default_match); 2246 EXPECT_EQ(url, match_inline.contents); 2247 2248 // Check the same offset and strings when inline autocompletion is prevented. 2249 QueryForInput(input, true, false); 2250 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result)); 2251 EXPECT_EQ(url, match_prevent.fill_into_edit); 2252 EXPECT_TRUE(match_prevent.inline_autocompletion.empty()); 2253 EXPECT_FALSE(match_prevent.allowed_to_be_default_match); 2254 EXPECT_EQ(url, match_prevent.contents); 2255} 2256 2257// Verifies that input "w" marks a more significant domain label than "www.". 2258TEST_F(SearchProviderTest, NavigationInlineDomainClassify) { 2259 QueryForInput(ASCIIToUTF16("w"), false, false); 2260 AutocompleteMatch match( 2261 provider_->NavigationToMatch(SearchProvider::NavigationResult( 2262 *provider_.get(), GURL("http://www.wow.com"), string16(), false, 0, 2263 false))); 2264 EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion); 2265 EXPECT_TRUE(match.allowed_to_be_default_match); 2266 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit); 2267 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents); 2268 2269 // Ensure that the match for input "w" is marked on "wow" and not "www". 2270 ASSERT_EQ(3U, match.contents_class.size()); 2271 EXPECT_EQ(0U, match.contents_class[0].offset); 2272 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 2273 match.contents_class[0].style); 2274 EXPECT_EQ(4U, match.contents_class[1].offset); 2275 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL | 2276 AutocompleteMatch::ACMatchClassification::MATCH, 2277 match.contents_class[1].style); 2278 EXPECT_EQ(5U, match.contents_class[2].offset); 2279 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 2280 match.contents_class[2].style); 2281} 2282 2283TEST_F(SearchProviderTest, RemoveStaleResultsTest) { 2284 // TODO(mpearson): Consider expanding this test to explicitly cover 2285 // testing staleness for keyword results. 2286 struct { 2287 const std::string omnibox_input; 2288 const int verbatim_relevance; 2289 // These cached suggestions should already be sorted. 2290 // The particular number 5 as the length of the array is 2291 // unimportant; it's merely enough cached results to fully test 2292 // the functioning of RemoveAllStaleResults(). 2293 struct { 2294 const std::string suggestion; 2295 const bool is_navigation_result; 2296 const int relevance; 2297 // |expect_match| is true if this result should survive 2298 // RemoveAllStaleResults() filtering against |omnibox_input| below. 2299 const bool expect_match; 2300 } results[5]; 2301 } cases[] = { 2302 // Simple case: multiple query suggestions and no navsuggestions. 2303 // All query suggestions score less than search-what-you-typed and 2304 // thus none should be filtered because none will appear first. 2305 { "x", 1300, 2306 { { "food", false, 1299, true }, 2307 { "foobar", false, 1298, true }, 2308 { "crazy", false, 1297, true }, 2309 { "friend", false, 1296, true }, 2310 { kNotApplicable, false, 0, false } } }, 2311 2312 // Similarly simple cases, but the query suggestion appears first. 2313 { "f", 1200, 2314 { { "food", false, 1299, true }, 2315 { "foobar", false, 1298, true }, 2316 { "crazy", false, 1297, true }, 2317 { "friend", false, 1296, true }, 2318 { kNotApplicable, false, 0, false } } }, 2319 { "c", 1200, 2320 { { "food", false, 1299, false }, 2321 { "foobar", false, 1298, false }, 2322 { "crazy", false, 1297, true }, 2323 { "friend", false, 1296, true }, 2324 { kNotApplicable, false, 0, false } } }, 2325 { "x", 1200, 2326 { { "food", false, 1299, false }, 2327 { "foobar", false, 1298, false }, 2328 { "crazy", false, 1297, false }, 2329 { "friend", false, 1296, false }, 2330 { kNotApplicable, false, 0, false } } }, 2331 2332 // The same sort of cases, just using a mix of queries and navsuggestions. 2333 { "x", 1300, 2334 { { "http://food.com/", true, 1299, true }, 2335 { "foobar", false, 1298, true }, 2336 { "http://crazy.com/", true, 1297, true }, 2337 { "friend", false, 1296, true }, 2338 { "http://friend.com/", true, 1295, true } } }, 2339 { "f", 1200, 2340 { { "http://food.com/", true, 1299, true }, 2341 { "foobar", false, 1298, true }, 2342 { "http://crazy.com/", true, 1297, true }, 2343 { "friend", false, 1296, true }, 2344 { "http://friend.com/", true, 1295, true } } }, 2345 { "c", 1200, 2346 { { "http://food.com/", true, 1299, false }, 2347 { "foobar", false, 1298, false }, 2348 { "http://crazy.com/", true, 1297, true }, 2349 { "friend", false, 1296, true }, 2350 { "http://friend.com/", true, 1295, true } } }, 2351 { "x", 1200, 2352 { { "http://food.com/", true, 1299, false }, 2353 { "foobar", false, 1298, false }, 2354 { "http://crazy.com/", true, 1297, false }, 2355 { "friend", false, 1296, false }, 2356 { "http://friend.com/", true, 1295, false } } }, 2357 2358 // Run the three tests immediately above again, just with verbatim 2359 // suppressed. Note that in the last case, all results are filtered. 2360 // Because verbatim is also suppressed, SearchProvider will realize 2361 // in UpdateMatches() that it needs to restore verbatim to fulfill 2362 // its constraints. This restoration does not happen in 2363 // RemoveAllStaleResults() and hence is not tested here. This restoration 2364 // is tested in the DefaultFetcherSuggestRelevance test. 2365 { "f", 0, 2366 { { "http://food.com/", true, 1299, true }, 2367 { "foobar", false, 1298, true }, 2368 { "http://crazy.com/", true, 1297, true }, 2369 { "friend", false, 1296, true }, 2370 { "http://friend.com/", true, 1295, true } } }, 2371 { "c", 0, 2372 { { "http://food.com/", true, 1299, false }, 2373 { "foobar", false, 1298, false }, 2374 { "http://crazy.com/", true, 1297, true }, 2375 { "friend", false, 1296, true }, 2376 { "http://friend.com/", true, 1295, true } } }, 2377 { "x", 0, 2378 { { "http://food.com/", true, 1299, false }, 2379 { "foobar", false, 1298, false }, 2380 { "http://crazy.com/", true, 1297, false }, 2381 { "friend", false, 1296, false }, 2382 { "http://friend.com/", true, 1295, false } } }, 2383 2384 // The same sort of tests again, just with verbatim with a score 2385 // that would place it in between other suggestions. 2386 { "f", 1290, 2387 { { "http://food.com/", true, 1299, true }, 2388 { "foobar", false, 1288, true }, 2389 { "http://crazy.com/", true, 1277, true }, 2390 { "friend", false, 1266, true }, 2391 { "http://friend.com/", true, 1255, true } } }, 2392 { "c", 1290, 2393 { { "http://food.com/", true, 1299, false }, 2394 { "foobar", false, 1288, true }, 2395 { "http://crazy.com/", true, 1277, true }, 2396 { "friend", false, 1266, true }, 2397 { "http://friend.com/", true, 1255, true } } }, 2398 { "c", 1270, 2399 { { "http://food.com/", true, 1299, false }, 2400 { "foobar", false, 1288, false }, 2401 { "http://crazy.com/", true, 1277, true }, 2402 { "friend", false, 1266, true }, 2403 { "http://friend.com/", true, 1255, true } } }, 2404 { "x", 1280, 2405 { { "http://food.com/", true, 1299, false }, 2406 { "foobar", false, 1288, false }, 2407 { "http://crazy.com/", true, 1277, true }, 2408 { "friend", false, 1266, true }, 2409 { "http://friend.com/", true, 1255, true } } }, 2410 }; 2411 2412 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2413 // Initialize cached results for this test case. 2414 provider_->default_results_.verbatim_relevance = 2415 cases[i].verbatim_relevance; 2416 provider_->default_results_.navigation_results.clear(); 2417 provider_->default_results_.suggest_results.clear(); 2418 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) { 2419 const std::string& suggestion = cases[i].results[j].suggestion; 2420 if (suggestion == kNotApplicable) 2421 break; 2422 if (cases[i].results[j].is_navigation_result) { 2423 provider_->default_results_.navigation_results.push_back( 2424 SearchProvider::NavigationResult( 2425 *provider_.get(), GURL(suggestion), string16(), false, 2426 cases[i].results[j].relevance, false)); 2427 } else { 2428 provider_->default_results_.suggest_results.push_back( 2429 SearchProvider::SuggestResult(ASCIIToUTF16(suggestion), false, 2430 cases[i].results[j].relevance, 2431 false, false)); 2432 } 2433 } 2434 2435 provider_->input_ = AutocompleteInput( 2436 ASCIIToUTF16(cases[i].omnibox_input), string16::npos, string16(), 2437 GURL(), AutocompleteInput::INVALID_SPEC, false, false, true, 2438 AutocompleteInput::ALL_MATCHES); 2439 provider_->RemoveAllStaleResults(); 2440 2441 // Check cached results. 2442 SearchProvider::SuggestResults::const_iterator sug_it = 2443 provider_->default_results_.suggest_results.begin(); 2444 const SearchProvider::SuggestResults::const_iterator sug_end = 2445 provider_->default_results_.suggest_results.end(); 2446 SearchProvider::NavigationResults::const_iterator nav_it = 2447 provider_->default_results_.navigation_results.begin(); 2448 const SearchProvider::NavigationResults::const_iterator nav_end = 2449 provider_->default_results_.navigation_results.end(); 2450 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) { 2451 const std::string& suggestion = cases[i].results[j].suggestion; 2452 if (suggestion == kNotApplicable) 2453 continue; 2454 if (!cases[i].results[j].expect_match) 2455 continue; 2456 if (cases[i].results[j].is_navigation_result) { 2457 ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion; 2458 EXPECT_EQ(suggestion, nav_it->url().spec()); 2459 ++nav_it; 2460 } else { 2461 ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion; 2462 EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion()); 2463 ++sug_it; 2464 } 2465 } 2466 EXPECT_EQ(sug_end, sug_it); 2467 EXPECT_EQ(nav_end, nav_it); 2468 } 2469} 2470 2471// A basic test that verifies the prefetch metadata parsing logic. 2472TEST_F(SearchProviderTest, PrefetchMetadataParsing) { 2473 struct Match { 2474 std::string contents; 2475 bool allowed_to_be_prefetched; 2476 AutocompleteMatchType::Type type; 2477 bool from_keyword; 2478 }; 2479 const Match kEmptyMatch = { kNotApplicable, 2480 false, 2481 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 2482 false }; 2483 2484 struct { 2485 const std::string input_text; 2486 bool prefer_keyword_provider_results; 2487 const std::string default_provider_response_json; 2488 const std::string keyword_provider_response_json; 2489 const Match matches[5]; 2490 } cases[] = { 2491 // Default provider response does not have prefetch details. Ensure that the 2492 // suggestions are not marked as prefetch query. 2493 { "a", 2494 false, 2495 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 2496 std::string(), 2497 { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 2498 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, 2499 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, 2500 kEmptyMatch, 2501 kEmptyMatch 2502 }, 2503 }, 2504 // Ensure that default provider suggest response prefetch details are 2505 // parsed and recorded in AutocompleteMatch. 2506 { "ab", 2507 false, 2508 "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[]," 2509 "{\"google:clientdata\":{\"phi\": 0}," 2510 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"]," 2511 "\"google:suggestrelevance\":[999, 12, 1]}]", 2512 std::string(), 2513 { { "ab", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 2514 { "abc", true, AutocompleteMatchType::SEARCH_SUGGEST, false }, 2515 { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false }, 2516 { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false }, 2517 kEmptyMatch 2518 }, 2519 }, 2520 // Default provider suggest response has prefetch details. 2521 // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for 2522 // the same query string. Ensure that the prefetch details from 2523 // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match. 2524 { "ab", 2525 false, 2526 "[\"ab\",[\"ab\", \"http://ab.com\"],[],[]," 2527 "{\"google:clientdata\":{\"phi\": 0}," 2528 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2529 "\"google:suggestrelevance\":[99, 98]}]", 2530 std::string(), 2531 { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 2532 {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false }, 2533 kEmptyMatch, 2534 kEmptyMatch, 2535 kEmptyMatch 2536 }, 2537 }, 2538 // Default provider response has prefetch details. We prefer keyword 2539 // provider results. Ensure that prefetch bit for a suggestion from the 2540 // default search provider does not get copied onto a higher-scoring match 2541 // for the same query string from the keyword provider. 2542 { "k a", 2543 true, 2544 "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0}," 2545 "\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," 2546 "\"google:suggestrelevance\":[9, 12]}]", 2547 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 2548 { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true}, 2549 { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 2550 { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, 2551 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true }, 2552 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true } 2553 }, 2554 } 2555 }; 2556 2557 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2558 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, 2559 cases[i].prefer_keyword_provider_results); 2560 2561 // Set up a default fetcher with provided results. 2562 net::TestURLFetcher* fetcher = 2563 test_factory_.GetFetcherByID( 2564 SearchProvider::kDefaultProviderURLFetcherID); 2565 ASSERT_TRUE(fetcher); 2566 fetcher->set_response_code(200); 2567 fetcher->SetResponseString(cases[i].default_provider_response_json); 2568 fetcher->delegate()->OnURLFetchComplete(fetcher); 2569 2570 if (cases[i].prefer_keyword_provider_results) { 2571 // Set up a keyword fetcher with provided results. 2572 net::TestURLFetcher* keyword_fetcher = 2573 test_factory_.GetFetcherByID( 2574 SearchProvider::kKeywordProviderURLFetcherID); 2575 ASSERT_TRUE(keyword_fetcher); 2576 keyword_fetcher->set_response_code(200); 2577 keyword_fetcher->SetResponseString( 2578 cases[i].keyword_provider_response_json); 2579 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 2580 keyword_fetcher = NULL; 2581 } 2582 2583 RunTillProviderDone(); 2584 2585 const std::string description = 2586 "for input with json =" + cases[i].default_provider_response_json; 2587 const ACMatches& matches = provider_->matches(); 2588 // The top match must inline and score as highly as calculated verbatim. 2589 ASSERT_FALSE(matches.empty()); 2590 EXPECT_GE(matches[0].relevance, 1300); 2591 2592 // Ensure that the returned matches equal the expectations. 2593 for (size_t j = 0; j < matches.size(); ++j) { 2594 SCOPED_TRACE(description); 2595 EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents)); 2596 EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched, 2597 SearchProvider::ShouldPrefetch(matches[j])); 2598 EXPECT_EQ(cases[i].matches[j].type, matches[j].type); 2599 EXPECT_EQ(cases[i].matches[j].from_keyword, 2600 matches[j].keyword == ASCIIToUTF16("k")); 2601 } 2602 } 2603} 2604