1 // Copyright 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/autocomplete/search_provider.h" 6 7 #include <string> 8 9 #include "base/command_line.h" 10 #include "base/metrics/field_trial.h" 11 #include "base/prefs/pref_service.h" 12 #include "base/run_loop.h" 13 #include "base/strings/string16.h" 14 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_util.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "base/time/time.h" 18 #include "build/build_config.h" 19 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" 20 #include "chrome/browser/autocomplete/autocomplete_controller.h" 21 #include "chrome/browser/autocomplete/autocomplete_input.h" 22 #include "chrome/browser/autocomplete/autocomplete_match.h" 23 #include "chrome/browser/autocomplete/autocomplete_provider.h" 24 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h" 25 #include "chrome/browser/autocomplete/history_url_provider.h" 26 #include "chrome/browser/history/history_service.h" 27 #include "chrome/browser/history/history_service_factory.h" 28 #include "chrome/browser/omnibox/omnibox_field_trial.h" 29 #include "chrome/browser/search_engines/template_url.h" 30 #include "chrome/browser/search_engines/template_url_service.h" 31 #include "chrome/browser/search_engines/template_url_service_factory.h" 32 #include "chrome/browser/signin/signin_manager_factory.h" 33 #include "chrome/browser/sync/profile_sync_service.h" 34 #include "chrome/browser/sync/profile_sync_service_factory.h" 35 #include "chrome/common/chrome_switches.h" 36 #include "chrome/common/pref_names.h" 37 #include "chrome/test/base/testing_browser_process.h" 38 #include "chrome/test/base/testing_profile.h" 39 #include "components/google/core/browser/google_switches.h" 40 #include "components/metrics/proto/omnibox_event.pb.h" 41 #include "components/search_engines/search_engine_type.h" 42 #include "components/signin/core/browser/signin_manager.h" 43 #include "components/sync_driver/pref_names.h" 44 #include "components/variations/entropy_provider.h" 45 #include "components/variations/variations_associated_data.h" 46 #include "content/public/test/test_browser_thread_bundle.h" 47 #include "net/url_request/test_url_fetcher_factory.h" 48 #include "net/url_request/url_request_status.h" 49 #include "testing/gtest/include/gtest/gtest.h" 50 51 using base::ASCIIToUTF16; 52 53 namespace { 54 55 // Returns the first match in |matches| with |allowed_to_be_default_match| 56 // set to true. 57 ACMatches::const_iterator FindDefaultMatch(const ACMatches& matches) { 58 ACMatches::const_iterator it = matches.begin(); 59 while ((it != matches.end()) && !it->allowed_to_be_default_match) 60 ++it; 61 return it; 62 } 63 64 class SuggestionDeletionHandler; 65 class SearchProviderForTest : public SearchProvider { 66 public: 67 SearchProviderForTest( 68 AutocompleteProviderListener* listener, 69 Profile* profile); 70 bool is_success() { return is_success_; }; 71 72 protected: 73 virtual ~SearchProviderForTest(); 74 75 private: 76 virtual void RecordDeletionResult(bool success) OVERRIDE; 77 bool is_success_; 78 DISALLOW_COPY_AND_ASSIGN(SearchProviderForTest); 79 }; 80 81 SearchProviderForTest::SearchProviderForTest( 82 AutocompleteProviderListener* listener, 83 Profile* profile) 84 : SearchProvider(listener, profile), is_success_(false) { 85 } 86 87 SearchProviderForTest::~SearchProviderForTest() { 88 } 89 90 void SearchProviderForTest::RecordDeletionResult(bool success) { 91 is_success_ = success; 92 } 93 94 } // namespace 95 96 // SearchProviderTest --------------------------------------------------------- 97 98 // The following environment is configured for these tests: 99 // . The TemplateURL default_t_url_ is set as the default provider. 100 // . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This 101 // TemplateURL has a valid suggest and search URL. 102 // . The URL created by using the search term term1_ with default_t_url_ is 103 // added to history. 104 // . The URL created by using the search term keyword_term_ with keyword_t_url_ 105 // is added to history. 106 // . test_factory_ is set as the URLFetcherFactory. 107 class SearchProviderTest : public testing::Test, 108 public AutocompleteProviderListener { 109 public: 110 struct ResultInfo { 111 ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES), 112 allowed_to_be_default_match(false) { 113 } 114 ResultInfo(GURL gurl, 115 AutocompleteMatch::Type result_type, 116 bool allowed_to_be_default_match, 117 base::string16 fill_into_edit) 118 : gurl(gurl), 119 result_type(result_type), 120 allowed_to_be_default_match(allowed_to_be_default_match), 121 fill_into_edit(fill_into_edit) { 122 } 123 124 const GURL gurl; 125 const AutocompleteMatch::Type result_type; 126 const bool allowed_to_be_default_match; 127 const base::string16 fill_into_edit; 128 }; 129 130 struct TestData { 131 const base::string16 input; 132 const size_t num_results; 133 const ResultInfo output[3]; 134 }; 135 136 SearchProviderTest() 137 : default_t_url_(NULL), 138 term1_(ASCIIToUTF16("term1")), 139 keyword_t_url_(NULL), 140 keyword_term_(ASCIIToUTF16("keyword")), 141 run_loop_(NULL) { 142 ResetFieldTrialList(); 143 } 144 145 // See description above class for what this registers. 146 virtual void SetUp() OVERRIDE; 147 virtual void TearDown() OVERRIDE; 148 149 void RunTest(TestData* cases, int num_cases, bool prefer_keyword); 150 151 protected: 152 // Needed for AutocompleteFieldTrial::ActivateStaticTrials(); 153 scoped_ptr<base::FieldTrialList> field_trial_list_; 154 155 // Default value used for testing. 156 static const std::string kNotApplicable; 157 158 // Adds a search for |term|, using the engine |t_url| to the history, and 159 // returns the URL for that search. 160 GURL AddSearchToHistory(TemplateURL* t_url, base::string16 term, int visit_count); 161 162 // Looks for a match in |provider_| with |contents| equal to |contents|. 163 // Sets |match| to it if found. Returns whether |match| was set. 164 bool FindMatchWithContents(const base::string16& contents, 165 AutocompleteMatch* match); 166 167 // Looks for a match in |provider_| with destination |url|. Sets |match| to 168 // it if found. Returns whether |match| was set. 169 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match); 170 171 // AutocompleteProviderListener: 172 // If we're waiting for the provider to finish, this exits the message loop. 173 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE; 174 175 // Runs a nested message loop until provider_ is done. The message loop is 176 // exited by way of OnProviderUpdate. 177 void RunTillProviderDone(); 178 179 // Invokes Start on provider_, then runs all pending tasks. 180 void QueryForInput(const base::string16& text, 181 bool prevent_inline_autocomplete, 182 bool prefer_keyword); 183 184 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is 185 // non-NULL, sets it to the "what you typed" entry for |text|. 186 void QueryForInputAndSetWYTMatch(const base::string16& text, 187 AutocompleteMatch* wyt_match); 188 189 // Notifies the URLFetcher for the suggest query corresponding to the default 190 // search provider that it's done. 191 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE. 192 void FinishDefaultSuggestQuery(); 193 194 // Runs SearchProvider on |input|, for which the suggest server replies 195 // with |json|, and expects that the resulting matches' contents equals 196 // that in |matches|. An empty entry in |matches| means no match should 197 // be returned in that position. Reports any errors with a message that 198 // includes |error_description|. 199 void ForcedQueryTestHelper(const std::string& input, 200 const std::string& json, 201 const std::string matches[3], 202 const std::string& error_description); 203 204 void ResetFieldTrialList(); 205 206 void ClearAllResults(); 207 208 // See description above class for details of these fields. 209 TemplateURL* default_t_url_; 210 const base::string16 term1_; 211 GURL term1_url_; 212 TemplateURL* keyword_t_url_; 213 const base::string16 keyword_term_; 214 GURL keyword_url_; 215 216 content::TestBrowserThreadBundle thread_bundle_; 217 218 // URLFetcherFactory implementation registered. 219 net::TestURLFetcherFactory test_factory_; 220 221 // Profile we use. 222 TestingProfile profile_; 223 224 // The provider. 225 scoped_refptr<SearchProviderForTest> provider_; 226 227 // If non-NULL, OnProviderUpdate quits the current |run_loop_|. 228 base::RunLoop* run_loop_; 229 230 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest); 231 }; 232 233 // static 234 const std::string SearchProviderTest::kNotApplicable = "Not Applicable"; 235 236 void SearchProviderTest::SetUp() { 237 // Make sure that fetchers are automatically ungregistered upon destruction. 238 test_factory_.set_remove_fetcher_on_delete(true); 239 240 // We need both the history service and template url model loaded. 241 ASSERT_TRUE(profile_.CreateHistoryService(true, false)); 242 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 243 &profile_, &TemplateURLServiceFactory::BuildInstanceFor); 244 245 TemplateURLService* turl_model = 246 TemplateURLServiceFactory::GetForProfile(&profile_); 247 248 turl_model->Load(); 249 250 // Reset the default TemplateURL. 251 TemplateURLData data; 252 data.short_name = ASCIIToUTF16("t"); 253 data.SetURL("http://defaultturl/{searchTerms}"); 254 data.suggestions_url = "http://defaultturl2/{searchTerms}"; 255 data.instant_url = "http://does/not/exist?strk=1"; 256 data.search_terms_replacement_key = "strk"; 257 default_t_url_ = new TemplateURL(data); 258 turl_model->Add(default_t_url_); 259 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_); 260 TemplateURLID default_provider_id = default_t_url_->id(); 261 ASSERT_NE(0, default_provider_id); 262 263 // Add url1, with search term term1_. 264 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1); 265 266 // Create another TemplateURL. 267 data.short_name = ASCIIToUTF16("k"); 268 data.SetKeyword(ASCIIToUTF16("k")); 269 data.SetURL("http://keyword/{searchTerms}"); 270 data.suggestions_url = "http://suggest_keyword/{searchTerms}"; 271 keyword_t_url_ = new TemplateURL(data); 272 turl_model->Add(keyword_t_url_); 273 ASSERT_NE(0, keyword_t_url_->id()); 274 275 // Add a page and search term for keyword_t_url_. 276 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1); 277 278 // Keywords are updated by the InMemoryHistoryBackend only after the message 279 // has been processed on the history thread. Block until history processes all 280 // requests to ensure the InMemoryDatabase is the state we expect it. 281 profile_.BlockUntilHistoryProcessesPendingRequests(); 282 283 provider_ = new SearchProviderForTest(this, &profile_); 284 provider_->kMinimumTimeBetweenSuggestQueriesMs = 0; 285 } 286 287 void SearchProviderTest::TearDown() { 288 base::RunLoop().RunUntilIdle(); 289 290 // Shutdown the provider before the profile. 291 provider_ = NULL; 292 } 293 294 void SearchProviderTest::RunTest(TestData* cases, 295 int num_cases, 296 bool prefer_keyword) { 297 ACMatches matches; 298 for (int i = 0; i < num_cases; ++i) { 299 AutocompleteInput input(cases[i].input, base::string16::npos, 300 base::string16(), GURL(), 301 metrics::OmniboxEventProto::INVALID_SPEC, false, 302 prefer_keyword, true, true); 303 provider_->Start(input, false); 304 matches = provider_->matches(); 305 base::string16 diagnostic_details = 306 ASCIIToUTF16("Input was: ") + 307 cases[i].input + 308 ASCIIToUTF16("; prefer_keyword was: ") + 309 (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false")); 310 EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details; 311 if (matches.size() == cases[i].num_results) { 312 for (size_t j = 0; j < cases[i].num_results; ++j) { 313 EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) << 314 diagnostic_details; 315 EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) << 316 diagnostic_details; 317 EXPECT_EQ(cases[i].output[j].fill_into_edit, 318 matches[j].fill_into_edit) << diagnostic_details; 319 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match, 320 matches[j].allowed_to_be_default_match) << diagnostic_details; 321 } 322 } 323 } 324 } 325 326 void SearchProviderTest::OnProviderUpdate(bool updated_matches) { 327 if (run_loop_ && provider_->done()) { 328 run_loop_->Quit(); 329 run_loop_ = NULL; 330 } 331 } 332 333 void SearchProviderTest::RunTillProviderDone() { 334 if (provider_->done()) 335 return; 336 337 base::RunLoop run_loop; 338 run_loop_ = &run_loop; 339 run_loop.Run(); 340 } 341 342 void SearchProviderTest::QueryForInput(const base::string16& text, 343 bool prevent_inline_autocomplete, 344 bool prefer_keyword) { 345 // Start a query. 346 AutocompleteInput input(text, base::string16::npos, base::string16(), GURL(), 347 metrics::OmniboxEventProto::INVALID_SPEC, 348 prevent_inline_autocomplete, prefer_keyword, true, 349 true); 350 provider_->Start(input, false); 351 352 // RunUntilIdle so that the task scheduled by SearchProvider to create the 353 // URLFetchers runs. 354 base::RunLoop().RunUntilIdle(); 355 } 356 357 void SearchProviderTest::QueryForInputAndSetWYTMatch( 358 const base::string16& text, 359 AutocompleteMatch* wyt_match) { 360 QueryForInput(text, false, false); 361 profile_.BlockUntilHistoryProcessesPendingRequests(); 362 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 363 if (!wyt_match) 364 return; 365 ASSERT_GE(provider_->matches().size(), 1u); 366 EXPECT_TRUE(FindMatchWithDestination( 367 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 368 TemplateURLRef::SearchTermsArgs(base::CollapseWhitespace( 369 text, false)), 370 TemplateURLServiceFactory::GetForProfile( 371 &profile_)->search_terms_data())), 372 wyt_match)); 373 } 374 375 GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url, 376 base::string16 term, 377 int visit_count) { 378 HistoryService* history = 379 HistoryServiceFactory::GetForProfile(&profile_, 380 Profile::EXPLICIT_ACCESS); 381 GURL search(t_url->url_ref().ReplaceSearchTerms( 382 TemplateURLRef::SearchTermsArgs(term), 383 TemplateURLServiceFactory::GetForProfile( 384 &profile_)->search_terms_data())); 385 static base::Time last_added_time; 386 last_added_time = std::max(base::Time::Now(), 387 last_added_time + base::TimeDelta::FromMicroseconds(1)); 388 history->AddPageWithDetails(search, base::string16(), visit_count, visit_count, 389 last_added_time, false, history::SOURCE_BROWSED); 390 history->SetKeywordSearchTermsForURL(search, t_url->id(), term); 391 return search; 392 } 393 394 bool SearchProviderTest::FindMatchWithContents(const base::string16& contents, 395 AutocompleteMatch* match) { 396 for (ACMatches::const_iterator i = provider_->matches().begin(); 397 i != provider_->matches().end(); ++i) { 398 if (i->contents == contents) { 399 *match = *i; 400 return true; 401 } 402 } 403 return false; 404 } 405 406 bool SearchProviderTest::FindMatchWithDestination(const GURL& url, 407 AutocompleteMatch* match) { 408 for (ACMatches::const_iterator i = provider_->matches().begin(); 409 i != provider_->matches().end(); ++i) { 410 if (i->destination_url == url) { 411 *match = *i; 412 return true; 413 } 414 } 415 return false; 416 } 417 418 void SearchProviderTest::FinishDefaultSuggestQuery() { 419 net::TestURLFetcher* default_fetcher = 420 test_factory_.GetFetcherByID( 421 SearchProvider::kDefaultProviderURLFetcherID); 422 ASSERT_TRUE(default_fetcher); 423 424 // Tell the SearchProvider the default suggest query is done. 425 default_fetcher->set_response_code(200); 426 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 427 } 428 429 void SearchProviderTest::ForcedQueryTestHelper( 430 const std::string& input, 431 const std::string& json, 432 const std::string expected_matches[3], 433 const std::string& error_description) { 434 QueryForInput(ASCIIToUTF16(input), false, false); 435 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 436 SearchProvider::kDefaultProviderURLFetcherID); 437 ASSERT_TRUE(fetcher); 438 fetcher->set_response_code(200); 439 fetcher->SetResponseString(json); 440 fetcher->delegate()->OnURLFetchComplete(fetcher); 441 RunTillProviderDone(); 442 443 const ACMatches& matches = provider_->matches(); 444 ASSERT_LE(matches.size(), 3u); 445 size_t i = 0; 446 // Ensure that the returned matches equal the expectations. 447 for (; i < matches.size(); ++i) { 448 EXPECT_EQ(ASCIIToUTF16(expected_matches[i]), matches[i].contents) << 449 error_description; 450 } 451 // Ensure that no expected matches are missing. 452 for (; i < 3u; ++i) { 453 EXPECT_EQ(std::string(), expected_matches[i]) << 454 "Case #" << i << ": " << error_description; 455 } 456 } 457 458 void SearchProviderTest::ResetFieldTrialList() { 459 // Destroy the existing FieldTrialList before creating a new one to avoid 460 // a DCHECK. 461 field_trial_list_.reset(); 462 field_trial_list_.reset(new base::FieldTrialList( 463 new metrics::SHA1EntropyProvider("foo"))); 464 chrome_variations::testing::ClearAllVariationParams(); 465 base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial( 466 "AutocompleteDynamicTrial_0", "DefaultGroup"); 467 trial->group(); 468 } 469 470 void SearchProviderTest::ClearAllResults() { 471 provider_->ClearAllResults(); 472 } 473 474 // Actual Tests --------------------------------------------------------------- 475 476 // Make sure we query history for the default provider and a URLFetcher is 477 // created for the default provider suggest results. 478 TEST_F(SearchProviderTest, QueryDefaultProvider) { 479 base::string16 term = term1_.substr(0, term1_.length() - 1); 480 QueryForInput(term, false, false); 481 482 // Make sure the default providers suggest service was queried. 483 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 484 SearchProvider::kDefaultProviderURLFetcherID); 485 ASSERT_TRUE(fetcher); 486 487 // And the URL matches what we expected. 488 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms( 489 TemplateURLRef::SearchTermsArgs(term), 490 TemplateURLServiceFactory::GetForProfile( 491 &profile_)->search_terms_data())); 492 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url); 493 494 // Tell the SearchProvider the suggest query is done. 495 fetcher->set_response_code(200); 496 fetcher->delegate()->OnURLFetchComplete(fetcher); 497 fetcher = NULL; 498 499 // Run till the history results complete. 500 RunTillProviderDone(); 501 502 // The SearchProvider is done. Make sure it has a result for the history 503 // term term1. 504 AutocompleteMatch term1_match; 505 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match)); 506 // Term1 should not have a description, it's set later. 507 EXPECT_TRUE(term1_match.description.empty()); 508 509 AutocompleteMatch wyt_match; 510 EXPECT_TRUE(FindMatchWithDestination( 511 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 512 TemplateURLRef::SearchTermsArgs(term), 513 TemplateURLServiceFactory::GetForProfile( 514 &profile_)->search_terms_data())), 515 &wyt_match)); 516 EXPECT_TRUE(wyt_match.description.empty()); 517 518 // The match for term1 should be more relevant than the what you typed match. 519 EXPECT_GT(term1_match.relevance, wyt_match.relevance); 520 // This longer match should be inlineable. 521 EXPECT_TRUE(term1_match.allowed_to_be_default_match); 522 // The what you typed match should be too, of course. 523 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 524 } 525 526 TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) { 527 base::string16 term = term1_.substr(0, term1_.length() - 1); 528 QueryForInput(term, true, false); 529 530 ASSERT_FALSE(provider_->matches().empty()); 531 ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 532 provider_->matches()[0].type); 533 EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match); 534 } 535 536 // Issues a query that matches the registered keyword and makes sure history 537 // is queried as well as URLFetchers getting created. 538 TEST_F(SearchProviderTest, QueryKeywordProvider) { 539 base::string16 term = keyword_term_.substr(0, keyword_term_.length() - 1); 540 QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term, 541 false, 542 false); 543 544 // Make sure the default providers suggest service was queried. 545 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID( 546 SearchProvider::kDefaultProviderURLFetcherID); 547 ASSERT_TRUE(default_fetcher); 548 549 // Tell the SearchProvider the default suggest query is done. 550 default_fetcher->set_response_code(200); 551 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 552 default_fetcher = NULL; 553 554 // Make sure the keyword providers suggest service was queried. 555 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID( 556 SearchProvider::kKeywordProviderURLFetcherID); 557 ASSERT_TRUE(keyword_fetcher); 558 559 // And the URL matches what we expected. 560 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms( 561 TemplateURLRef::SearchTermsArgs(term), 562 TemplateURLServiceFactory::GetForProfile( 563 &profile_)->search_terms_data())); 564 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url); 565 566 // Tell the SearchProvider the keyword suggest query is done. 567 keyword_fetcher->set_response_code(200); 568 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 569 keyword_fetcher = NULL; 570 571 // Run till the history results complete. 572 RunTillProviderDone(); 573 574 // The SearchProvider is done. Make sure it has a result for the history 575 // term keyword. 576 AutocompleteMatch match; 577 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match)); 578 579 // The match should have an associated keyword. 580 EXPECT_FALSE(match.keyword.empty()); 581 582 // The fill into edit should contain the keyword. 583 EXPECT_EQ(keyword_t_url_->keyword() + base::char16(' ') + keyword_term_, 584 match.fill_into_edit); 585 } 586 587 TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) { 588 // None of the following input strings should be sent to the suggest server, 589 // because they may contain private data. 590 const char* inputs[] = { 591 "username:password", 592 "http://username:password", 593 "https://username:password", 594 "username:password@hostname", 595 "http://username:password@hostname/", 596 "file://filename", 597 "data://data", 598 "unknownscheme:anything", 599 "http://hostname/?query=q", 600 "http://hostname/path#ref", 601 "http://hostname/path #ref", 602 "https://hostname/path", 603 }; 604 605 for (size_t i = 0; i < arraysize(inputs); ++i) { 606 QueryForInput(ASCIIToUTF16(inputs[i]), false, false); 607 // Make sure the default provider's suggest service was not queried. 608 ASSERT_TRUE(test_factory_.GetFetcherByID( 609 SearchProvider::kDefaultProviderURLFetcherID) == NULL); 610 // Run till the history results complete. 611 RunTillProviderDone(); 612 } 613 } 614 615 TEST_F(SearchProviderTest, SendNonPrivateDataToSuggest) { 616 // All of the following input strings should be sent to the suggest server, 617 // because they should not get caught by the private data checks. 618 const char* inputs[] = { 619 "query", 620 "query with spaces", 621 "http://hostname", 622 "http://hostname/path", 623 "http://hostname #ref", 624 "www.hostname.com #ref", 625 "https://hostname", 626 "#hashtag", 627 "foo https://hostname/path" 628 }; 629 630 profile_.BlockUntilHistoryProcessesPendingRequests(); 631 for (size_t i = 0; i < arraysize(inputs); ++i) { 632 QueryForInput(ASCIIToUTF16(inputs[i]), false, false); 633 // Make sure the default provider's suggest service was queried. 634 ASSERT_TRUE(test_factory_.GetFetcherByID( 635 SearchProvider::kDefaultProviderURLFetcherID) != NULL); 636 } 637 } 638 639 TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) { 640 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse( 641 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor); 642 GURL url = AddSearchToHistory(default_t_url_, 643 ASCIIToUTF16("docs.google.com"), 1); 644 645 // Add the term as a url. 646 HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)-> 647 AddPageWithDetails(GURL("http://docs.google.com"), base::string16(), 1, 1, 648 base::Time::Now(), false, history::SOURCE_BROWSED); 649 profile_.BlockUntilHistoryProcessesPendingRequests(); 650 651 AutocompleteMatch wyt_match; 652 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"), 653 &wyt_match)); 654 655 // There should be two matches, one for what you typed, the other for 656 // 'docs.google.com'. The search term should have a lower priority than the 657 // what you typed match. 658 ASSERT_EQ(2u, provider_->matches().size()); 659 AutocompleteMatch term_match; 660 EXPECT_TRUE(FindMatchWithDestination(url, &term_match)); 661 EXPECT_GT(wyt_match.relevance, term_match.relevance); 662 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 663 EXPECT_TRUE(term_match.allowed_to_be_default_match); 664 } 665 666 TEST_F(SearchProviderTest, DontGiveNavsuggestionsInForcedQueryMode) { 667 const std::string kEmptyMatch; 668 struct { 669 const std::string json; 670 const std::string matches_in_default_mode[3]; 671 const std::string matches_in_forced_query_mode[3]; 672 } cases[] = { 673 // Without suggested relevance scores. 674 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[]," 675 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]}]", 676 { "a", "a1.com", "a2" }, 677 { "a", "a2", kEmptyMatch } }, 678 679 // With suggested relevance scores in a situation where navsuggest would 680 // go second. 681 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[]," 682 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]," 683 "\"google:suggestrelevance\":[1250, 1200]}]", 684 { "a", "a1.com", "a2" }, 685 { "a", "a2", kEmptyMatch } }, 686 687 // With suggested relevance scores in a situation where navsuggest 688 // would go first. 689 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[]," 690 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]," 691 "\"google:suggestrelevance\":[1350, 1250]}]", 692 { "a1.com", "a", "a2" }, 693 { "a", "a2", kEmptyMatch } }, 694 695 // With suggested relevance scores in a situation where navsuggest 696 // would go first only because verbatim has been demoted. 697 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[]," 698 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]," 699 "\"google:suggestrelevance\":[1450, 1400]," 700 "\"google:verbatimrelevance\":1350}]", 701 { "a1.com", "a2", "a" }, 702 { "a2", "a", kEmptyMatch } }, 703 }; 704 705 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 706 ForcedQueryTestHelper("a", cases[i].json, cases[i].matches_in_default_mode, 707 "regular input with json=" + cases[i].json); 708 ForcedQueryTestHelper("?a", cases[i].json, 709 cases[i].matches_in_forced_query_mode, 710 "forced query input with json=" + cases[i].json); 711 } 712 } 713 714 // A multiword search with one visit should not autocomplete until multiple 715 // words are typed. 716 TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) { 717 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"), 718 1)); 719 profile_.BlockUntilHistoryProcessesPendingRequests(); 720 721 AutocompleteMatch wyt_match; 722 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"), 723 &wyt_match)); 724 ASSERT_EQ(2u, provider_->matches().size()); 725 AutocompleteMatch term_match; 726 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 727 EXPECT_GT(wyt_match.relevance, term_match.relevance); 728 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 729 EXPECT_TRUE(term_match.allowed_to_be_default_match); 730 731 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"), 732 &wyt_match)); 733 ASSERT_EQ(2u, provider_->matches().size()); 734 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 735 EXPECT_GT(term_match.relevance, wyt_match.relevance); 736 EXPECT_TRUE(term_match.allowed_to_be_default_match); 737 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 738 } 739 740 // A multiword search with more than one visit should autocomplete immediately. 741 TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) { 742 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 743 2)); 744 profile_.BlockUntilHistoryProcessesPendingRequests(); 745 746 AutocompleteMatch wyt_match; 747 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"), 748 &wyt_match)); 749 ASSERT_EQ(2u, provider_->matches().size()); 750 AutocompleteMatch term_match; 751 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 752 EXPECT_GT(term_match.relevance, wyt_match.relevance); 753 EXPECT_TRUE(term_match.allowed_to_be_default_match); 754 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 755 } 756 757 // Autocompletion should work at a word boundary after a space, and should 758 // offer a suggestion for the trimmed search query. 759 TEST_F(SearchProviderTest, AutocompleteAfterSpace) { 760 AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches "), 2); 761 GURL suggested_url(default_t_url_->url_ref().ReplaceSearchTerms( 762 TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("two searches")), 763 TemplateURLServiceFactory::GetForProfile( 764 &profile_)->search_terms_data())); 765 profile_.BlockUntilHistoryProcessesPendingRequests(); 766 767 AutocompleteMatch wyt_match; 768 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "), 769 &wyt_match)); 770 ASSERT_EQ(2u, provider_->matches().size()); 771 AutocompleteMatch term_match; 772 EXPECT_TRUE(FindMatchWithDestination(suggested_url, &term_match)); 773 EXPECT_GT(term_match.relevance, wyt_match.relevance); 774 EXPECT_TRUE(term_match.allowed_to_be_default_match); 775 EXPECT_EQ(ASCIIToUTF16("searches"), term_match.inline_autocompletion); 776 EXPECT_EQ(ASCIIToUTF16("two searches"), term_match.fill_into_edit); 777 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 778 } 779 780 // Newer multiword searches should score more highly than older ones. 781 TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) { 782 GURL term_url_a(AddSearchToHistory(default_t_url_, 783 ASCIIToUTF16("three searches aaa"), 1)); 784 GURL term_url_b(AddSearchToHistory(default_t_url_, 785 ASCIIToUTF16("three searches bbb"), 1)); 786 profile_.BlockUntilHistoryProcessesPendingRequests(); 787 788 AutocompleteMatch wyt_match; 789 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"), 790 &wyt_match)); 791 ASSERT_EQ(3u, provider_->matches().size()); 792 AutocompleteMatch term_match_a; 793 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 794 AutocompleteMatch term_match_b; 795 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 796 EXPECT_GT(term_match_b.relevance, term_match_a.relevance); 797 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 798 EXPECT_TRUE(term_match_b.allowed_to_be_default_match); 799 EXPECT_TRUE(term_match_a.allowed_to_be_default_match); 800 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 801 } 802 803 // An autocompleted multiword search should not be replaced by a different 804 // autocompletion while the user is still typing a valid prefix. 805 TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) { 806 GURL term_url_a(AddSearchToHistory(default_t_url_, 807 ASCIIToUTF16("four searches aaa"), 2)); 808 GURL term_url_b(AddSearchToHistory(default_t_url_, 809 ASCIIToUTF16("four searches bbb"), 1)); 810 profile_.BlockUntilHistoryProcessesPendingRequests(); 811 812 AutocompleteMatch wyt_match; 813 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"), 814 &wyt_match)); 815 ASSERT_EQ(3u, provider_->matches().size()); 816 AutocompleteMatch term_match_a; 817 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 818 AutocompleteMatch term_match_b; 819 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 820 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 821 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 822 EXPECT_TRUE(term_match_a.allowed_to_be_default_match); 823 EXPECT_TRUE(term_match_b.allowed_to_be_default_match); 824 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 825 826 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"), 827 &wyt_match)); 828 ASSERT_EQ(3u, provider_->matches().size()); 829 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 830 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 831 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 832 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 833 EXPECT_TRUE(term_match_a.allowed_to_be_default_match); 834 EXPECT_TRUE(term_match_b.allowed_to_be_default_match); 835 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 836 } 837 838 // Non-completable multiword searches should not crowd out single-word searches. 839 TEST_F(SearchProviderTest, DontCrowdOutSingleWords) { 840 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1)); 841 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1); 842 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1); 843 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1); 844 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1); 845 profile_.BlockUntilHistoryProcessesPendingRequests(); 846 847 AutocompleteMatch wyt_match; 848 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"), 849 &wyt_match)); 850 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size()); 851 AutocompleteMatch term_match; 852 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 853 EXPECT_GT(term_match.relevance, wyt_match.relevance); 854 EXPECT_TRUE(term_match.allowed_to_be_default_match); 855 EXPECT_TRUE(wyt_match.allowed_to_be_default_match); 856 } 857 858 // Inline autocomplete matches regardless of case differences from the input. 859 TEST_F(SearchProviderTest, InlineMixedCaseMatches) { 860 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1)); 861 profile_.BlockUntilHistoryProcessesPendingRequests(); 862 863 AutocompleteMatch wyt_match; 864 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"), 865 &wyt_match)); 866 ASSERT_EQ(2u, provider_->matches().size()); 867 AutocompleteMatch term_match; 868 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 869 EXPECT_GT(term_match.relevance, wyt_match.relevance); 870 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit); 871 EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion); 872 EXPECT_TRUE(term_match.allowed_to_be_default_match); 873 } 874 875 // Verifies AutocompleteControllers return results (including keyword 876 // results) in the right order and set descriptions for them correctly. 877 TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) { 878 // Add an entry that corresponds to a keyword search with 'term2'. 879 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1); 880 profile_.BlockUntilHistoryProcessesPendingRequests(); 881 882 AutocompleteController controller(&profile_, NULL, 883 AutocompleteProvider::TYPE_SEARCH); 884 controller.Start(AutocompleteInput( 885 ASCIIToUTF16("k t"), base::string16::npos, base::string16(), GURL(), 886 metrics::OmniboxEventProto::INVALID_SPEC, false, false, true, true)); 887 const AutocompleteResult& result = controller.result(); 888 889 // There should be three matches, one for the keyword history, one for 890 // keyword provider's what-you-typed, and one for the default provider's 891 // what you typed, in that order. 892 ASSERT_EQ(3u, result.size()); 893 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type); 894 EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE, 895 result.match_at(1).type); 896 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 897 result.match_at(2).type); 898 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance); 899 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance); 900 EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match); 901 EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match); 902 EXPECT_FALSE(result.match_at(2).allowed_to_be_default_match); 903 904 // The two keyword results should come with the keyword we expect. 905 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword); 906 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword); 907 // The default provider has a different keyword. (We don't explicitly 908 // set it during this test, so all we do is assert that it's different.) 909 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword); 910 911 // The top result will always have a description. The third result, 912 // coming from a different provider than the first two, should also. 913 // Whether the second result has one doesn't matter much. (If it was 914 // missing, people would infer that it's the same search provider as 915 // the one above it.) 916 EXPECT_FALSE(result.match_at(0).description.empty()); 917 EXPECT_FALSE(result.match_at(2).description.empty()); 918 EXPECT_NE(result.match_at(0).description, result.match_at(2).description); 919 } 920 921 TEST_F(SearchProviderTest, KeywordVerbatim) { 922 TestData cases[] = { 923 // Test a simple keyword input. 924 { ASCIIToUTF16("k foo"), 2, 925 { ResultInfo(GURL("http://keyword/foo"), 926 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 927 true, 928 ASCIIToUTF16("k foo")), 929 ResultInfo(GURL("http://defaultturl/k%20foo"), 930 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 931 false, 932 ASCIIToUTF16("k foo") ) } }, 933 934 // Make sure extra whitespace after the keyword doesn't change the 935 // keyword verbatim query. Also verify that interior consecutive 936 // whitespace gets trimmed. 937 { ASCIIToUTF16("k foo"), 2, 938 { ResultInfo(GURL("http://keyword/foo"), 939 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 940 true, 941 ASCIIToUTF16("k foo")), 942 ResultInfo(GURL("http://defaultturl/k%20foo"), 943 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 944 false, 945 ASCIIToUTF16("k foo")) } }, 946 // Leading whitespace should be stripped before SearchProvider gets the 947 // input; hence there are no tests here about how it handles those inputs. 948 949 // Verify that interior consecutive whitespace gets trimmed in either case. 950 { ASCIIToUTF16("k foo bar"), 2, 951 { ResultInfo(GURL("http://keyword/foo%20bar"), 952 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 953 true, 954 ASCIIToUTF16("k foo bar")), 955 ResultInfo(GURL("http://defaultturl/k%20foo%20bar"), 956 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 957 false, 958 ASCIIToUTF16("k foo bar")) } }, 959 960 // Verify that trailing whitespace gets trimmed. 961 { ASCIIToUTF16("k foo bar "), 2, 962 { ResultInfo(GURL("http://keyword/foo%20bar"), 963 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 964 true, 965 ASCIIToUTF16("k foo bar")), 966 ResultInfo(GURL("http://defaultturl/k%20foo%20bar"), 967 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 968 false, 969 ASCIIToUTF16("k foo bar")) } }, 970 971 // Keywords can be prefixed by certain things that should get ignored 972 // when constructing the keyword match. 973 { ASCIIToUTF16("www.k foo"), 2, 974 { ResultInfo(GURL("http://keyword/foo"), 975 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 976 true, 977 ASCIIToUTF16("k foo")), 978 ResultInfo(GURL("http://defaultturl/www.k%20foo"), 979 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 980 false, 981 ASCIIToUTF16("www.k foo")) } }, 982 { ASCIIToUTF16("http://k foo"), 2, 983 { ResultInfo(GURL("http://keyword/foo"), 984 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 985 true, 986 ASCIIToUTF16("k foo")), 987 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"), 988 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 989 false, 990 ASCIIToUTF16("http://k foo")) } }, 991 { ASCIIToUTF16("http://www.k foo"), 2, 992 { ResultInfo(GURL("http://keyword/foo"), 993 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 994 true, 995 ASCIIToUTF16("k foo")), 996 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"), 997 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 998 false, 999 ASCIIToUTF16("http://www.k foo")) } }, 1000 1001 // A keyword with no remaining input shouldn't get a keyword 1002 // verbatim match. 1003 { ASCIIToUTF16("k"), 1, 1004 { ResultInfo(GURL("http://defaultturl/k"), 1005 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1006 true, 1007 ASCIIToUTF16("k")) } }, 1008 // Ditto. Trailing whitespace shouldn't make a difference. 1009 { ASCIIToUTF16("k "), 1, 1010 { ResultInfo(GURL("http://defaultturl/k"), 1011 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1012 true, 1013 ASCIIToUTF16("k")) } } 1014 1015 // The fact that verbatim queries to keyword are handled by KeywordProvider 1016 // not SearchProvider is tested in 1017 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc. 1018 }; 1019 1020 // Test not in keyword mode. 1021 RunTest(cases, arraysize(cases), false); 1022 1023 // Test in keyword mode. (Both modes should give the same result.) 1024 RunTest(cases, arraysize(cases), true); 1025 } 1026 1027 // Ensures command-line flags are reflected in the URLs the search provider 1028 // generates. 1029 TEST_F(SearchProviderTest, CommandLineOverrides) { 1030 TemplateURLService* turl_model = 1031 TemplateURLServiceFactory::GetForProfile(&profile_); 1032 1033 TemplateURLData data; 1034 data.short_name = ASCIIToUTF16("default"); 1035 data.SetKeyword(data.short_name); 1036 data.SetURL("{google:baseURL}{searchTerms}"); 1037 default_t_url_ = new TemplateURL(data); 1038 turl_model->Add(default_t_url_); 1039 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_); 1040 1041 CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL, 1042 "http://www.bar.com/"); 1043 CommandLine::ForCurrentProcess()->AppendSwitchASCII( 1044 switches::kExtraSearchQueryParams, "a=b"); 1045 1046 TestData cases[] = { 1047 { ASCIIToUTF16("k a"), 2, 1048 { ResultInfo(GURL("http://keyword/a"), 1049 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 1050 true, 1051 ASCIIToUTF16("k a")), 1052 ResultInfo(GURL("http://www.bar.com/k%20a?a=b"), 1053 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1054 false, 1055 ASCIIToUTF16("k a")) } }, 1056 }; 1057 1058 RunTest(cases, arraysize(cases), false); 1059 } 1060 1061 // Verifies Navsuggest results don't set a TemplateURL, which Instant relies on. 1062 // Also verifies that just the *first* navigational result is listed as a match 1063 // if suggested relevance scores were not sent. 1064 TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) { 1065 QueryForInput(ASCIIToUTF16("a.c"), false, false); 1066 1067 // Make sure the default providers suggest service was queried. 1068 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1069 SearchProvider::kDefaultProviderURLFetcherID); 1070 ASSERT_TRUE(fetcher); 1071 1072 // Tell the SearchProvider the suggest query is done. 1073 fetcher->set_response_code(200); 1074 fetcher->SetResponseString( 1075 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[]," 1076 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]"); 1077 fetcher->delegate()->OnURLFetchComplete(fetcher); 1078 fetcher = NULL; 1079 1080 // Run till the history results complete. 1081 RunTillProviderDone(); 1082 1083 // Make sure the only match is 'a.com' and it doesn't have a template_url. 1084 AutocompleteMatch nav_match; 1085 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match)); 1086 EXPECT_TRUE(nav_match.keyword.empty()); 1087 EXPECT_TRUE(nav_match.allowed_to_be_default_match); 1088 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match)); 1089 } 1090 1091 // Verifies that the most relevant suggest results are added properly. 1092 TEST_F(SearchProviderTest, SuggestRelevance) { 1093 QueryForInput(ASCIIToUTF16("a"), false, false); 1094 1095 // Make sure the default provider's suggest service was queried. 1096 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1097 SearchProvider::kDefaultProviderURLFetcherID); 1098 ASSERT_TRUE(fetcher); 1099 1100 // Tell the SearchProvider the suggest query is done. 1101 fetcher->set_response_code(200); 1102 fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]"); 1103 fetcher->delegate()->OnURLFetchComplete(fetcher); 1104 fetcher = NULL; 1105 1106 // Run till the history results complete. 1107 RunTillProviderDone(); 1108 1109 // Check the expected verbatim and (first 3) suggestions' relative relevances. 1110 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4; 1111 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim)); 1112 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1)); 1113 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2)); 1114 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3)); 1115 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4)); 1116 EXPECT_GT(verbatim.relevance, match_a1.relevance); 1117 EXPECT_GT(match_a1.relevance, match_a2.relevance); 1118 EXPECT_GT(match_a2.relevance, match_a3.relevance); 1119 EXPECT_TRUE(verbatim.allowed_to_be_default_match); 1120 EXPECT_TRUE(match_a1.allowed_to_be_default_match); 1121 EXPECT_TRUE(match_a2.allowed_to_be_default_match); 1122 EXPECT_TRUE(match_a3.allowed_to_be_default_match); 1123 } 1124 1125 // Verifies that the default provider abandons suggested relevance scores 1126 // when in keyword mode. This should happen regardless of whether the 1127 // keyword provider returns suggested relevance scores. 1128 TEST_F(SearchProviderTest, DefaultProviderNoSuggestRelevanceInKeywordMode) { 1129 struct { 1130 const std::string default_provider_json; 1131 const std::string keyword_provider_json; 1132 const std::string matches[5]; 1133 } cases[] = { 1134 // First, try an input where the keyword provider does not deliver 1135 // suggested relevance scores. 1136 { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[]," 1137 "{\"google:verbatimrelevance\":9700," 1138 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1139 "\"google:suggestrelevance\":[9900, 9800]}]", 1140 "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]}]", 1141 { "a", "akeyword-query", "k a", "adefault.com", "k adefault-query" } }, 1142 1143 // Now try with keyword provider suggested relevance scores. 1144 { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[]," 1145 "{\"google:verbatimrelevance\":9700," 1146 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1147 "\"google:suggestrelevance\":[9900, 9800]}]", 1148 "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]," 1149 "\"google:verbatimrelevance\":9500," 1150 "\"google:suggestrelevance\":[9600]}]", 1151 { "akeyword-query", "a", "k a", "adefault.com", "k adefault-query" } } 1152 }; 1153 1154 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1155 QueryForInput(ASCIIToUTF16("k a"), false, true); 1156 net::TestURLFetcher* default_fetcher = 1157 test_factory_.GetFetcherByID( 1158 SearchProvider::kDefaultProviderURLFetcherID); 1159 ASSERT_TRUE(default_fetcher); 1160 default_fetcher->set_response_code(200); 1161 default_fetcher->SetResponseString(cases[i].default_provider_json); 1162 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 1163 net::TestURLFetcher* keyword_fetcher = 1164 test_factory_.GetFetcherByID( 1165 SearchProvider::kKeywordProviderURLFetcherID); 1166 ASSERT_TRUE(keyword_fetcher); 1167 keyword_fetcher->set_response_code(200); 1168 keyword_fetcher->SetResponseString(cases[i].keyword_provider_json); 1169 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 1170 RunTillProviderDone(); 1171 1172 const std::string description = "for input with default_provider_json=" + 1173 cases[i].default_provider_json + " and keyword_provider_json=" + 1174 cases[i].keyword_provider_json; 1175 const ACMatches& matches = provider_->matches(); 1176 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 1177 size_t j = 0; 1178 // Ensure that the returned matches equal the expectations. 1179 for (; j < matches.size(); ++j) { 1180 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), matches[j].contents) << 1181 description; 1182 } 1183 // Ensure that no expected matches are missing. 1184 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1185 EXPECT_EQ(std::string(), cases[i].matches[j]) << description; 1186 } 1187 } 1188 1189 // Verifies that suggest results with relevance scores are added 1190 // properly when using the default fetcher. When adding a new test 1191 // case to this test, please consider adding it to the tests in 1192 // KeywordFetcherSuggestRelevance below. 1193 TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) { 1194 struct DefaultFetcherMatch { 1195 std::string contents; 1196 bool allowed_to_be_default_match; 1197 }; 1198 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false }; 1199 struct { 1200 const std::string json; 1201 const DefaultFetcherMatch matches[6]; 1202 const std::string inline_autocompletion; 1203 } cases[] = { 1204 // Ensure that suggestrelevance scores reorder matches. 1205 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1206 { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch, kEmptyMatch, 1207 kEmptyMatch }, 1208 std::string() }, 1209 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1210 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1211 "\"google:suggestrelevance\":[1, 2]}]", 1212 { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch, 1213 kEmptyMatch, kEmptyMatch }, 1214 std::string() }, 1215 1216 // Without suggested relevance scores, we should only allow one 1217 // navsuggest result to be be displayed. 1218 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1219 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1220 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch, 1221 kEmptyMatch, kEmptyMatch }, 1222 std::string() }, 1223 1224 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1225 // Negative values will have no effect; the calculated value will be used. 1226 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1227 "\"google:suggestrelevance\":[9998]}]", 1228 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1229 kEmptyMatch }, 1230 std::string() }, 1231 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1232 "\"google:suggestrelevance\":[9999]}]", 1233 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1234 kEmptyMatch }, 1235 "1" }, 1236 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1237 "\"google:suggestrelevance\":[9999]}]", 1238 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1239 kEmptyMatch }, 1240 "1" }, 1241 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1242 "\"google:suggestrelevance\":[9999]}]", 1243 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1244 kEmptyMatch }, 1245 "1" }, 1246 { "[\"a\",[\"http://a.com\"],[],[]," 1247 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1248 "\"google:verbatimrelevance\":9999," 1249 "\"google:suggestrelevance\":[9998]}]", 1250 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1251 kEmptyMatch }, 1252 std::string() }, 1253 { "[\"a\",[\"http://a.com\"],[],[]," 1254 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1255 "\"google:verbatimrelevance\":9998," 1256 "\"google:suggestrelevance\":[9999]}]", 1257 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1258 kEmptyMatch }, 1259 ".com" }, 1260 { "[\"a\",[\"http://a.com\"],[],[]," 1261 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1262 "\"google:verbatimrelevance\":0," 1263 "\"google:suggestrelevance\":[9999]}]", 1264 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1265 kEmptyMatch }, 1266 ".com" }, 1267 { "[\"a\",[\"http://a.com\"],[],[]," 1268 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1269 "\"google:verbatimrelevance\":-1," 1270 "\"google:suggestrelevance\":[9999]}]", 1271 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1272 kEmptyMatch }, 1273 ".com" }, 1274 1275 // Ensure that both types of relevance scores reorder matches together. 1276 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1277 "\"google:verbatimrelevance\":9998}]", 1278 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch, kEmptyMatch, 1279 kEmptyMatch }, 1280 "1" }, 1281 1282 // Allow non-inlineable matches to be the highest-scoring match but, 1283 // if the result set lacks a single inlineable result, abandon suggested 1284 // relevance scores entirely. 1285 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1286 { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1287 kEmptyMatch }, 1288 std::string() }, 1289 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1290 "\"google:verbatimrelevance\":0}]", 1291 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1292 kEmptyMatch }, 1293 std::string() }, 1294 { "[\"a\",[\"http://b.com\"],[],[]," 1295 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1296 "\"google:suggestrelevance\":[9999]}]", 1297 { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch, 1298 kEmptyMatch, kEmptyMatch }, 1299 std::string() }, 1300 { "[\"a\",[\"http://b.com\"],[],[]," 1301 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1302 "\"google:suggestrelevance\":[9999]," 1303 "\"google:verbatimrelevance\":0}]", 1304 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch, 1305 kEmptyMatch, kEmptyMatch }, 1306 std::string() }, 1307 1308 // Allow low-scoring matches. 1309 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1310 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1311 kEmptyMatch }, 1312 "1" }, 1313 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1314 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1315 kEmptyMatch }, 1316 "1" }, 1317 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1318 "\"google:verbatimrelevance\":0}]", 1319 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1320 kEmptyMatch }, 1321 "1" }, 1322 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1323 "\"google:verbatimrelevance\":0}]", 1324 { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1325 kEmptyMatch }, 1326 "2" }, 1327 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1328 "\"google:verbatimrelevance\":2}]", 1329 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1330 kEmptyMatch }, 1331 "2" }, 1332 { "[\"a\",[\"http://a.com\"],[],[]," 1333 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1334 "\"google:suggestrelevance\":[1]," 1335 "\"google:verbatimrelevance\":0}]", 1336 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1337 kEmptyMatch }, 1338 ".com" }, 1339 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1340 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1341 "\"google:suggestrelevance\":[1, 2]," 1342 "\"google:verbatimrelevance\":0}]", 1343 { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch, 1344 kEmptyMatch, kEmptyMatch }, 1345 "2.com" }, 1346 1347 // Ensure that all suggestions are considered, regardless of order. 1348 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1349 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1350 { { "a", true }, { "h", false }, { "g", false }, { "f", false }, 1351 { "e", false }, { "d", false } }, 1352 std::string() }, 1353 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1354 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1355 "\"http://h.com\"],[],[]," 1356 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1357 "\"NAVIGATION\", \"NAVIGATION\"," 1358 "\"NAVIGATION\", \"NAVIGATION\"," 1359 "\"NAVIGATION\"]," 1360 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1361 { { "a", true }, { "h.com", false }, { "g.com", false }, 1362 { "f.com", false }, { "e.com", false }, { "d.com", false } }, 1363 std::string() }, 1364 1365 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1366 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1367 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch, kEmptyMatch, 1368 kEmptyMatch }, 1369 std::string() }, 1370 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1371 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1372 kEmptyMatch }, 1373 std::string() }, 1374 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1375 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1376 "\"google:suggestrelevance\":[1]}]", 1377 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch, 1378 kEmptyMatch, kEmptyMatch }, 1379 std::string() }, 1380 { "[\"a\",[\"http://a1.com\"],[],[]," 1381 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1382 "\"google:suggestrelevance\":[9999, 1]}]", 1383 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch, 1384 kEmptyMatch, kEmptyMatch }, 1385 std::string() }, 1386 1387 // Ensure that all 'verbatim' results are merged with their maximum score. 1388 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1389 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1390 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1391 kEmptyMatch }, 1392 "2" }, 1393 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1394 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1395 "\"google:verbatimrelevance\":0}]", 1396 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, 1397 kEmptyMatch }, 1398 "2" }, 1399 1400 // Ensure that verbatim is always generated without other suggestions. 1401 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 1402 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1403 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1404 kEmptyMatch }, 1405 std::string() }, 1406 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1407 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch, 1408 kEmptyMatch }, 1409 std::string() }, 1410 }; 1411 1412 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1413 QueryForInput(ASCIIToUTF16("a"), false, false); 1414 net::TestURLFetcher* fetcher = 1415 test_factory_.GetFetcherByID( 1416 SearchProvider::kDefaultProviderURLFetcherID); 1417 ASSERT_TRUE(fetcher); 1418 fetcher->set_response_code(200); 1419 fetcher->SetResponseString(cases[i].json); 1420 fetcher->delegate()->OnURLFetchComplete(fetcher); 1421 RunTillProviderDone(); 1422 1423 const std::string description = "for input with json=" + cases[i].json; 1424 const ACMatches& matches = provider_->matches(); 1425 ASSERT_FALSE(matches.empty()); 1426 // Find the first match that's allowed to be the default match and check 1427 // its inline_autocompletion. 1428 ACMatches::const_iterator it = FindDefaultMatch(matches); 1429 ASSERT_NE(matches.end(), it); 1430 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1431 it->inline_autocompletion) << description; 1432 1433 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 1434 size_t j = 0; 1435 // Ensure that the returned matches equal the expectations. 1436 for (; j < matches.size(); ++j) { 1437 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1438 matches[j].contents) << description; 1439 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 1440 matches[j].allowed_to_be_default_match) << description; 1441 } 1442 // Ensure that no expected matches are missing. 1443 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1444 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1445 "Case # " << i << " " << description; 1446 } 1447 } 1448 1449 // Verifies that suggest results with relevance scores are added 1450 // properly when using the keyword fetcher. This is similar to the 1451 // test DefaultFetcherSuggestRelevance above but this uses inputs that 1452 // trigger keyword suggestions (i.e., "k a" rather than "a") and has 1453 // different expectations (because now the results are a mix of 1454 // keyword suggestions and default provider suggestions). When a new 1455 // test is added to this TEST_F, please consider if it would be 1456 // appropriate to add to DefaultFetcherSuggestRelevance as well. 1457 TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) { 1458 struct KeywordFetcherMatch { 1459 std::string contents; 1460 bool from_keyword; 1461 bool allowed_to_be_default_match; 1462 }; 1463 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false }; 1464 struct { 1465 const std::string json; 1466 const KeywordFetcherMatch matches[6]; 1467 const std::string inline_autocompletion; 1468 } cases[] = { 1469 // Ensure that suggest relevance scores reorder matches and that 1470 // the keyword verbatim (lacking a suggested verbatim score) beats 1471 // the default provider verbatim. 1472 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1473 { { "a", true, true }, 1474 { "k a", false, false }, 1475 { "c", true, false }, 1476 { "b", true, false }, 1477 kEmptyMatch, kEmptyMatch }, 1478 std::string() }, 1479 // Again, check that relevance scores reorder matches, just this 1480 // time with navigation matches. This also checks that with 1481 // suggested relevance scores we allow multiple navsuggest results. 1482 // Note that navsuggest results that come from a keyword provider 1483 // are marked as not a keyword result. (They don't go to a 1484 // keyword search engine.) 1485 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[]," 1486 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1487 "\"google:suggestrelevance\":[1301, 1302, 1303]}]", 1488 { { "a", true, true }, 1489 { "d", true, false }, 1490 { "c.com", false, false }, 1491 { "b.com", false, false }, 1492 { "k a", false, false }, 1493 kEmptyMatch }, 1494 std::string() }, 1495 1496 // Without suggested relevance scores, we should only allow one 1497 // navsuggest result to be be displayed. 1498 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1499 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1500 { { "a", true, true }, 1501 { "b.com", false, false }, 1502 { "k a", false, false }, 1503 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1504 std::string() }, 1505 1506 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1507 // Negative values will have no effect; the calculated value will be used. 1508 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1509 "\"google:suggestrelevance\":[9998]}]", 1510 { { "a", true, true }, 1511 { "a1", true, true }, 1512 { "k a", false, false }, 1513 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1514 std::string() }, 1515 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1516 "\"google:suggestrelevance\":[9999]}]", 1517 { { "a1", true, true }, 1518 { "a", true, true }, 1519 { "k a", false, false }, 1520 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1521 "1" }, 1522 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1523 "\"google:suggestrelevance\":[9999]}]", 1524 { { "a1", true, true }, 1525 { "k a", false, false }, 1526 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1527 "1" }, 1528 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1529 "\"google:suggestrelevance\":[9999]}]", 1530 { { "a1", true, true }, 1531 { "a", true, true }, 1532 { "k a", false, false }, 1533 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1534 "1" }, 1535 { "[\"a\",[\"http://a.com\"],[],[]," 1536 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1537 "\"google:verbatimrelevance\":9999," 1538 "\"google:suggestrelevance\":[9998]}]", 1539 { { "a", true, true }, 1540 { "a.com", false, false }, 1541 { "k a", false, false }, 1542 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1543 std::string() }, 1544 1545 // Ensure that both types of relevance scores reorder matches together. 1546 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1547 "\"google:verbatimrelevance\":9998}]", 1548 { { "a1", true, true }, 1549 { "a", true, true }, 1550 { "a2", true, true }, 1551 { "k a", false, false }, 1552 kEmptyMatch, kEmptyMatch }, 1553 "1" }, 1554 1555 // Check that non-inlinable matches may be ranked as the highest result 1556 // if there is at least one inlineable match. 1557 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1558 { { "b", true, false }, 1559 { "a", true, true }, 1560 { "k a", false, false }, 1561 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1562 std::string() }, 1563 { "[\"a\",[\"http://b.com\"],[],[]," 1564 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1565 "\"google:suggestrelevance\":[9999]}]", 1566 { { "b.com", false, false }, 1567 { "a", true, true }, 1568 { "k a", false, false }, 1569 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1570 std::string() }, 1571 // On the other hand, if there is no inlineable match, restore 1572 // the keyword verbatim score. 1573 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1574 "\"google:verbatimrelevance\":0}]", 1575 { { "b", true, false }, 1576 { "a", true, true }, 1577 { "k a", false, false }, 1578 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1579 std::string() }, 1580 { "[\"a\",[\"http://b.com\"],[],[]," 1581 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1582 "\"google:suggestrelevance\":[9999]," 1583 "\"google:verbatimrelevance\":0}]", 1584 { { "b.com", false, false }, 1585 { "a", true, true }, 1586 { "k a", false, false }, 1587 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1588 std::string() }, 1589 1590 // The top result does not have to score as highly as calculated 1591 // verbatim. i.e., there are no minimum score restrictions in 1592 // this provider. 1593 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1594 { { "a1", true, true }, 1595 { "k a", false, false }, 1596 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1597 "1" }, 1598 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1599 { { "a1", true, true }, 1600 { "k a", false, false }, 1601 { "a", true, true }, 1602 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1603 "1" }, 1604 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1605 "\"google:verbatimrelevance\":0}]", 1606 { { "k a", false, false }, 1607 { "a1", true, true }, 1608 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1609 "1" }, 1610 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1611 "\"google:verbatimrelevance\":0}]", 1612 { 1613 { "k a", false, false }, 1614 { "a2", true, true }, 1615 { "a1", true, true }, 1616 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1617 "2" }, 1618 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1619 "\"google:verbatimrelevance\":2}]", 1620 { { "k a", false, false }, 1621 { "a2", true, true }, 1622 { "a", true, true }, 1623 { "a1", true, true }, 1624 kEmptyMatch, kEmptyMatch }, 1625 "2" }, 1626 1627 // Ensure that all suggestions are considered, regardless of order. 1628 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1629 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1630 { { "a", true, true }, 1631 { "k a", false, false }, 1632 { "h", true, false }, 1633 { "g", true, false }, 1634 { "f", true, false }, 1635 { "e", true, false } }, 1636 std::string() }, 1637 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1638 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1639 "\"http://h.com\"],[],[]," 1640 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1641 "\"NAVIGATION\", \"NAVIGATION\"," 1642 "\"NAVIGATION\", \"NAVIGATION\"," 1643 "\"NAVIGATION\"]," 1644 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1645 { { "a", true, true }, 1646 { "k a", false, false }, 1647 { "h.com", false, false }, 1648 { "g.com", false, false }, 1649 { "f.com", false, false }, 1650 { "e.com", false, false } }, 1651 std::string() }, 1652 1653 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1654 // Note that keyword suggestions by default (not in suggested relevance 1655 // mode) score more highly than the default verbatim. 1656 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1657 { { "a", true, true }, 1658 { "a1", true, true }, 1659 { "a2", true, true }, 1660 { "k a", false, false }, 1661 kEmptyMatch, kEmptyMatch }, 1662 std::string() }, 1663 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1664 { { "a", true, true }, 1665 { "a1", true, true }, 1666 { "k a", false, false }, 1667 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1668 std::string() }, 1669 // In this case, ignoring the suggested relevance scores means we keep 1670 // only one navsuggest result. 1671 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1672 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1673 "\"google:suggestrelevance\":[1]}]", 1674 { { "a", true, true }, 1675 { "a1.com", false, false }, 1676 { "k a", false, false }, 1677 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1678 std::string() }, 1679 { "[\"a\",[\"http://a1.com\"],[],[]," 1680 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1681 "\"google:suggestrelevance\":[9999, 1]}]", 1682 { { "a", true, true }, 1683 { "a1.com", false, false }, 1684 { "k a", false, false }, 1685 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1686 std::string() }, 1687 1688 // Ensure that all 'verbatim' results are merged with their maximum score. 1689 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1690 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1691 { { "a2", true, true }, 1692 { "a", true, true }, 1693 { "a1", true, true }, 1694 { "k a", false, false }, 1695 kEmptyMatch, kEmptyMatch }, 1696 "2" }, 1697 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1698 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1699 "\"google:verbatimrelevance\":0}]", 1700 { { "a2", true, true }, 1701 { "a", true, true }, 1702 { "a1", true, true }, 1703 { "k a", false, false }, 1704 kEmptyMatch, kEmptyMatch }, 1705 "2" }, 1706 1707 // Ensure that verbatim is always generated without other suggestions. 1708 // TODO(mpearson): Ensure the value of verbatimrelevance is respected 1709 // (except when suggested relevances are ignored). 1710 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1711 { { "k a", false, false }, 1712 { "a", true, true }, 1713 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1714 std::string() }, 1715 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1716 { { "a", true, true }, 1717 { "k a", false, false }, 1718 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1719 std::string() }, 1720 1721 // In reorder mode, navsuggestions will not need to be demoted (because 1722 // they are marked as not allowed to be default match and will be 1723 // reordered as necessary). 1724 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1725 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1726 "\"google:verbatimrelevance\":9990," 1727 "\"google:suggestrelevance\":[9998, 9999]}]", 1728 { { "a2.com", false, false }, 1729 { "a1.com", false, false }, 1730 { "a", true, true }, 1731 { "k a", false, false }, 1732 kEmptyMatch, kEmptyMatch }, 1733 std::string() }, 1734 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1735 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1736 "\"google:verbatimrelevance\":9990," 1737 "\"google:suggestrelevance\":[9999, 9998]}]", 1738 { { "a1.com", false, false }, 1739 { "a2.com", false, false }, 1740 { "a", true, true }, 1741 { "k a", false, false }, 1742 kEmptyMatch, kEmptyMatch }, 1743 std::string() }, 1744 { "[\"a\",[\"https://a/\"],[],[]," 1745 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1746 "\"google:suggestrelevance\":[9999]}]", 1747 { { "https://a", false, false }, 1748 { "a", true, true }, 1749 { "k a", false, false }, 1750 kEmptyMatch, kEmptyMatch, kEmptyMatch }, 1751 std::string() }, 1752 // Check when navsuggest scores more than verbatim and there is query 1753 // suggestion but it scores lower. 1754 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1755 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1756 "\"google:verbatimrelevance\":9990," 1757 "\"google:suggestrelevance\":[9998, 9999, 1300]}]", 1758 { { "a2.com", false, false }, 1759 { "a1.com", false, false }, 1760 { "a", true, true }, 1761 { "a3", true, true }, 1762 { "k a", false, false }, 1763 kEmptyMatch }, 1764 std::string() }, 1765 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1766 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1767 "\"google:verbatimrelevance\":9990," 1768 "\"google:suggestrelevance\":[9999, 9998, 1300]}]", 1769 { { "a1.com", false, false }, 1770 { "a2.com", false, false }, 1771 { "a", true, true }, 1772 { "a3", true, true }, 1773 { "k a", false, false }, 1774 kEmptyMatch }, 1775 std::string() }, 1776 // Check when navsuggest scores more than a query suggestion. There is 1777 // a verbatim but it scores lower. 1778 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1779 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1780 "\"google:verbatimrelevance\":9990," 1781 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1782 { { "a2.com", false, false }, 1783 { "a1.com", false, false }, 1784 { "a3", true, true }, 1785 { "a", true, true }, 1786 { "k a", false, false }, 1787 kEmptyMatch }, 1788 "3" }, 1789 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1790 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1791 "\"google:verbatimrelevance\":9990," 1792 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1793 { { "a1.com", false, false }, 1794 { "a2.com", false, false }, 1795 { "a3", true, true }, 1796 { "a", true, true }, 1797 { "k a", false, false }, 1798 kEmptyMatch }, 1799 "3" }, 1800 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1801 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1802 "\"google:verbatimrelevance\":0," 1803 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1804 { { "a2.com", false, false }, 1805 { "a1.com", false, false }, 1806 { "a3", true, true }, 1807 { "k a", false, false }, 1808 kEmptyMatch, kEmptyMatch }, 1809 "3" }, 1810 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1811 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1812 "\"google:verbatimrelevance\":0," 1813 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1814 { { "a1.com", false, false }, 1815 { "a2.com", false, false }, 1816 { "a3", true, true }, 1817 { "k a", false, false }, 1818 kEmptyMatch, kEmptyMatch }, 1819 "3" }, 1820 // Check when there is neither verbatim nor a query suggestion that, 1821 // because we can't demote navsuggestions below a query suggestion, 1822 // we restore the keyword verbatim score. 1823 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1824 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1825 "\"google:verbatimrelevance\":0," 1826 "\"google:suggestrelevance\":[9998, 9999]}]", 1827 { { "a2.com", false, false }, 1828 { "a1.com", false, false }, 1829 { "a", true, true }, 1830 { "k a", false, false }, 1831 kEmptyMatch, kEmptyMatch }, 1832 std::string() }, 1833 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1834 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1835 "\"google:verbatimrelevance\":0," 1836 "\"google:suggestrelevance\":[9999, 9998]}]", 1837 { { "a1.com", false, false }, 1838 { "a2.com", false, false }, 1839 { "a", true, true }, 1840 { "k a", false, false }, 1841 kEmptyMatch, kEmptyMatch }, 1842 std::string() }, 1843 // More checks that everything works when it's not necessary to demote. 1844 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1845 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1846 "\"google:verbatimrelevance\":9990," 1847 "\"google:suggestrelevance\":[9997, 9998, 9999]}]", 1848 { { "a3", true, true }, 1849 { "a2.com", false, false }, 1850 { "a1.com", false, false }, 1851 { "a", true, true }, 1852 { "k a", false, false }, 1853 kEmptyMatch }, 1854 "3" }, 1855 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1856 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1857 "\"google:verbatimrelevance\":9990," 1858 "\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1859 { { "a3", true, true }, 1860 { "a1.com", false, false }, 1861 { "a2.com", false, false }, 1862 { "a", true, true }, 1863 { "k a", false, false }, 1864 kEmptyMatch }, 1865 "3" }, 1866 }; 1867 1868 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1869 QueryForInput(ASCIIToUTF16("k a"), false, true); 1870 1871 // Set up a default fetcher with no results. 1872 net::TestURLFetcher* default_fetcher = 1873 test_factory_.GetFetcherByID( 1874 SearchProvider::kDefaultProviderURLFetcherID); 1875 ASSERT_TRUE(default_fetcher); 1876 default_fetcher->set_response_code(200); 1877 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 1878 default_fetcher = NULL; 1879 1880 // Set up a keyword fetcher with provided results. 1881 net::TestURLFetcher* keyword_fetcher = 1882 test_factory_.GetFetcherByID( 1883 SearchProvider::kKeywordProviderURLFetcherID); 1884 ASSERT_TRUE(keyword_fetcher); 1885 keyword_fetcher->set_response_code(200); 1886 keyword_fetcher->SetResponseString(cases[i].json); 1887 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 1888 keyword_fetcher = NULL; 1889 RunTillProviderDone(); 1890 1891 const std::string description = "for input with json=" + cases[i].json; 1892 const ACMatches& matches = provider_->matches(); 1893 ASSERT_FALSE(matches.empty()); 1894 // Find the first match that's allowed to be the default match and check 1895 // its inline_autocompletion. 1896 ACMatches::const_iterator it = FindDefaultMatch(matches); 1897 ASSERT_NE(matches.end(), it); 1898 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1899 it->inline_autocompletion) << description; 1900 1901 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 1902 size_t j = 0; 1903 // Ensure that the returned matches equal the expectations. 1904 for (; j < matches.size(); ++j) { 1905 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1906 matches[j].contents) << description; 1907 EXPECT_EQ(cases[i].matches[j].from_keyword, 1908 matches[j].keyword == ASCIIToUTF16("k")) << description; 1909 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match, 1910 matches[j].allowed_to_be_default_match) << description; 1911 } 1912 // Ensure that no expected matches are missing. 1913 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1914 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1915 "Case # " << i << " " << description; 1916 } 1917 } 1918 1919 TEST_F(SearchProviderTest, LocalAndRemoteRelevances) { 1920 // We hardcode the string "term1" below, so ensure that the search term that 1921 // got added to history already is that string. 1922 ASSERT_EQ(ASCIIToUTF16("term1"), term1_); 1923 base::string16 term = term1_.substr(0, term1_.length() - 1); 1924 1925 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2); 1926 profile_.BlockUntilHistoryProcessesPendingRequests(); 1927 1928 struct { 1929 const base::string16 input; 1930 const std::string json; 1931 const std::string matches[6]; 1932 } cases[] = { 1933 // The history results outscore the default verbatim score. term2 has more 1934 // visits so it outscores term1. The suggestions are still returned since 1935 // they're server-scored. 1936 { term, 1937 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 1938 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 1939 "\"google:suggestrelevance\":[1, 2, 3]}]", 1940 { "term2", "term1", "term", "a3", "a2", "a1" } }, 1941 // Because we already have three suggestions by the time we see the history 1942 // results, they don't get returned. 1943 { term, 1944 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 1945 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 1946 "\"google:verbatimrelevance\":1450," 1947 "\"google:suggestrelevance\":[1440, 1430, 1420]}]", 1948 { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } }, 1949 // If we only have two suggestions, we have room for a history result. 1950 { term, 1951 "[\"term\",[\"a1\", \"a2\"],[],[]," 1952 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," 1953 "\"google:verbatimrelevance\":1450," 1954 "\"google:suggestrelevance\":[1430, 1410]}]", 1955 { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } }, 1956 // If we have more than three suggestions, they should all be returned as 1957 // long as we have enough total space for them. 1958 { term, 1959 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 1960 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 1961 "\"google:verbatimrelevance\":1450," 1962 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]", 1963 { "term", "a1", "a2", "a3", "a4", kNotApplicable } }, 1964 { term, 1965 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[]," 1966 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"," 1967 "\"QUERY\", \"QUERY\"]," 1968 "\"google:verbatimrelevance\":1450," 1969 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]", 1970 { "term", "a1", "a2", "a3", "a4", "a5" } }, 1971 { term, 1972 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 1973 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 1974 "\"google:verbatimrelevance\":1450," 1975 "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]", 1976 { "term", "a1", "a2", "term2", "a3", "a4" } } 1977 }; 1978 1979 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1980 QueryForInput(cases[i].input, false, false); 1981 net::TestURLFetcher* fetcher = 1982 test_factory_.GetFetcherByID( 1983 SearchProvider::kDefaultProviderURLFetcherID); 1984 ASSERT_TRUE(fetcher); 1985 fetcher->set_response_code(200); 1986 fetcher->SetResponseString(cases[i].json); 1987 fetcher->delegate()->OnURLFetchComplete(fetcher); 1988 RunTillProviderDone(); 1989 1990 const std::string description = "for input with json=" + cases[i].json; 1991 const ACMatches& matches = provider_->matches(); 1992 1993 // Ensure no extra matches are present. 1994 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 1995 1996 size_t j = 0; 1997 // Ensure that the returned matches equal the expectations. 1998 for (; j < matches.size(); ++j) 1999 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), 2000 matches[j].contents) << description; 2001 // Ensure that no expected matches are missing. 2002 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 2003 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) << 2004 "Case # " << i << " " << description; 2005 } 2006 } 2007 2008 // Verifies suggest relevance behavior for URL input. 2009 TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) { 2010 struct DefaultFetcherUrlInputMatch { 2011 const std::string match_contents; 2012 AutocompleteMatch::Type match_type; 2013 bool allowed_to_be_default_match; 2014 }; 2015 const DefaultFetcherUrlInputMatch kEmptyMatch = 2016 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false }; 2017 struct { 2018 const std::string input; 2019 const std::string json; 2020 const DefaultFetcherUrlInputMatch output[4]; 2021 } cases[] = { 2022 // Ensure NAVIGATION matches are allowed to be listed first for URL 2023 // input regardless of whether the match is inlineable. Note that 2024 // non-inlineable matches should not be allowed to be the default match. 2025 { "a.com", "[\"a.com\",[\"http://b.com/\"],[],[]," 2026 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2027 "\"google:suggestrelevance\":[9999]}]", 2028 { { "b.com", AutocompleteMatchType::NAVSUGGEST, false }, 2029 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2030 kEmptyMatch, kEmptyMatch } }, 2031 { "a.com", "[\"a.com\",[\"https://b.com\"],[],[]," 2032 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2033 "\"google:suggestrelevance\":[9999]}]", 2034 { { "https://b.com", AutocompleteMatchType::NAVSUGGEST, false }, 2035 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2036 kEmptyMatch, kEmptyMatch } }, 2037 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[]," 2038 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2039 "\"google:suggestrelevance\":[9999]}]", 2040 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true }, 2041 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2042 kEmptyMatch, kEmptyMatch } }, 2043 { "a.com", "[\"a.com\",[\"https://a.com\"],[],[]," 2044 "{\"google:suggesttype\":[\"NAVIGATION\"]," 2045 "\"google:suggestrelevance\":[9999]}]", 2046 { { "https://a.com", AutocompleteMatchType::NAVSUGGEST, true }, 2047 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2048 kEmptyMatch, kEmptyMatch } }, 2049 2050 // Ensure topmost inlineable SUGGEST matches are NOT allowed for URL 2051 // input. SearchProvider disregards search and verbatim suggested 2052 // relevances. 2053 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 2054 "{\"google:suggestrelevance\":[9999]}]", 2055 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2056 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2057 kEmptyMatch, kEmptyMatch } }, 2058 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 2059 "{\"google:suggestrelevance\":[9999]}]", 2060 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2061 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2062 kEmptyMatch, kEmptyMatch } }, 2063 2064 // Ensure the fallback mechanism allows inlinable NAVIGATION matches. 2065 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[]," 2066 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2067 "\"google:suggestrelevance\":[9999, 9998]}]", 2068 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true }, 2069 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2070 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2071 kEmptyMatch } }, 2072 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[]," 2073 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2074 "\"google:suggestrelevance\":[9998, 9997]," 2075 "\"google:verbatimrelevance\":9999}]", 2076 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true }, 2077 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2078 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true }, 2079 kEmptyMatch } }, 2080 2081 // Ensure topmost non-inlineable SUGGEST matches are allowed for URL 2082 // input assuming the top inlineable match is not a query (i.e., is a 2083 // NAVSUGGEST). 2084 { "a.com", "[\"a.com\",[\"info\"],[],[]," 2085 "{\"google:suggestrelevance\":[9999]}]", 2086 { { "info", AutocompleteMatchType::SEARCH_SUGGEST, false }, 2087 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2088 kEmptyMatch, kEmptyMatch } }, 2089 { "a.com", "[\"a.com\",[\"info\"],[],[]," 2090 "{\"google:suggestrelevance\":[9999]}]", 2091 { { "info", AutocompleteMatchType::SEARCH_SUGGEST, false }, 2092 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true }, 2093 kEmptyMatch, kEmptyMatch } }, 2094 }; 2095 2096 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2097 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 2098 net::TestURLFetcher* fetcher = 2099 test_factory_.GetFetcherByID( 2100 SearchProvider::kDefaultProviderURLFetcherID); 2101 ASSERT_TRUE(fetcher); 2102 fetcher->set_response_code(200); 2103 fetcher->SetResponseString(cases[i].json); 2104 fetcher->delegate()->OnURLFetchComplete(fetcher); 2105 RunTillProviderDone(); 2106 2107 size_t j = 0; 2108 const ACMatches& matches = provider_->matches(); 2109 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].output)); 2110 // Ensure that the returned matches equal the expectations. 2111 for (; j < matches.size(); ++j) { 2112 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents), 2113 matches[j].contents); 2114 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type); 2115 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match, 2116 matches[j].allowed_to_be_default_match); 2117 } 2118 // Ensure that no expected matches are missing. 2119 for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) { 2120 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents); 2121 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES, 2122 cases[i].output[j].match_type); 2123 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match); 2124 } 2125 } 2126 } 2127 2128 // A basic test that verifies the field trial triggered parsing logic. 2129 TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) { 2130 QueryForInput(ASCIIToUTF16("foo"), false, false); 2131 2132 // Make sure the default providers suggest service was queried. 2133 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 2134 SearchProvider::kDefaultProviderURLFetcherID); 2135 ASSERT_TRUE(fetcher); 2136 2137 // Tell the SearchProvider the suggest query is done. 2138 fetcher->set_response_code(200); 2139 fetcher->SetResponseString( 2140 "[\"foo\",[\"foo bar\"],[\"\"],[]," 2141 "{\"google:suggesttype\":[\"QUERY\"]," 2142 "\"google:fieldtrialtriggered\":true}]"); 2143 fetcher->delegate()->OnURLFetchComplete(fetcher); 2144 fetcher = NULL; 2145 2146 // Run till the history results complete. 2147 RunTillProviderDone(); 2148 2149 { 2150 // Check for the match and field trial triggered bits. 2151 AutocompleteMatch match; 2152 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match)); 2153 ProvidersInfo providers_info; 2154 provider_->AddProviderInfo(&providers_info); 2155 ASSERT_EQ(1U, providers_info.size()); 2156 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 2157 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size()); 2158 } 2159 { 2160 // Reset the session and check that bits are reset. 2161 provider_->ResetSession(); 2162 ProvidersInfo providers_info; 2163 provider_->AddProviderInfo(&providers_info); 2164 ASSERT_EQ(1U, providers_info.size()); 2165 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 2166 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size()); 2167 } 2168 } 2169 2170 // Verifies inline autocompletion of navigational results. 2171 TEST_F(SearchProviderTest, NavigationInline) { 2172 struct { 2173 const std::string input; 2174 const std::string url; 2175 // Test the expected fill_into_edit, which may drop "http://". 2176 // Some cases do not trim "http://" to match from the start of the scheme. 2177 const std::string fill_into_edit; 2178 const std::string inline_autocompletion; 2179 const bool allowed_to_be_default_match_in_regular_mode; 2180 const bool allowed_to_be_default_match_in_prevent_inline_mode; 2181 } cases[] = { 2182 // Do not inline matches that do not contain the input; trim http as needed. 2183 { "x", "http://www.abc.com", 2184 "www.abc.com", std::string(), false, false }, 2185 { "https:", "http://www.abc.com", 2186 "www.abc.com", std::string(), false, false }, 2187 { "http://www.abc.com/a", "http://www.abc.com", 2188 "http://www.abc.com", std::string(), false, 2189 false }, 2190 { "http://www.abc.com", "https://www.abc.com", 2191 "https://www.abc.com", std::string(), false, 2192 false }, 2193 { "http://abc.com", "ftp://abc.com", 2194 "ftp://abc.com", std::string(), false, 2195 false }, 2196 { "https://www.abc.com", "http://www.abc.com", 2197 "www.abc.com", std::string(), false, 2198 false }, 2199 { "ftp://abc.com", "http://abc.com", 2200 "abc.com", std::string(), false, 2201 false }, 2202 2203 // Do not inline matches with invalid input prefixes; trim http as needed. 2204 { "ttp", "http://www.abc.com", 2205 "www.abc.com", std::string(), false, false }, 2206 { "://w", "http://www.abc.com", 2207 "www.abc.com", std::string(), false, false }, 2208 { "ww.", "http://www.abc.com", 2209 "www.abc.com", std::string(), false, false }, 2210 { ".ab", "http://www.abc.com", 2211 "www.abc.com", std::string(), false, false }, 2212 { "bc", "http://www.abc.com", 2213 "www.abc.com", std::string(), false, false }, 2214 { ".com", "http://www.abc.com", 2215 "www.abc.com", std::string(), false, false }, 2216 2217 // Do not inline matches that omit input domain labels; trim http as needed. 2218 { "www.a", "http://a.com", 2219 "a.com", std::string(), false, false }, 2220 { "http://www.a", "http://a.com", 2221 "http://a.com", std::string(), false, false }, 2222 { "www.a", "ftp://a.com", 2223 "ftp://a.com", std::string(), false, false }, 2224 { "ftp://www.a", "ftp://a.com", 2225 "ftp://a.com", std::string(), false, false }, 2226 2227 // Input matching but with nothing to inline will not yield an offset, but 2228 // will be allowed to be default. 2229 { "abc.com", "http://www.abc.com", 2230 "www.abc.com", std::string(), true, true }, 2231 { "abc.com/", "http://www.abc.com", 2232 "www.abc.com", std::string(), true, true }, 2233 { "http://www.abc.com", "http://www.abc.com", 2234 "http://www.abc.com", std::string(), true, true }, 2235 { "http://www.abc.com/", "http://www.abc.com", 2236 "http://www.abc.com", std::string(), true, true }, 2237 2238 // Inputs with trailing whitespace should inline when possible. 2239 { "abc.com ", "http://www.abc.com", 2240 "www.abc.com", std::string(), true, true }, 2241 { "abc.com/ ", "http://www.abc.com", 2242 "www.abc.com", std::string(), true, true }, 2243 { "abc.com ", "http://www.abc.com/bar", 2244 "www.abc.com/bar", "/bar", false, false }, 2245 2246 // Inline matches when the input is a leading substring of the scheme. 2247 { "h", "http://www.abc.com", 2248 "http://www.abc.com", "ttp://www.abc.com", true, false }, 2249 { "http", "http://www.abc.com", 2250 "http://www.abc.com", "://www.abc.com", true, false }, 2251 2252 // Inline matches when the input is a leading substring of the full URL. 2253 { "http:", "http://www.abc.com", 2254 "http://www.abc.com", "//www.abc.com", true, false }, 2255 { "http://w", "http://www.abc.com", 2256 "http://www.abc.com", "ww.abc.com", true, false }, 2257 { "http://www.", "http://www.abc.com", 2258 "http://www.abc.com", "abc.com", true, false }, 2259 { "http://www.ab", "http://www.abc.com", 2260 "http://www.abc.com", "c.com", true, false }, 2261 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 2262 "http://www.abc.com/path/file.htm?q=x#foo", 2263 "ath/file.htm?q=x#foo", 2264 true, false }, 2265 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 2266 "http://abc.com/path/file.htm?q=x#foo", 2267 "ath/file.htm?q=x#foo", 2268 true, false}, 2269 2270 // Inline matches with valid URLPrefixes; only trim "http://". 2271 { "w", "http://www.abc.com", 2272 "www.abc.com", "ww.abc.com", true, false }, 2273 { "www.a", "http://www.abc.com", 2274 "www.abc.com", "bc.com", true, false }, 2275 { "abc", "http://www.abc.com", 2276 "www.abc.com", ".com", true, false }, 2277 { "abc.c", "http://www.abc.com", 2278 "www.abc.com", "om", true, false }, 2279 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 2280 "www.abc.com/path/file.htm?q=x#foo", 2281 "ath/file.htm?q=x#foo", 2282 true, false }, 2283 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 2284 "abc.com/path/file.htm?q=x#foo", 2285 "ath/file.htm?q=x#foo", 2286 true, false }, 2287 2288 // Inline matches using the maximal URLPrefix components. 2289 { "h", "http://help.com", 2290 "help.com", "elp.com", true, false }, 2291 { "http", "http://http.com", 2292 "http.com", ".com", true, false }, 2293 { "h", "http://www.help.com", 2294 "www.help.com", "elp.com", true, false }, 2295 { "http", "http://www.http.com", 2296 "www.http.com", ".com", true, false }, 2297 { "w", "http://www.www.com", 2298 "www.www.com", "ww.com", true, false }, 2299 2300 // Test similar behavior for the ftp and https schemes. 2301 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 2302 "ftp://www.abc.com/path/file.htm?q=x#foo", 2303 "c.com/path/file.htm?q=x#foo", true, false }, 2304 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 2305 "ftp://www.abc.com/path/file.htm?q=x#foo", 2306 "c.com/path/file.htm?q=x#foo", true, false }, 2307 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 2308 "ftp://www.abc.com/path/file.htm?q=x#foo", 2309 "c.com/path/file.htm?q=x#foo", true, false }, 2310 { "ab", "ftp://abc.com/path/file.htm?q=x#foo", 2311 "ftp://abc.com/path/file.htm?q=x#foo", 2312 "c.com/path/file.htm?q=x#foo", true, false }, 2313 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 2314 "https://www.abc.com/path/file.htm?q=x#foo", 2315 "c.com/path/file.htm?q=x#foo", 2316 true, false }, 2317 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 2318 "https://www.abc.com/path/file.htm?q=x#foo", 2319 "c.com/path/file.htm?q=x#foo", true, false }, 2320 { "ab", "https://www.abc.com/path/file.htm?q=x#foo", 2321 "https://www.abc.com/path/file.htm?q=x#foo", 2322 "c.com/path/file.htm?q=x#foo", true, false }, 2323 { "ab", "https://abc.com/path/file.htm?q=x#foo", 2324 "https://abc.com/path/file.htm?q=x#foo", 2325 "c.com/path/file.htm?q=x#foo", true, false }, 2326 2327 // Forced query input should inline and retain the "?" prefix. 2328 { "?http://www.ab", "http://www.abc.com", 2329 "?http://www.abc.com", "c.com", true, false }, 2330 { "?www.ab", "http://www.abc.com", 2331 "?www.abc.com", "c.com", true, false }, 2332 { "?ab", "http://www.abc.com", 2333 "?www.abc.com", "c.com", true, false }, 2334 { "?abc.com", "http://www.abc.com", 2335 "?www.abc.com", "", true, true }, 2336 }; 2337 2338 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2339 // First test regular mode. 2340 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 2341 AutocompleteMatch match( 2342 provider_->NavigationToMatch(SearchProvider::NavigationResult( 2343 *provider_.get(), GURL(cases[i].url), 2344 AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(), 2345 false, 0, false, ASCIIToUTF16(cases[i].input), std::string()))); 2346 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 2347 match.inline_autocompletion); 2348 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit); 2349 EXPECT_EQ(cases[i].allowed_to_be_default_match_in_regular_mode, 2350 match.allowed_to_be_default_match); 2351 2352 // Then test prevent-inline-autocomplete mode. 2353 QueryForInput(ASCIIToUTF16(cases[i].input), true, false); 2354 AutocompleteMatch match_prevent_inline( 2355 provider_->NavigationToMatch(SearchProvider::NavigationResult( 2356 *provider_.get(), GURL(cases[i].url), 2357 AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(), 2358 false, 0, false, ASCIIToUTF16(cases[i].input), std::string()))); 2359 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 2360 match_prevent_inline.inline_autocompletion); 2361 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), 2362 match_prevent_inline.fill_into_edit); 2363 EXPECT_EQ(cases[i].allowed_to_be_default_match_in_prevent_inline_mode, 2364 match_prevent_inline.allowed_to_be_default_match); 2365 } 2366 } 2367 2368 // Verifies that "http://" is not trimmed for input that is a leading substring. 2369 TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) { 2370 const base::string16 input(ASCIIToUTF16("ht")); 2371 const base::string16 url(ASCIIToUTF16("http://a.com")); 2372 const SearchProvider::NavigationResult result( 2373 *provider_.get(), GURL(url), AutocompleteMatchType::NAVSUGGEST, 2374 base::string16(), std::string(), false, 0, false, input, std::string()); 2375 2376 // Check the offset and strings when inline autocompletion is allowed. 2377 QueryForInput(input, false, false); 2378 AutocompleteMatch match_inline(provider_->NavigationToMatch(result)); 2379 EXPECT_EQ(url, match_inline.fill_into_edit); 2380 EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion); 2381 EXPECT_TRUE(match_inline.allowed_to_be_default_match); 2382 EXPECT_EQ(url, match_inline.contents); 2383 2384 // Check the same strings when inline autocompletion is prevented. 2385 QueryForInput(input, true, false); 2386 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result)); 2387 EXPECT_EQ(url, match_prevent.fill_into_edit); 2388 EXPECT_FALSE(match_prevent.allowed_to_be_default_match); 2389 EXPECT_EQ(url, match_prevent.contents); 2390 } 2391 2392 // Verifies that input "w" marks a more significant domain label than "www.". 2393 TEST_F(SearchProviderTest, NavigationInlineDomainClassify) { 2394 QueryForInput(ASCIIToUTF16("w"), false, false); 2395 AutocompleteMatch match( 2396 provider_->NavigationToMatch(SearchProvider::NavigationResult( 2397 *provider_.get(), GURL("http://www.wow.com"), 2398 AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(), 2399 false, 0, false, ASCIIToUTF16("w"), std::string()))); 2400 EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion); 2401 EXPECT_TRUE(match.allowed_to_be_default_match); 2402 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit); 2403 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents); 2404 2405 // Ensure that the match for input "w" is marked on "wow" and not "www". 2406 ASSERT_EQ(3U, match.contents_class.size()); 2407 EXPECT_EQ(0U, match.contents_class[0].offset); 2408 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 2409 match.contents_class[0].style); 2410 EXPECT_EQ(4U, match.contents_class[1].offset); 2411 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL | 2412 AutocompleteMatch::ACMatchClassification::MATCH, 2413 match.contents_class[1].style); 2414 EXPECT_EQ(5U, match.contents_class[2].offset); 2415 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 2416 match.contents_class[2].style); 2417 } 2418 2419 #if !defined(OS_WIN) 2420 // Verify entity suggestion parsing. 2421 TEST_F(SearchProviderTest, ParseEntitySuggestion) { 2422 struct Match { 2423 std::string contents; 2424 std::string description; 2425 std::string query_params; 2426 std::string fill_into_edit; 2427 AutocompleteMatchType::Type type; 2428 }; 2429 const Match kEmptyMatch = { 2430 kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable, 2431 AutocompleteMatchType::NUM_TYPES}; 2432 2433 struct { 2434 const std::string input_text; 2435 const std::string response_json; 2436 const Match matches[5]; 2437 } cases[] = { 2438 // A query and an entity suggestion with different search terms. 2439 { "x", 2440 "[\"x\",[\"xy\", \"yy\"],[\"\",\"\"],[]," 2441 " {\"google:suggestdetail\":[{}," 2442 " {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}]," 2443 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]", 2444 { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2445 { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST }, 2446 { "xy", "A", "p=v", "yy", 2447 AutocompleteMatchType::SEARCH_SUGGEST_ENTITY }, 2448 kEmptyMatch, 2449 kEmptyMatch 2450 }, 2451 }, 2452 // A query and an entity suggestion with same search terms. 2453 { "x", 2454 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[]," 2455 " {\"google:suggestdetail\":[{}," 2456 " {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}]," 2457 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]", 2458 { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2459 { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST }, 2460 { "xy", "A", "p=v", "xy", 2461 AutocompleteMatchType::SEARCH_SUGGEST_ENTITY }, 2462 kEmptyMatch, 2463 kEmptyMatch 2464 }, 2465 }, 2466 }; 2467 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2468 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false); 2469 2470 // Set up a default fetcher with provided results. 2471 net::TestURLFetcher* fetcher = 2472 test_factory_.GetFetcherByID( 2473 SearchProvider::kDefaultProviderURLFetcherID); 2474 ASSERT_TRUE(fetcher); 2475 fetcher->set_response_code(200); 2476 fetcher->SetResponseString(cases[i].response_json); 2477 fetcher->delegate()->OnURLFetchComplete(fetcher); 2478 2479 RunTillProviderDone(); 2480 2481 const ACMatches& matches = provider_->matches(); 2482 ASSERT_FALSE(matches.empty()); 2483 2484 SCOPED_TRACE("for input with json = " + cases[i].response_json); 2485 2486 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 2487 size_t j = 0; 2488 // Ensure that the returned matches equal the expectations. 2489 for (; j < matches.size(); ++j) { 2490 const Match& match = cases[i].matches[j]; 2491 SCOPED_TRACE(" and match index: " + base::IntToString(j)); 2492 EXPECT_EQ(match.contents, 2493 base::UTF16ToUTF8(matches[j].contents)); 2494 EXPECT_EQ(match.description, 2495 base::UTF16ToUTF8(matches[j].description)); 2496 EXPECT_EQ(match.query_params, 2497 matches[j].search_terms_args->suggest_query_params); 2498 EXPECT_EQ(match.fill_into_edit, 2499 base::UTF16ToUTF8(matches[j].fill_into_edit)); 2500 EXPECT_EQ(match.type, matches[j].type); 2501 } 2502 // Ensure that no expected matches are missing. 2503 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) { 2504 SCOPED_TRACE(" and match index: " + base::IntToString(j)); 2505 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable); 2506 EXPECT_EQ(cases[i].matches[j].description, kNotApplicable); 2507 EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable); 2508 EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable); 2509 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES); 2510 } 2511 } 2512 } 2513 #endif // !defined(OS_WIN) 2514 2515 2516 // A basic test that verifies the prefetch metadata parsing logic. 2517 TEST_F(SearchProviderTest, PrefetchMetadataParsing) { 2518 struct Match { 2519 std::string contents; 2520 bool allowed_to_be_prefetched; 2521 AutocompleteMatchType::Type type; 2522 bool from_keyword; 2523 }; 2524 const Match kEmptyMatch = { kNotApplicable, 2525 false, 2526 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 2527 false }; 2528 2529 struct { 2530 const std::string input_text; 2531 bool prefer_keyword_provider_results; 2532 const std::string default_provider_response_json; 2533 const std::string keyword_provider_response_json; 2534 const Match matches[5]; 2535 } cases[] = { 2536 // Default provider response does not have prefetch details. Ensure that the 2537 // suggestions are not marked as prefetch query. 2538 { "a", 2539 false, 2540 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 2541 std::string(), 2542 { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 2543 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, 2544 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, 2545 kEmptyMatch, 2546 kEmptyMatch 2547 }, 2548 }, 2549 // Ensure that default provider suggest response prefetch details are 2550 // parsed and recorded in AutocompleteMatch. 2551 { "ab", 2552 false, 2553 "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[]," 2554 "{\"google:clientdata\":{\"phi\": 0}," 2555 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"]," 2556 "\"google:suggestrelevance\":[999, 12, 1]}]", 2557 std::string(), 2558 { { "ab", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 2559 { "abc", true, AutocompleteMatchType::SEARCH_SUGGEST, false }, 2560 { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false }, 2561 { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false }, 2562 kEmptyMatch 2563 }, 2564 }, 2565 // Default provider suggest response has prefetch details. 2566 // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for 2567 // the same query string. Ensure that the prefetch details from 2568 // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match. 2569 { "ab", 2570 false, 2571 "[\"ab\",[\"ab\", \"http://ab.com\"],[],[]," 2572 "{\"google:clientdata\":{\"phi\": 0}," 2573 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 2574 "\"google:suggestrelevance\":[99, 98]}]", 2575 std::string(), 2576 { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 2577 {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false }, 2578 kEmptyMatch, 2579 kEmptyMatch, 2580 kEmptyMatch 2581 }, 2582 }, 2583 // Default provider response has prefetch details. We prefer keyword 2584 // provider results. Ensure that prefetch bit for a suggestion from the 2585 // default search provider does not get copied onto a higher-scoring match 2586 // for the same query string from the keyword provider. 2587 { "k a", 2588 true, 2589 "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0}," 2590 "\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," 2591 "\"google:suggestrelevance\":[9, 12]}]", 2592 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 2593 { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true}, 2594 { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, 2595 { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, 2596 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true }, 2597 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true } 2598 }, 2599 } 2600 }; 2601 2602 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2603 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, 2604 cases[i].prefer_keyword_provider_results); 2605 2606 // Set up a default fetcher with provided results. 2607 net::TestURLFetcher* fetcher = 2608 test_factory_.GetFetcherByID( 2609 SearchProvider::kDefaultProviderURLFetcherID); 2610 ASSERT_TRUE(fetcher); 2611 fetcher->set_response_code(200); 2612 fetcher->SetResponseString(cases[i].default_provider_response_json); 2613 fetcher->delegate()->OnURLFetchComplete(fetcher); 2614 2615 if (cases[i].prefer_keyword_provider_results) { 2616 // Set up a keyword fetcher with provided results. 2617 net::TestURLFetcher* keyword_fetcher = 2618 test_factory_.GetFetcherByID( 2619 SearchProvider::kKeywordProviderURLFetcherID); 2620 ASSERT_TRUE(keyword_fetcher); 2621 keyword_fetcher->set_response_code(200); 2622 keyword_fetcher->SetResponseString( 2623 cases[i].keyword_provider_response_json); 2624 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 2625 keyword_fetcher = NULL; 2626 } 2627 2628 RunTillProviderDone(); 2629 2630 const std::string description = 2631 "for input with json =" + cases[i].default_provider_response_json; 2632 const ACMatches& matches = provider_->matches(); 2633 // The top match must inline and score as highly as calculated verbatim. 2634 ASSERT_FALSE(matches.empty()); 2635 EXPECT_GE(matches[0].relevance, 1300); 2636 2637 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 2638 // Ensure that the returned matches equal the expectations. 2639 for (size_t j = 0; j < matches.size(); ++j) { 2640 SCOPED_TRACE(description); 2641 EXPECT_EQ(cases[i].matches[j].contents, 2642 base::UTF16ToUTF8(matches[j].contents)); 2643 EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched, 2644 SearchProvider::ShouldPrefetch(matches[j])); 2645 EXPECT_EQ(cases[i].matches[j].type, matches[j].type); 2646 EXPECT_EQ(cases[i].matches[j].from_keyword, 2647 matches[j].keyword == ASCIIToUTF16("k")); 2648 } 2649 } 2650 } 2651 2652 TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_InvalidResponse) { 2653 ClearAllResults(); 2654 2655 std::string input_str("abc"); 2656 QueryForInput(ASCIIToUTF16(input_str), false, false); 2657 2658 // Set up a default fetcher with provided results. 2659 net::TestURLFetcher* fetcher = 2660 test_factory_.GetFetcherByID( 2661 SearchProvider::kDefaultProviderURLFetcherID); 2662 ASSERT_TRUE(fetcher); 2663 fetcher->set_response_code(200); 2664 fetcher->SetResponseString("this is a bad non-json response"); 2665 fetcher->delegate()->OnURLFetchComplete(fetcher); 2666 2667 RunTillProviderDone(); 2668 2669 const ACMatches& matches = provider_->matches(); 2670 2671 // Should have exactly one "search what you typed" match 2672 ASSERT_TRUE(matches.size() == 1); 2673 EXPECT_EQ(input_str, base::UTF16ToUTF8(matches[0].contents)); 2674 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 2675 matches[0].type); 2676 } 2677 2678 // A basic test that verifies that the XSSI guarded JSON response is parsed 2679 // correctly. 2680 TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_ValidResponses) { 2681 struct Match { 2682 std::string contents; 2683 AutocompleteMatchType::Type type; 2684 }; 2685 const Match kEmptyMatch = { 2686 kNotApplicable, AutocompleteMatchType::NUM_TYPES 2687 }; 2688 2689 struct { 2690 const std::string input_text; 2691 const std::string default_provider_response_json; 2692 const Match matches[4]; 2693 } cases[] = { 2694 // No XSSI guard. 2695 { "a", 2696 "[\"a\",[\"b\", \"c\"],[],[]," 2697 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"]," 2698 "\"google:suggestrelevance\":[1, 2]}]", 2699 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2700 { "c", AutocompleteMatchType::SEARCH_SUGGEST }, 2701 { "b", AutocompleteMatchType::SEARCH_SUGGEST }, 2702 kEmptyMatch, 2703 }, 2704 }, 2705 // Standard XSSI guard - )]}'\n. 2706 { "a", 2707 ")]}'\n[\"a\",[\"b\", \"c\"],[],[]," 2708 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"]," 2709 "\"google:suggestrelevance\":[1, 2]}]", 2710 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2711 { "c", AutocompleteMatchType::SEARCH_SUGGEST }, 2712 { "b", AutocompleteMatchType::SEARCH_SUGGEST }, 2713 kEmptyMatch, 2714 }, 2715 }, 2716 // Modified XSSI guard - contains "[". 2717 { "a", 2718 ")]}'\n[)\"[\"a\",[\"b\", \"c\"],[],[]," 2719 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"]," 2720 "\"google:suggestrelevance\":[1, 2]}]", 2721 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2722 { "c", AutocompleteMatchType::SEARCH_SUGGEST }, 2723 { "b", AutocompleteMatchType::SEARCH_SUGGEST }, 2724 kEmptyMatch, 2725 }, 2726 }, 2727 }; 2728 2729 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2730 ClearAllResults(); 2731 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false); 2732 2733 // Set up a default fetcher with provided results. 2734 net::TestURLFetcher* fetcher = 2735 test_factory_.GetFetcherByID( 2736 SearchProvider::kDefaultProviderURLFetcherID); 2737 ASSERT_TRUE(fetcher); 2738 fetcher->set_response_code(200); 2739 fetcher->SetResponseString(cases[i].default_provider_response_json); 2740 fetcher->delegate()->OnURLFetchComplete(fetcher); 2741 2742 RunTillProviderDone(); 2743 2744 const ACMatches& matches = provider_->matches(); 2745 // The top match must inline and score as highly as calculated verbatim. 2746 ASSERT_FALSE(matches.empty()); 2747 EXPECT_GE(matches[0].relevance, 1300); 2748 2749 SCOPED_TRACE("for case: " + base::IntToString(i)); 2750 ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches)); 2751 size_t j = 0; 2752 // Ensure that the returned matches equal the expectations. 2753 for (; j < matches.size(); ++j) { 2754 SCOPED_TRACE("and match: " + base::IntToString(j)); 2755 EXPECT_EQ(cases[i].matches[j].contents, 2756 base::UTF16ToUTF8(matches[j].contents)); 2757 EXPECT_EQ(cases[i].matches[j].type, matches[j].type); 2758 } 2759 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) { 2760 SCOPED_TRACE("and match: " + base::IntToString(j)); 2761 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable); 2762 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES); 2763 } 2764 } 2765 } 2766 2767 // Test that deletion url gets set on an AutocompleteMatch when available for a 2768 // personalized query or a personalized URL. 2769 TEST_F(SearchProviderTest, ParseDeletionUrl) { 2770 struct Match { 2771 std::string contents; 2772 std::string deletion_url; 2773 AutocompleteMatchType::Type type; 2774 }; 2775 2776 const Match kEmptyMatch = { 2777 kNotApplicable, "", AutocompleteMatchType::NUM_TYPES 2778 }; 2779 2780 const char* url[] = { 2781 "http://defaultturl/complete/deleteitems" 2782 "?delq=ab&client=chrome&deltok=xsrf124", 2783 "http://defaultturl/complete/deleteitems" 2784 "?delq=www.amazon.com&client=chrome&deltok=xsrf123", 2785 }; 2786 2787 struct { 2788 const std::string input_text; 2789 const std::string response_json; 2790 const Match matches[5]; 2791 } cases[] = { 2792 // A deletion URL on a personalized query should be reflected in the 2793 // resulting AutocompleteMatch. 2794 { "a", 2795 "[\"a\",[\"ab\", \"ac\",\"www.amazon.com\"],[],[]," 2796 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"," 2797 "\"PERSONALIZED_NAVIGATION\"]," 2798 "\"google:suggestrelevance\":[3, 2, 1]," 2799 "\"google:suggestdetail\":[{\"du\":" 2800 "\"/complete/deleteitems?delq=ab&client=chrome" 2801 "&deltok=xsrf124\"}, {}, {\"du\":" 2802 "\"/complete/deleteitems?delq=www.amazon.com&" 2803 "client=chrome&deltok=xsrf123\"}]}]", 2804 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2805 { "ab", url[0], AutocompleteMatchType::SEARCH_SUGGEST }, 2806 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST }, 2807 { "www.amazon.com", url[1], 2808 AutocompleteMatchType::NAVSUGGEST_PERSONALIZED }, 2809 kEmptyMatch, 2810 }, 2811 }, 2812 // Personalized queries or a personalized URL without deletion URLs 2813 // shouldn't cause errors. 2814 { "a", 2815 "[\"a\",[\"ab\", \"ac\"],[],[]," 2816 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"," 2817 "\"PERSONALIZED_NAVIGATION\"]," 2818 "\"google:suggestrelevance\":[1, 2]," 2819 "\"google:suggestdetail\":[{}, {}]}]", 2820 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2821 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST }, 2822 { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST }, 2823 { "www.amazon.com", "", 2824 AutocompleteMatchType::NAVSUGGEST_PERSONALIZED }, 2825 kEmptyMatch, 2826 }, 2827 }, 2828 // Personalized queries or a personalized URL without 2829 // google:suggestdetail shouldn't cause errors. 2830 { "a", 2831 "[\"a\",[\"ab\", \"ac\"],[],[]," 2832 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\"," 2833 "\"PERSONALIZED_NAVIGATION\"]," 2834 "\"google:suggestrelevance\":[1, 2]}]", 2835 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 2836 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST }, 2837 { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST }, 2838 { "www.amazon.com", "", 2839 AutocompleteMatchType::NAVSUGGEST_PERSONALIZED }, 2840 kEmptyMatch, 2841 }, 2842 }, 2843 }; 2844 2845 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2846 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false); 2847 2848 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 2849 SearchProvider::kDefaultProviderURLFetcherID); 2850 ASSERT_TRUE(fetcher); 2851 fetcher->set_response_code(200); 2852 fetcher->SetResponseString(cases[i].response_json); 2853 fetcher->delegate()->OnURLFetchComplete(fetcher); 2854 2855 RunTillProviderDone(); 2856 2857 const ACMatches& matches = provider_->matches(); 2858 ASSERT_FALSE(matches.empty()); 2859 2860 SCOPED_TRACE("for input with json = " + cases[i].response_json); 2861 2862 for (size_t j = 0; j < matches.size(); ++j) { 2863 const Match& match = cases[i].matches[j]; 2864 SCOPED_TRACE(" and match index: " + base::IntToString(j)); 2865 EXPECT_EQ(match.contents, base::UTF16ToUTF8(matches[j].contents)); 2866 EXPECT_EQ(match.deletion_url, matches[j].GetAdditionalInfo( 2867 "deletion_url")); 2868 } 2869 } 2870 } 2871 2872 TEST_F(SearchProviderTest, ReflectsBookmarkBarState) { 2873 profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, false); 2874 base::string16 term = term1_.substr(0, term1_.length() - 1); 2875 QueryForInput(term, true, false); 2876 ASSERT_FALSE(provider_->matches().empty()); 2877 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 2878 provider_->matches()[0].type); 2879 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL); 2880 EXPECT_FALSE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned); 2881 2882 profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, true); 2883 term = term1_.substr(0, term1_.length() - 1); 2884 QueryForInput(term, true, false); 2885 ASSERT_FALSE(provider_->matches().empty()); 2886 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 2887 provider_->matches()[0].type); 2888 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL); 2889 EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned); 2890 } 2891 2892 TEST_F(SearchProviderTest, CanSendURL) { 2893 TemplateURLData template_url_data; 2894 template_url_data.short_name = ASCIIToUTF16("t"); 2895 template_url_data.SetURL("http://www.google.com/{searchTerms}"); 2896 template_url_data.suggestions_url = "http://www.google.com/{searchTerms}"; 2897 template_url_data.instant_url = "http://does/not/exist?strk=1"; 2898 template_url_data.search_terms_replacement_key = "strk"; 2899 template_url_data.id = SEARCH_ENGINE_GOOGLE; 2900 TemplateURL google_template_url(template_url_data); 2901 2902 // Create field trial. 2903 base::FieldTrial* field_trial = base::FieldTrialList::CreateFieldTrial( 2904 "AutocompleteDynamicTrial_2", "EnableZeroSuggest"); 2905 field_trial->group(); 2906 2907 // Not signed in. 2908 EXPECT_FALSE(SearchProvider::CanSendURL( 2909 GURL("http://www.google.com/search"), 2910 GURL("https://www.google.com/complete/search"), &google_template_url, 2911 metrics::OmniboxEventProto::OTHER, &profile_)); 2912 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(&profile_); 2913 signin->SetAuthenticatedUsername("test"); 2914 2915 // All conditions should be met. 2916 EXPECT_TRUE(SearchProvider::CanSendURL( 2917 GURL("http://www.google.com/search"), 2918 GURL("https://www.google.com/complete/search"), &google_template_url, 2919 metrics::OmniboxEventProto::OTHER, &profile_)); 2920 2921 // Not in field trial. 2922 ResetFieldTrialList(); 2923 EXPECT_FALSE(SearchProvider::CanSendURL( 2924 GURL("http://www.google.com/search"), 2925 GURL("https://www.google.com/complete/search"), &google_template_url, 2926 metrics::OmniboxEventProto::OTHER, &profile_)); 2927 field_trial = base::FieldTrialList::CreateFieldTrial( 2928 "AutocompleteDynamicTrial_2", "EnableZeroSuggest"); 2929 field_trial->group(); 2930 2931 // Invalid page URL. 2932 EXPECT_FALSE(SearchProvider::CanSendURL( 2933 GURL("badpageurl"), 2934 GURL("https://www.google.com/complete/search"), &google_template_url, 2935 metrics::OmniboxEventProto::OTHER, &profile_)); 2936 2937 // Invalid page classification. 2938 EXPECT_FALSE(SearchProvider::CanSendURL( 2939 GURL("http://www.google.com/search"), 2940 GURL("https://www.google.com/complete/search"), &google_template_url, 2941 metrics::OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS, 2942 &profile_)); 2943 2944 // Invalid page classification. 2945 EXPECT_FALSE(SearchProvider::CanSendURL( 2946 GURL("http://www.google.com/search"), 2947 GURL("https://www.google.com/complete/search"), &google_template_url, 2948 metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS, 2949 &profile_)); 2950 2951 // HTTPS page URL on same domain as provider. 2952 EXPECT_TRUE(SearchProvider::CanSendURL( 2953 GURL("https://www.google.com/search"), 2954 GURL("https://www.google.com/complete/search"), 2955 &google_template_url, metrics::OmniboxEventProto::OTHER, &profile_)); 2956 2957 // Non-HTTP[S] page URL on same domain as provider. 2958 EXPECT_FALSE(SearchProvider::CanSendURL( 2959 GURL("ftp://www.google.com/search"), 2960 GURL("https://www.google.com/complete/search"), &google_template_url, 2961 metrics::OmniboxEventProto::OTHER, &profile_)); 2962 2963 // Non-HTTP page URL on different domain. 2964 EXPECT_FALSE(SearchProvider::CanSendURL( 2965 GURL("https://www.notgoogle.com/search"), 2966 GURL("https://www.google.com/complete/search"), &google_template_url, 2967 metrics::OmniboxEventProto::OTHER, &profile_)); 2968 2969 // Non-HTTPS provider. 2970 EXPECT_FALSE(SearchProvider::CanSendURL( 2971 GURL("http://www.google.com/search"), 2972 GURL("http://www.google.com/complete/search"), &google_template_url, 2973 metrics::OmniboxEventProto::OTHER, &profile_)); 2974 2975 // Suggest disabled. 2976 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, false); 2977 EXPECT_FALSE(SearchProvider::CanSendURL( 2978 GURL("http://www.google.com/search"), 2979 GURL("https://www.google.com/complete/search"), &google_template_url, 2980 metrics::OmniboxEventProto::OTHER, &profile_)); 2981 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true); 2982 2983 // Incognito. 2984 EXPECT_FALSE(SearchProvider::CanSendURL( 2985 GURL("http://www.google.com/search"), 2986 GURL("https://www.google.com/complete/search"), &google_template_url, 2987 metrics::OmniboxEventProto::OTHER, profile_.GetOffTheRecordProfile())); 2988 2989 // Tab sync not enabled. 2990 profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncKeepEverythingSynced, 2991 false); 2992 profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, false); 2993 EXPECT_FALSE(SearchProvider::CanSendURL( 2994 GURL("http://www.google.com/search"), 2995 GURL("https://www.google.com/complete/search"), &google_template_url, 2996 metrics::OmniboxEventProto::OTHER, &profile_)); 2997 profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, true); 2998 2999 // Tab sync is encrypted. 3000 ProfileSyncService* service = 3001 ProfileSyncServiceFactory::GetInstance()->GetForProfile(&profile_); 3002 syncer::ModelTypeSet encrypted_types = service->GetEncryptedDataTypes(); 3003 encrypted_types.Put(syncer::SESSIONS); 3004 service->OnEncryptedTypesChanged(encrypted_types, false); 3005 EXPECT_FALSE(SearchProvider::CanSendURL( 3006 GURL("http://www.google.com/search"), 3007 GURL("https://www.google.com/complete/search"), &google_template_url, 3008 metrics::OmniboxEventProto::OTHER, &profile_)); 3009 encrypted_types.Remove(syncer::SESSIONS); 3010 service->OnEncryptedTypesChanged(encrypted_types, false); 3011 3012 // Check that there were no side effects from previous tests. 3013 EXPECT_TRUE(SearchProvider::CanSendURL( 3014 GURL("http://www.google.com/search"), 3015 GURL("https://www.google.com/complete/search"), &google_template_url, 3016 metrics::OmniboxEventProto::OTHER, &profile_)); 3017 } 3018 3019 TEST_F(SearchProviderTest, TestDeleteMatch) { 3020 AutocompleteMatch match(provider_, 0, true, 3021 AutocompleteMatchType::SEARCH_SUGGEST); 3022 match.RecordAdditionalInfo( 3023 SearchProvider::kDeletionUrlKey, 3024 "https://www.google.com/complete/deleteitem?q=foo"); 3025 3026 // Test a successful deletion request. 3027 provider_->matches_.push_back(match); 3028 provider_->DeleteMatch(match); 3029 EXPECT_FALSE(provider_->deletion_handlers_.empty()); 3030 EXPECT_TRUE(provider_->matches_.empty()); 3031 // Set up a default fetcher with provided results. 3032 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 3033 SearchProvider::kDeletionURLFetcherID); 3034 ASSERT_TRUE(fetcher); 3035 fetcher->set_response_code(200); 3036 fetcher->delegate()->OnURLFetchComplete(fetcher); 3037 EXPECT_TRUE(provider_->deletion_handlers_.empty()); 3038 EXPECT_TRUE(provider_->is_success()); 3039 3040 // Test a failing deletion request. 3041 provider_->matches_.push_back(match); 3042 provider_->DeleteMatch(match); 3043 EXPECT_FALSE(provider_->deletion_handlers_.empty()); 3044 // Set up a default fetcher with provided results. 3045 fetcher = test_factory_.GetFetcherByID( 3046 SearchProvider::kDeletionURLFetcherID); 3047 ASSERT_TRUE(fetcher); 3048 fetcher->set_response_code(500); 3049 fetcher->delegate()->OnURLFetchComplete(fetcher); 3050 EXPECT_TRUE(provider_->deletion_handlers_.empty()); 3051 EXPECT_FALSE(provider_->is_success()); 3052 } 3053 3054 TEST_F(SearchProviderTest, TestDeleteHistoryQueryMatch) { 3055 GURL term_url( 3056 AddSearchToHistory(default_t_url_, ASCIIToUTF16("flash games"), 1)); 3057 profile_.BlockUntilHistoryProcessesPendingRequests(); 3058 3059 AutocompleteMatch games; 3060 QueryForInput(ASCIIToUTF16("fla"), false, false); 3061 profile_.BlockUntilHistoryProcessesPendingRequests(); 3062 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 3063 ASSERT_TRUE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games)); 3064 3065 size_t matches_before = provider_->matches().size(); 3066 provider_->DeleteMatch(games); 3067 EXPECT_EQ(matches_before - 1, provider_->matches().size()); 3068 3069 // Process history deletions. 3070 profile_.BlockUntilHistoryProcessesPendingRequests(); 3071 3072 // Check that the match is gone. 3073 QueryForInput(ASCIIToUTF16("fla"), false, false); 3074 profile_.BlockUntilHistoryProcessesPendingRequests(); 3075 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 3076 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games)); 3077 } 3078 3079 // Verifies that duplicates are preserved in AddMatchToMap(). 3080 TEST_F(SearchProviderTest, CheckDuplicateMatchesSaved) { 3081 AddSearchToHistory(default_t_url_, ASCIIToUTF16("a"), 1); 3082 AddSearchToHistory(default_t_url_, ASCIIToUTF16("alpha"), 1); 3083 AddSearchToHistory(default_t_url_, ASCIIToUTF16("avid"), 1); 3084 3085 profile_.BlockUntilHistoryProcessesPendingRequests(); 3086 QueryForInput(ASCIIToUTF16("a"), false, false); 3087 3088 // Make sure the default provider's suggest service was queried. 3089 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 3090 SearchProvider::kDefaultProviderURLFetcherID); 3091 ASSERT_TRUE(fetcher); 3092 3093 // Tell the SearchProvider the suggest query is done. 3094 fetcher->set_response_code(200); 3095 fetcher->SetResponseString( 3096 "[\"a\",[\"a\", \"alpha\", \"avid\", \"apricot\"],[],[]," 3097 "{\"google:suggestrelevance\":[1450, 1200, 1150, 1100]," 3098 "\"google:verbatimrelevance\":1350}]"); 3099 fetcher->delegate()->OnURLFetchComplete(fetcher); 3100 fetcher = NULL; 3101 3102 // Run till the history results complete. 3103 RunTillProviderDone(); 3104 3105 AutocompleteMatch verbatim, match_alpha, match_apricot, match_avid; 3106 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim)); 3107 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("alpha"), &match_alpha)); 3108 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("apricot"), &match_apricot)); 3109 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("avid"), &match_avid)); 3110 3111 // Verbatim match duplicates are added such that each one has a higher 3112 // relevance than the previous one. 3113 EXPECT_EQ(2U, verbatim.duplicate_matches.size()); 3114 3115 // Other match duplicates are added in descending relevance order. 3116 EXPECT_EQ(1U, match_alpha.duplicate_matches.size()); 3117 EXPECT_EQ(1U, match_avid.duplicate_matches.size()); 3118 3119 EXPECT_EQ(0U, match_apricot.duplicate_matches.size()); 3120 } 3121 3122 TEST_F(SearchProviderTest, SuggestQueryUsesToken) { 3123 CommandLine::ForCurrentProcess()->AppendSwitch( 3124 switches::kEnableAnswersInSuggest); 3125 3126 TemplateURLService* turl_model = 3127 TemplateURLServiceFactory::GetForProfile(&profile_); 3128 3129 TemplateURLData data; 3130 data.short_name = ASCIIToUTF16("default"); 3131 data.SetKeyword(data.short_name); 3132 data.SetURL("http://example/{searchTerms}{google:sessionToken}"); 3133 data.suggestions_url = 3134 "http://suggest/?q={searchTerms}&{google:sessionToken}"; 3135 default_t_url_ = new TemplateURL(data); 3136 turl_model->Add(default_t_url_); 3137 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_); 3138 3139 base::string16 term = term1_.substr(0, term1_.length() - 1); 3140 QueryForInput(term, false, false); 3141 3142 // Make sure the default provider's suggest service was queried. 3143 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 3144 SearchProvider::kDefaultProviderURLFetcherID); 3145 ASSERT_TRUE(fetcher); 3146 3147 // And the URL matches what we expected. 3148 TemplateURLRef::SearchTermsArgs search_terms_args(term); 3149 search_terms_args.session_token = provider_->current_token_; 3150 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms( 3151 search_terms_args, turl_model->search_terms_data())); 3152 EXPECT_EQ(fetcher->GetOriginalURL().spec(), expected_url.spec()); 3153 3154 // Complete running the fetcher to clean up. 3155 fetcher->set_response_code(200); 3156 fetcher->delegate()->OnURLFetchComplete(fetcher); 3157 RunTillProviderDone(); 3158 } 3159 3160 TEST_F(SearchProviderTest, SessionToken) { 3161 // Subsequent calls always get the same token. 3162 std::string token = provider_->GetSessionToken(); 3163 std::string token2 = provider_->GetSessionToken(); 3164 EXPECT_EQ(token, token2); 3165 EXPECT_FALSE(token.empty()); 3166 3167 // Calls do not regenerate a token. 3168 provider_->current_token_ = "PRE-EXISTING TOKEN"; 3169 token = provider_->GetSessionToken(); 3170 EXPECT_EQ(token, "PRE-EXISTING TOKEN"); 3171 3172 // ... unless the token has expired. 3173 provider_->current_token_.clear(); 3174 const base::TimeDelta kSmallDelta = base::TimeDelta::FromMilliseconds(1); 3175 provider_->token_expiration_time_ = base::TimeTicks::Now() - kSmallDelta; 3176 token = provider_->GetSessionToken(); 3177 EXPECT_FALSE(token.empty()); 3178 EXPECT_EQ(token, provider_->current_token_); 3179 3180 // The expiration time is always updated. 3181 provider_->GetSessionToken(); 3182 base::TimeTicks expiration_time_1 = provider_->token_expiration_time_; 3183 base::PlatformThread::Sleep(kSmallDelta); 3184 provider_->GetSessionToken(); 3185 base::TimeTicks expiration_time_2 = provider_->token_expiration_time_; 3186 EXPECT_GT(expiration_time_2, expiration_time_1); 3187 EXPECT_GE(expiration_time_2, expiration_time_1 + kSmallDelta); 3188 } 3189