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