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