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