search_provider_unittest.cc revision d3868032626d59662ff73b372b5d584c1d144c53
1// Copyright 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/autocomplete/search_provider.h" 6 7#include "base/command_line.h" 8#include "base/metrics/field_trial.h" 9#include "base/prefs/pref_service.h" 10#include "base/run_loop.h" 11#include "base/strings/string_util.h" 12#include "base/strings/utf_string_conversions.h" 13#include "base/time/time.h" 14#include "build/build_config.h" 15#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" 16#include "chrome/browser/autocomplete/autocomplete_controller.h" 17#include "chrome/browser/autocomplete/autocomplete_input.h" 18#include "chrome/browser/autocomplete/autocomplete_match.h" 19#include "chrome/browser/autocomplete/autocomplete_provider.h" 20#include "chrome/browser/autocomplete/autocomplete_provider_listener.h" 21#include "chrome/browser/autocomplete/history_url_provider.h" 22#include "chrome/browser/history/history_service.h" 23#include "chrome/browser/history/history_service_factory.h" 24#include "chrome/browser/omnibox/omnibox_field_trial.h" 25#include "chrome/browser/search/search.h" 26#include "chrome/browser/search_engines/template_url.h" 27#include "chrome/browser/search_engines/template_url_service.h" 28#include "chrome/browser/search_engines/template_url_service_factory.h" 29#include "chrome/common/chrome_switches.h" 30#include "chrome/common/metrics/entropy_provider.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 "content/public/test/test_browser_thread_bundle.h" 35#include "net/url_request/test_url_fetcher_factory.h" 36#include "net/url_request/url_request_status.h" 37#include "testing/gtest/include/gtest/gtest.h" 38 39 40// SearchProviderTest --------------------------------------------------------- 41 42// The following environment is configured for these tests: 43// . The TemplateURL default_t_url_ is set as the default provider. 44// . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This 45// TemplateURL has a valid suggest and search URL. 46// . The URL created by using the search term term1_ with default_t_url_ is 47// added to history. 48// . The URL created by using the search term keyword_term_ with keyword_t_url_ 49// is added to history. 50// . test_factory_ is set as the URLFetcherFactory. 51class SearchProviderTest : public testing::Test, 52 public AutocompleteProviderListener { 53 public: 54 struct ResultInfo { 55 ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES) { 56 } 57 ResultInfo(GURL gurl, 58 AutocompleteMatch::Type result_type, 59 string16 fill_into_edit) 60 : gurl(gurl), 61 result_type(result_type), 62 fill_into_edit(fill_into_edit) { 63 } 64 65 const GURL gurl; 66 const AutocompleteMatch::Type result_type; 67 const string16 fill_into_edit; 68 }; 69 70 struct TestData { 71 const string16 input; 72 const size_t num_results; 73 const ResultInfo output[3]; 74 }; 75 76 SearchProviderTest() 77 : default_t_url_(NULL), 78 term1_(ASCIIToUTF16("term1")), 79 keyword_t_url_(NULL), 80 keyword_term_(ASCIIToUTF16("keyword")), 81 run_loop_(NULL) { 82 } 83 84 static void SetUpTestCase(); 85 static void TearDownTestCase(); 86 87 // See description above class for what this registers. 88 virtual void SetUp() OVERRIDE; 89 virtual void TearDown() OVERRIDE; 90 91 void RunTest(TestData* cases, int num_cases, bool prefer_keyword); 92 93 protected: 94 // Needed for AutocompleteFieldTrial::ActivateStaticTrials(); 95 static base::FieldTrialList* field_trial_list_; 96 97 // Default value used for testing. 98 static const std::string kNotApplicable; 99 100 // Adds a search for |term|, using the engine |t_url| to the history, and 101 // returns the URL for that search. 102 GURL AddSearchToHistory(TemplateURL* t_url, string16 term, int visit_count); 103 104 // Looks for a match in |provider_| with |contents| equal to |contents|. 105 // Sets |match| to it if found. Returns whether |match| was set. 106 bool FindMatchWithContents(const string16& contents, 107 AutocompleteMatch* match); 108 109 // Looks for a match in |provider_| with destination |url|. Sets |match| to 110 // it if found. Returns whether |match| was set. 111 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match); 112 113 // AutocompleteProviderListener: 114 // If we're waiting for the provider to finish, this exits the message loop. 115 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE; 116 117 // Runs a nested message loop until provider_ is done. The message loop is 118 // exited by way of OnProviderUpdate. 119 void RunTillProviderDone(); 120 121 // Invokes Start on provider_, then runs all pending tasks. 122 void QueryForInput(const string16& text, 123 bool prevent_inline_autocomplete, 124 bool prefer_keyword); 125 126 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is 127 // non-NULL, sets it to the "what you typed" entry for |text|. 128 void QueryForInputAndSetWYTMatch(const string16& text, 129 AutocompleteMatch* wyt_match); 130 131 // Notifies the URLFetcher for the suggest query corresponding to the default 132 // search provider that it's done. 133 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE. 134 void FinishDefaultSuggestQuery(); 135 136 // See description above class for details of these fields. 137 TemplateURL* default_t_url_; 138 const string16 term1_; 139 GURL term1_url_; 140 TemplateURL* keyword_t_url_; 141 const string16 keyword_term_; 142 GURL keyword_url_; 143 144 content::TestBrowserThreadBundle thread_bundle_; 145 146 // URLFetcherFactory implementation registered. 147 net::TestURLFetcherFactory test_factory_; 148 149 // Profile we use. 150 TestingProfile profile_; 151 152 // The provider. 153 scoped_refptr<SearchProvider> provider_; 154 155 // If non-NULL, OnProviderUpdate quits the current |run_loop_|. 156 base::RunLoop* run_loop_; 157 158 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest); 159}; 160 161// static 162base::FieldTrialList* SearchProviderTest::field_trial_list_ = NULL; 163const std::string SearchProviderTest::kNotApplicable = "Not Applicable"; 164 165// static 166void SearchProviderTest::SetUpTestCase() { 167 // Set up Suggest experiments. 168 field_trial_list_ = new base::FieldTrialList( 169 new metrics::SHA1EntropyProvider("foo")); 170 base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial( 171 "AutocompleteDynamicTrial_0", "DefaultGroup"); 172 trial->group(); 173} 174 175// static 176void SearchProviderTest::TearDownTestCase() { 177 // Make sure the global instance of FieldTrialList is gone. 178 delete field_trial_list_; 179} 180 181void SearchProviderTest::SetUp() { 182 // Make sure that fetchers are automatically ungregistered upon destruction. 183 test_factory_.set_remove_fetcher_on_delete(true); 184 185 // We need both the history service and template url model loaded. 186 ASSERT_TRUE(profile_.CreateHistoryService(true, false)); 187 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 188 &profile_, &TemplateURLServiceFactory::BuildInstanceFor); 189 190 TemplateURLService* turl_model = 191 TemplateURLServiceFactory::GetForProfile(&profile_); 192 193 turl_model->Load(); 194 195 // Reset the default TemplateURL. 196 TemplateURLData data; 197 data.short_name = ASCIIToUTF16("t"); 198 data.SetURL("http://defaultturl/{searchTerms}"); 199 data.suggestions_url = "http://defaultturl2/{searchTerms}"; 200 data.instant_url = "http://does/not/exist?strk=1"; 201 data.search_terms_replacement_key = "strk"; 202 default_t_url_ = new TemplateURL(&profile_, data); 203 turl_model->Add(default_t_url_); 204 turl_model->SetDefaultSearchProvider(default_t_url_); 205 TemplateURLID default_provider_id = default_t_url_->id(); 206 ASSERT_NE(0, default_provider_id); 207 208 // Add url1, with search term term1_. 209 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1); 210 211 // Create another TemplateURL. 212 data.short_name = ASCIIToUTF16("k"); 213 data.SetKeyword(ASCIIToUTF16("k")); 214 data.SetURL("http://keyword/{searchTerms}"); 215 data.suggestions_url = "http://suggest_keyword/{searchTerms}"; 216 keyword_t_url_ = new TemplateURL(&profile_, data); 217 turl_model->Add(keyword_t_url_); 218 ASSERT_NE(0, keyword_t_url_->id()); 219 220 // Add a page and search term for keyword_t_url_. 221 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1); 222 223 // Keywords are updated by the InMemoryHistoryBackend only after the message 224 // has been processed on the history thread. Block until history processes all 225 // requests to ensure the InMemoryDatabase is the state we expect it. 226 profile_.BlockUntilHistoryProcessesPendingRequests(); 227 228 provider_ = new SearchProvider(this, &profile_); 229 provider_->kMinimumTimeBetweenSuggestQueriesMs = 0; 230} 231 232void SearchProviderTest::TearDown() { 233 base::RunLoop().RunUntilIdle(); 234 235 // Shutdown the provider before the profile. 236 provider_ = NULL; 237} 238 239void SearchProviderTest::RunTest(TestData* cases, 240 int num_cases, 241 bool prefer_keyword) { 242 ACMatches matches; 243 for (int i = 0; i < num_cases; ++i) { 244 AutocompleteInput input(cases[i].input, string16::npos, string16(), GURL(), 245 AutocompleteInput::INVALID_SPEC, false, 246 prefer_keyword, true, 247 AutocompleteInput::ALL_MATCHES); 248 provider_->Start(input, false); 249 matches = provider_->matches(); 250 string16 diagnostic_details = ASCIIToUTF16("Input was: ") + cases[i].input + 251 ASCIIToUTF16("; prefer_keyword was: ") + 252 (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false")); 253 EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details; 254 if (matches.size() == cases[i].num_results) { 255 for (size_t j = 0; j < cases[i].num_results; ++j) { 256 EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) << 257 diagnostic_details; 258 EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) << 259 diagnostic_details; 260 EXPECT_EQ(cases[i].output[j].fill_into_edit, 261 matches[j].fill_into_edit) << 262 diagnostic_details; 263 } 264 } 265 } 266} 267 268void SearchProviderTest::OnProviderUpdate(bool updated_matches) { 269 if (run_loop_ && provider_->done()) { 270 run_loop_->Quit(); 271 run_loop_ = NULL; 272 } 273} 274 275void SearchProviderTest::RunTillProviderDone() { 276 if (provider_->done()) 277 return; 278 279 base::RunLoop run_loop; 280 run_loop_ = &run_loop; 281 run_loop.Run(); 282} 283 284void SearchProviderTest::QueryForInput(const string16& text, 285 bool prevent_inline_autocomplete, 286 bool prefer_keyword) { 287 // Start a query. 288 AutocompleteInput input(text, string16::npos, string16(), GURL(), 289 AutocompleteInput::INVALID_SPEC, 290 prevent_inline_autocomplete, prefer_keyword, true, 291 AutocompleteInput::ALL_MATCHES); 292 provider_->Start(input, false); 293 294 // RunUntilIdle so that the task scheduled by SearchProvider to create the 295 // URLFetchers runs. 296 base::RunLoop().RunUntilIdle(); 297} 298 299void SearchProviderTest::QueryForInputAndSetWYTMatch( 300 const string16& text, 301 AutocompleteMatch* wyt_match) { 302 QueryForInput(text, false, false); 303 profile_.BlockUntilHistoryProcessesPendingRequests(); 304 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery()); 305 if (!wyt_match) 306 return; 307 ASSERT_GE(provider_->matches().size(), 1u); 308 EXPECT_TRUE(FindMatchWithDestination(GURL( 309 default_t_url_->url_ref().ReplaceSearchTerms( 310 TemplateURLRef::SearchTermsArgs(text))), 311 wyt_match)); 312} 313 314GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url, 315 string16 term, 316 int visit_count) { 317 HistoryService* history = 318 HistoryServiceFactory::GetForProfile(&profile_, 319 Profile::EXPLICIT_ACCESS); 320 GURL search(t_url->url_ref().ReplaceSearchTerms( 321 TemplateURLRef::SearchTermsArgs(term))); 322 static base::Time last_added_time; 323 last_added_time = std::max(base::Time::Now(), 324 last_added_time + base::TimeDelta::FromMicroseconds(1)); 325 history->AddPageWithDetails(search, string16(), visit_count, visit_count, 326 last_added_time, false, history::SOURCE_BROWSED); 327 history->SetKeywordSearchTermsForURL(search, t_url->id(), term); 328 return search; 329} 330 331bool SearchProviderTest::FindMatchWithContents(const string16& contents, 332 AutocompleteMatch* match) { 333 for (ACMatches::const_iterator i = provider_->matches().begin(); 334 i != provider_->matches().end(); ++i) { 335 if (i->contents == contents) { 336 *match = *i; 337 return true; 338 } 339 } 340 return false; 341} 342 343bool SearchProviderTest::FindMatchWithDestination(const GURL& url, 344 AutocompleteMatch* match) { 345 for (ACMatches::const_iterator i = provider_->matches().begin(); 346 i != provider_->matches().end(); ++i) { 347 if (i->destination_url == url) { 348 *match = *i; 349 return true; 350 } 351 } 352 return false; 353} 354 355void SearchProviderTest::FinishDefaultSuggestQuery() { 356 net::TestURLFetcher* default_fetcher = 357 test_factory_.GetFetcherByID( 358 SearchProvider::kDefaultProviderURLFetcherID); 359 ASSERT_TRUE(default_fetcher); 360 361 // Tell the SearchProvider the default suggest query is done. 362 default_fetcher->set_response_code(200); 363 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 364} 365 366 367// Actual Tests --------------------------------------------------------------- 368 369// Make sure we query history for the default provider and a URLFetcher is 370// created for the default provider suggest results. 371TEST_F(SearchProviderTest, QueryDefaultProvider) { 372 string16 term = term1_.substr(0, term1_.length() - 1); 373 QueryForInput(term, false, false); 374 375 // Make sure the default providers suggest service was queried. 376 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 377 SearchProvider::kDefaultProviderURLFetcherID); 378 ASSERT_TRUE(fetcher); 379 380 // And the URL matches what we expected. 381 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms( 382 TemplateURLRef::SearchTermsArgs(term))); 383 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url); 384 385 // Tell the SearchProvider the suggest query is done. 386 fetcher->set_response_code(200); 387 fetcher->delegate()->OnURLFetchComplete(fetcher); 388 fetcher = NULL; 389 390 // Run till the history results complete. 391 RunTillProviderDone(); 392 393 // The SearchProvider is done. Make sure it has a result for the history 394 // term term1. 395 AutocompleteMatch term1_match; 396 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match)); 397 // Term1 should not have a description, it's set later. 398 EXPECT_TRUE(term1_match.description.empty()); 399 400 AutocompleteMatch wyt_match; 401 EXPECT_TRUE(FindMatchWithDestination( 402 GURL(default_t_url_->url_ref().ReplaceSearchTerms( 403 TemplateURLRef::SearchTermsArgs(term))), &wyt_match)); 404 EXPECT_TRUE(wyt_match.description.empty()); 405 406 // The match for term1 should be more relevant than the what you typed result. 407 EXPECT_GT(term1_match.relevance, wyt_match.relevance); 408} 409 410TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) { 411 string16 term = term1_.substr(0, term1_.length() - 1); 412 QueryForInput(term, true, false); 413 414 ASSERT_FALSE(provider_->matches().empty()); 415 ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 416 provider_->matches()[0].type); 417} 418 419// Issues a query that matches the registered keyword and makes sure history 420// is queried as well as URLFetchers getting created. 421TEST_F(SearchProviderTest, QueryKeywordProvider) { 422 string16 term = keyword_term_.substr(0, keyword_term_.length() - 1); 423 QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term, 424 false, 425 false); 426 427 // Make sure the default providers suggest service was queried. 428 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID( 429 SearchProvider::kDefaultProviderURLFetcherID); 430 ASSERT_TRUE(default_fetcher); 431 432 // Tell the SearchProvider the default suggest query is done. 433 default_fetcher->set_response_code(200); 434 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 435 default_fetcher = NULL; 436 437 // Make sure the keyword providers suggest service was queried. 438 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID( 439 SearchProvider::kKeywordProviderURLFetcherID); 440 ASSERT_TRUE(keyword_fetcher); 441 442 // And the URL matches what we expected. 443 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms( 444 TemplateURLRef::SearchTermsArgs(term))); 445 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url); 446 447 // Tell the SearchProvider the keyword suggest query is done. 448 keyword_fetcher->set_response_code(200); 449 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 450 keyword_fetcher = NULL; 451 452 // Run till the history results complete. 453 RunTillProviderDone(); 454 455 // The SearchProvider is done. Make sure it has a result for the history 456 // term keyword. 457 AutocompleteMatch match; 458 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match)); 459 460 // The match should have an associated keyword. 461 EXPECT_FALSE(match.keyword.empty()); 462 463 // The fill into edit should contain the keyword. 464 EXPECT_EQ(keyword_t_url_->keyword() + char16(' ') + keyword_term_, 465 match.fill_into_edit); 466} 467 468TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) { 469 // None of the following input strings should be sent to the suggest server, 470 // because they may contain private data. 471 const char* inputs[] = { 472 "username:password", 473 "http://username:password", 474 "https://username:password", 475 "username:password@hostname", 476 "http://username:password@hostname/", 477 "file://filename", 478 "data://data", 479 "unknownscheme:anything", 480 "http://hostname/?query=q", 481 "http://hostname/path#ref", 482 "https://hostname/path", 483 }; 484 485 for (size_t i = 0; i < arraysize(inputs); ++i) { 486 QueryForInput(ASCIIToUTF16(inputs[i]), false, false); 487 // Make sure the default providers suggest service was not queried. 488 ASSERT_TRUE(test_factory_.GetFetcherByID( 489 SearchProvider::kDefaultProviderURLFetcherID) == NULL); 490 // Run till the history results complete. 491 RunTillProviderDone(); 492 } 493} 494 495TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) { 496 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse( 497 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor); 498 GURL url = AddSearchToHistory(default_t_url_, 499 ASCIIToUTF16("docs.google.com"), 1); 500 501 // Add the term as a url. 502 HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)-> 503 AddPageWithDetails(GURL("http://docs.google.com"), string16(), 1, 1, 504 base::Time::Now(), false, history::SOURCE_BROWSED); 505 profile_.BlockUntilHistoryProcessesPendingRequests(); 506 507 AutocompleteMatch wyt_match; 508 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"), 509 &wyt_match)); 510 511 // There should be two matches, one for what you typed, the other for 512 // 'docs.google.com'. The search term should have a lower priority than the 513 // what you typed match. 514 ASSERT_EQ(2u, provider_->matches().size()); 515 AutocompleteMatch term_match; 516 EXPECT_TRUE(FindMatchWithDestination(url, &term_match)); 517 EXPECT_GT(wyt_match.relevance, term_match.relevance); 518} 519 520// A multiword search with one visit should not autocomplete until multiple 521// words are typed. 522TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) { 523 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"), 524 1)); 525 profile_.BlockUntilHistoryProcessesPendingRequests(); 526 527 AutocompleteMatch wyt_match; 528 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"), 529 &wyt_match)); 530 ASSERT_EQ(2u, provider_->matches().size()); 531 AutocompleteMatch term_match; 532 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 533 EXPECT_GT(wyt_match.relevance, term_match.relevance); 534 535 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"), 536 &wyt_match)); 537 ASSERT_EQ(2u, provider_->matches().size()); 538 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 539 EXPECT_GT(term_match.relevance, wyt_match.relevance); 540} 541 542// A multiword search with more than one visit should autocomplete immediately. 543TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) { 544 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 545 2)); 546 profile_.BlockUntilHistoryProcessesPendingRequests(); 547 548 AutocompleteMatch wyt_match; 549 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"), 550 &wyt_match)); 551 ASSERT_EQ(2u, provider_->matches().size()); 552 AutocompleteMatch term_match; 553 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 554 EXPECT_GT(term_match.relevance, wyt_match.relevance); 555} 556 557// Autocompletion should work at a word boundary after a space. 558TEST_F(SearchProviderTest, AutocompleteAfterSpace) { 559 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"), 560 2)); 561 profile_.BlockUntilHistoryProcessesPendingRequests(); 562 563 AutocompleteMatch wyt_match; 564 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "), 565 &wyt_match)); 566 ASSERT_EQ(2u, provider_->matches().size()); 567 AutocompleteMatch term_match; 568 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 569 EXPECT_GT(term_match.relevance, wyt_match.relevance); 570} 571 572// Newer multiword searches should score more highly than older ones. 573TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) { 574 GURL term_url_a(AddSearchToHistory(default_t_url_, 575 ASCIIToUTF16("three searches aaa"), 1)); 576 GURL term_url_b(AddSearchToHistory(default_t_url_, 577 ASCIIToUTF16("three searches bbb"), 1)); 578 profile_.BlockUntilHistoryProcessesPendingRequests(); 579 580 AutocompleteMatch wyt_match; 581 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"), 582 &wyt_match)); 583 ASSERT_EQ(3u, provider_->matches().size()); 584 AutocompleteMatch term_match_a; 585 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 586 AutocompleteMatch term_match_b; 587 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 588 EXPECT_GT(term_match_b.relevance, term_match_a.relevance); 589 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 590} 591 592// An autocompleted multiword search should not be replaced by a different 593// autocompletion while the user is still typing a valid prefix. 594TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) { 595 GURL term_url_a(AddSearchToHistory(default_t_url_, 596 ASCIIToUTF16("four searches aaa"), 2)); 597 GURL term_url_b(AddSearchToHistory(default_t_url_, 598 ASCIIToUTF16("four searches bbb"), 1)); 599 profile_.BlockUntilHistoryProcessesPendingRequests(); 600 601 AutocompleteMatch wyt_match; 602 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"), 603 &wyt_match)); 604 ASSERT_EQ(3u, provider_->matches().size()); 605 AutocompleteMatch term_match_a; 606 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 607 AutocompleteMatch term_match_b; 608 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 609 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 610 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 611 612 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"), 613 &wyt_match)); 614 ASSERT_EQ(3u, provider_->matches().size()); 615 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a)); 616 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b)); 617 EXPECT_GT(term_match_a.relevance, wyt_match.relevance); 618 EXPECT_GT(wyt_match.relevance, term_match_b.relevance); 619} 620 621// Non-completable multiword searches should not crowd out single-word searches. 622TEST_F(SearchProviderTest, DontCrowdOutSingleWords) { 623 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1)); 624 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1); 625 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1); 626 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1); 627 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1); 628 profile_.BlockUntilHistoryProcessesPendingRequests(); 629 630 AutocompleteMatch wyt_match; 631 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"), 632 &wyt_match)); 633 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size()); 634 AutocompleteMatch term_match; 635 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 636 EXPECT_GT(term_match.relevance, wyt_match.relevance); 637} 638 639// Inline autocomplete matches regardless of case differences from the input. 640TEST_F(SearchProviderTest, InlineMixedCaseMatches) { 641 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1)); 642 profile_.BlockUntilHistoryProcessesPendingRequests(); 643 644 AutocompleteMatch wyt_match; 645 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"), 646 &wyt_match)); 647 ASSERT_EQ(2u, provider_->matches().size()); 648 AutocompleteMatch term_match; 649 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match)); 650 EXPECT_GT(term_match.relevance, wyt_match.relevance); 651 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit); 652 EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion); 653} 654 655// Verifies AutocompleteControllers return results (including keyword 656// results) in the right order and set descriptions for them correctly. 657TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) { 658 // Add an entry that corresponds to a keyword search with 'term2'. 659 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1); 660 profile_.BlockUntilHistoryProcessesPendingRequests(); 661 662 AutocompleteController controller(&profile_, NULL, 663 AutocompleteProvider::TYPE_SEARCH); 664 controller.Start(AutocompleteInput( 665 ASCIIToUTF16("k t"), string16::npos, string16(), GURL(), 666 AutocompleteInput::INVALID_SPEC, false, false, true, 667 AutocompleteInput::ALL_MATCHES)); 668 const AutocompleteResult& result = controller.result(); 669 670 // There should be three matches, one for the keyword history, one for 671 // keyword provider's what-you-typed, and one for the default provider's 672 // what you typed, in that order. 673 ASSERT_EQ(3u, result.size()); 674 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type); 675 EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE, 676 result.match_at(1).type); 677 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 678 result.match_at(2).type); 679 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance); 680 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance); 681 682 // The two keyword results should come with the keyword we expect. 683 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword); 684 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword); 685 // The default provider has a different keyword. (We don't explicitly 686 // set it during this test, so all we do is assert that it's different.) 687 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword); 688 689 // The top result will always have a description. The third result, 690 // coming from a different provider than the first two, should also. 691 // Whether the second result has one doesn't matter much. (If it was 692 // missing, people would infer that it's the same search provider as 693 // the one above it.) 694 EXPECT_FALSE(result.match_at(0).description.empty()); 695 EXPECT_FALSE(result.match_at(2).description.empty()); 696 EXPECT_NE(result.match_at(0).description, result.match_at(2).description); 697} 698 699TEST_F(SearchProviderTest, KeywordVerbatim) { 700 TestData cases[] = { 701 // Test a simple keyword input. 702 { ASCIIToUTF16("k foo"), 2, 703 { ResultInfo(GURL("http://keyword/foo"), 704 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 705 ASCIIToUTF16("k foo")), 706 ResultInfo(GURL("http://defaultturl/k%20foo"), 707 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 708 ASCIIToUTF16("k foo") ) } }, 709 710 // Make sure extra whitespace after the keyword doesn't change the 711 // keyword verbatim query. 712 { ASCIIToUTF16("k foo"), 2, 713 { ResultInfo(GURL("http://keyword/foo"), 714 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 715 ASCIIToUTF16("k foo")), 716 ResultInfo(GURL("http://defaultturl/k%20%20%20foo"), 717 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 718 ASCIIToUTF16("k foo")) } }, 719 // Leading whitespace should be stripped before SearchProvider gets the 720 // input; hence there are no tests here about how it handles those inputs. 721 722 // But whitespace elsewhere in the query string should matter to both 723 // matches. 724 { ASCIIToUTF16("k foo bar"), 2, 725 { ResultInfo(GURL("http://keyword/foo%20%20bar"), 726 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 727 ASCIIToUTF16("k foo bar")), 728 ResultInfo(GURL("http://defaultturl/k%20%20foo%20%20bar"), 729 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 730 ASCIIToUTF16("k foo bar")) } }, 731 // Note in the above test case we don't test trailing whitespace because 732 // SearchProvider still doesn't handle this well. See related bugs: 733 // 102690, 99239, 164635. 734 735 // Keywords can be prefixed by certain things that should get ignored 736 // when constructing the keyword match. 737 { ASCIIToUTF16("www.k foo"), 2, 738 { ResultInfo(GURL("http://keyword/foo"), 739 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 740 ASCIIToUTF16("k foo")), 741 ResultInfo(GURL("http://defaultturl/www.k%20foo"), 742 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 743 ASCIIToUTF16("www.k foo")) } }, 744 { ASCIIToUTF16("http://k foo"), 2, 745 { ResultInfo(GURL("http://keyword/foo"), 746 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 747 ASCIIToUTF16("k foo")), 748 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"), 749 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 750 ASCIIToUTF16("http://k foo")) } }, 751 { ASCIIToUTF16("http://www.k foo"), 2, 752 { ResultInfo(GURL("http://keyword/foo"), 753 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 754 ASCIIToUTF16("k foo")), 755 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"), 756 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 757 ASCIIToUTF16("http://www.k foo")) } }, 758 759 // A keyword with no remaining input shouldn't get a keyword 760 // verbatim match. 761 { ASCIIToUTF16("k"), 1, 762 { ResultInfo(GURL("http://defaultturl/k"), 763 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 764 ASCIIToUTF16("k")) } }, 765 { ASCIIToUTF16("k "), 1, 766 { ResultInfo(GURL("http://defaultturl/k%20"), 767 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 768 ASCIIToUTF16("k ")) } } 769 770 // The fact that verbatim queries to keyword are handled by KeywordProvider 771 // not SearchProvider is tested in 772 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc. 773 }; 774 775 // Test not in keyword mode. 776 RunTest(cases, arraysize(cases), false); 777 778 // Test in keyword mode. (Both modes should give the same result.) 779 RunTest(cases, arraysize(cases), true); 780} 781 782// Ensures command-line flags are reflected in the URLs the search provider 783// generates. 784TEST_F(SearchProviderTest, CommandLineOverrides) { 785 TemplateURLService* turl_model = 786 TemplateURLServiceFactory::GetForProfile(&profile_); 787 788 TemplateURLData data; 789 data.short_name = ASCIIToUTF16("default"); 790 data.SetKeyword(data.short_name); 791 data.SetURL("{google:baseURL}{searchTerms}"); 792 default_t_url_ = new TemplateURL(&profile_, data); 793 turl_model->Add(default_t_url_); 794 turl_model->SetDefaultSearchProvider(default_t_url_); 795 796 CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL, 797 "http://www.bar.com/"); 798 CommandLine::ForCurrentProcess()->AppendSwitchASCII( 799 switches::kExtraSearchQueryParams, "a=b"); 800 801 TestData cases[] = { 802 { ASCIIToUTF16("k a"), 2, 803 { ResultInfo(GURL("http://keyword/a"), 804 AutocompleteMatchType::SEARCH_OTHER_ENGINE, 805 ASCIIToUTF16("k a")), 806 ResultInfo(GURL("http://www.bar.com/k%20a?a=b"), 807 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 808 ASCIIToUTF16("k a")) } }, 809 }; 810 811 RunTest(cases, arraysize(cases), false); 812} 813 814// Verifies Navsuggest results don't set a TemplateURL, which Instant relies on. 815// Also verifies that just the *first* navigational result is listed as a match 816// if suggested relevance scores were not sent. 817TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) { 818 QueryForInput(ASCIIToUTF16("a.c"), false, false); 819 820 // Make sure the default providers suggest service was queried. 821 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 822 SearchProvider::kDefaultProviderURLFetcherID); 823 ASSERT_TRUE(fetcher); 824 825 // Tell the SearchProvider the suggest query is done. 826 fetcher->set_response_code(200); 827 fetcher->SetResponseString( 828 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[]," 829 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]"); 830 fetcher->delegate()->OnURLFetchComplete(fetcher); 831 fetcher = NULL; 832 833 // Run till the history results complete. 834 RunTillProviderDone(); 835 836 // Make sure the only match is 'a.com' and it doesn't have a template_url. 837 AutocompleteMatch nav_match; 838 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match)); 839 EXPECT_TRUE(nav_match.keyword.empty()); 840 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match)); 841} 842 843// Verifies that the most relevant suggest results are added properly. 844TEST_F(SearchProviderTest, SuggestRelevance) { 845 QueryForInput(ASCIIToUTF16("a"), false, false); 846 847 // Make sure the default provider's suggest service was queried. 848 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 849 SearchProvider::kDefaultProviderURLFetcherID); 850 ASSERT_TRUE(fetcher); 851 852 // Tell the SearchProvider the suggest query is done. 853 fetcher->set_response_code(200); 854 fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]"); 855 fetcher->delegate()->OnURLFetchComplete(fetcher); 856 fetcher = NULL; 857 858 // Run till the history results complete. 859 RunTillProviderDone(); 860 861 // Check the expected verbatim and (first 3) suggestions' relative relevances. 862 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4; 863 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim)); 864 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1)); 865 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2)); 866 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3)); 867 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4)); 868 EXPECT_GT(verbatim.relevance, match_a1.relevance); 869 EXPECT_GT(match_a1.relevance, match_a2.relevance); 870 EXPECT_GT(match_a2.relevance, match_a3.relevance); 871} 872 873// Verifies that suggest results with relevance scores are added 874// properly when using the default fetcher. When adding a new test 875// case to this test, please consider adding it to the tests in 876// KeywordFetcherSuggestRelevance below. 877TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) { 878 struct { 879 const std::string json; 880 const std::string matches[4]; 881 const std::string inline_autocompletion; 882 } cases[] = { 883 // Ensure that suggestrelevance scores reorder matches. 884 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 885 { "a", "c", "b", kNotApplicable }, std::string() }, 886 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 887 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 888 "\"google:suggestrelevance\":[1, 2]}]", 889 { "a", "c.com", "b.com", kNotApplicable }, std::string() }, 890 891 // Without suggested relevance scores, we should only allow one 892 // navsuggest result to be be displayed. 893 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 894 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 895 { "a", "b.com", kNotApplicable, kNotApplicable }, std::string() }, 896 897 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 898 // Negative values will have no effect; the calculated value will be used. 899 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 900 "\"google:suggestrelevance\":[9998]}]", 901 { "a", "a1", kNotApplicable, kNotApplicable }, std::string() }, 902 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 903 "\"google:suggestrelevance\":[9999]}]", 904 { "a1", "a", kNotApplicable, kNotApplicable }, "1" }, 905 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 906 "\"google:suggestrelevance\":[9999]}]", 907 { "a1", kNotApplicable, kNotApplicable, kNotApplicable }, "1" }, 908 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 909 "\"google:suggestrelevance\":[9999]}]", 910 { "a1", "a", kNotApplicable, kNotApplicable }, "1" }, 911 { "[\"a\",[\"http://a.com\"],[],[]," 912 "{\"google:suggesttype\":[\"NAVIGATION\"]," 913 "\"google:verbatimrelevance\":9999," 914 "\"google:suggestrelevance\":[9998]}]", 915 { "a", "a.com", kNotApplicable, kNotApplicable }, std::string() }, 916 { "[\"a\",[\"http://a.com\"],[],[]," 917 "{\"google:suggesttype\":[\"NAVIGATION\"]," 918 "\"google:verbatimrelevance\":9998," 919 "\"google:suggestrelevance\":[9999]}]", 920 { "a.com", "a", kNotApplicable, kNotApplicable }, ".com" }, 921 { "[\"a\",[\"http://a.com\"],[],[]," 922 "{\"google:suggesttype\":[\"NAVIGATION\"]," 923 "\"google:verbatimrelevance\":0," 924 "\"google:suggestrelevance\":[9999]}]", 925 { "a.com", kNotApplicable, kNotApplicable, kNotApplicable }, ".com" }, 926 { "[\"a\",[\"http://a.com\"],[],[]," 927 "{\"google:suggesttype\":[\"NAVIGATION\"]," 928 "\"google:verbatimrelevance\":-1," 929 "\"google:suggestrelevance\":[9999]}]", 930 { "a.com", "a", kNotApplicable, kNotApplicable }, ".com" }, 931 932 // Ensure that both types of relevance scores reorder matches together. 933 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 934 "\"google:verbatimrelevance\":9998}]", 935 { "a1", "a", "a2", kNotApplicable }, "1" }, 936 937 // Ensure that only inlinable matches may be ranked as the highest result. 938 // Ignore all suggested relevance scores if this constraint is violated. 939 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 940 { "a", "b", kNotApplicable, kNotApplicable }, std::string() }, 941 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 942 "\"google:verbatimrelevance\":0}]", 943 { "a", "b", kNotApplicable, kNotApplicable }, std::string() }, 944 { "[\"a\",[\"http://b.com\"],[],[]," 945 "{\"google:suggesttype\":[\"NAVIGATION\"]," 946 "\"google:suggestrelevance\":[9999]}]", 947 { "a", "b.com", kNotApplicable, kNotApplicable }, std::string() }, 948 { "[\"a\",[\"http://b.com\"],[],[]," 949 "{\"google:suggesttype\":[\"NAVIGATION\"]," 950 "\"google:suggestrelevance\":[9999]," 951 "\"google:verbatimrelevance\":0}]", 952 { "a", "b.com", kNotApplicable, kNotApplicable }, std::string() }, 953 954 // Ensure that the top result is ranked as highly as calculated verbatim. 955 // Ignore the suggested verbatim relevance if this constraint is violated. 956 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 957 { "a", "a1", kNotApplicable, kNotApplicable }, std::string() }, 958 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 959 { "a", "a1", kNotApplicable, kNotApplicable }, std::string() }, 960 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 961 "\"google:verbatimrelevance\":0}]", 962 { "a", "a1", kNotApplicable, kNotApplicable }, std::string() }, 963 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 964 "\"google:verbatimrelevance\":0}]", 965 { "a", "a2", "a1", kNotApplicable }, std::string() }, 966 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 967 "\"google:verbatimrelevance\":2}]", 968 { "a", "a2", "a1", kNotApplicable }, std::string() }, 969 { "[\"a\",[\"http://a.com\"],[],[]," 970 "{\"google:suggesttype\":[\"NAVIGATION\"]," 971 "\"google:suggestrelevance\":[1]," 972 "\"google:verbatimrelevance\":0}]", 973 { "a", "a.com", kNotApplicable, kNotApplicable }, std::string() }, 974 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 975 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 976 "\"google:suggestrelevance\":[1, 2]," 977 "\"google:verbatimrelevance\":0}]", 978 { "a", "a2.com", "a1.com", kNotApplicable }, std::string() }, 979 980 // Ensure that all suggestions are considered, regardless of order. 981 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 982 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 983 { "a", "h", "g", "f" }, std::string() }, 984 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 985 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 986 "\"http://h.com\"],[],[]," 987 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 988 "\"NAVIGATION\", \"NAVIGATION\"," 989 "\"NAVIGATION\", \"NAVIGATION\"," 990 "\"NAVIGATION\"]," 991 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 992 { "a", "h.com", "g.com", "f.com" }, std::string() }, 993 994 // Ensure that incorrectly sized suggestion relevance lists are ignored. 995 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 996 { "a", "a1", "a2", kNotApplicable }, std::string() }, 997 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 998 { "a", "a1", kNotApplicable, kNotApplicable }, std::string() }, 999 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1000 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1001 "\"google:suggestrelevance\":[1]}]", 1002 { "a", "a1.com", kNotApplicable, kNotApplicable }, std::string() }, 1003 { "[\"a\",[\"http://a1.com\"],[],[]," 1004 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1005 "\"google:suggestrelevance\":[9999, 1]}]", 1006 { "a", "a1.com", kNotApplicable, kNotApplicable }, std::string() }, 1007 1008 // Ensure that all 'verbatim' results are merged with their maximum score. 1009 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1010 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1011 { "a2", "a", "a1", kNotApplicable }, "2" }, 1012 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1013 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1014 "\"google:verbatimrelevance\":0}]", 1015 { "a2", "a", "a1", kNotApplicable }, "2" }, 1016 1017 // Ensure that verbatim is always generated without other suggestions. 1018 // TODO(msw): Ensure verbatimrelevance is respected (except suppression). 1019 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1020 { "a", kNotApplicable, kNotApplicable, kNotApplicable }, std::string() }, 1021 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1022 { "a", kNotApplicable, kNotApplicable, kNotApplicable }, std::string() }, 1023 }; 1024 1025 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1026 QueryForInput(ASCIIToUTF16("a"), false, false); 1027 net::TestURLFetcher* fetcher = 1028 test_factory_.GetFetcherByID( 1029 SearchProvider::kDefaultProviderURLFetcherID); 1030 ASSERT_TRUE(fetcher); 1031 fetcher->set_response_code(200); 1032 fetcher->SetResponseString(cases[i].json); 1033 fetcher->delegate()->OnURLFetchComplete(fetcher); 1034 RunTillProviderDone(); 1035 1036 const std::string description = "for input with json=" + cases[i].json; 1037 const ACMatches& matches = provider_->matches(); 1038 // The top match must inline and score as highly as calculated verbatim. 1039 ASSERT_FALSE(matches.empty()); 1040 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1041 matches[0].inline_autocompletion) << description; 1042 EXPECT_GE(matches[0].relevance, 1300) << description; 1043 1044 size_t j = 0; 1045 // Ensure that the returned matches equal the expectations. 1046 for (; j < matches.size(); ++j) 1047 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), 1048 matches[j].contents) << description; 1049 // Ensure that no expected matches are missing. 1050 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1051 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) << 1052 "Case # " << i << " " << description; 1053 } 1054} 1055 1056// Verifies that suggest results with relevance scores are added 1057// properly when using the keyword fetcher. This is similar to the 1058// test DefaultFetcherSuggestRelevance above but this uses inputs that 1059// trigger keyword suggestions (i.e., "k a" rather than "a") and has 1060// different expectations (because now the results are a mix of 1061// keyword suggestions and default provider suggestions). When a new 1062// test is added to this TEST_F, please consider if it would be 1063// appropriate to add to DefaultFetcherSuggestRelevance as well. 1064TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) { 1065 struct { 1066 const std::string json; 1067 const struct { 1068 const std::string contents; 1069 const bool from_keyword; 1070 } matches[5]; 1071 const std::string inline_autocompletion; 1072 } cases[] = { 1073 // Ensure that suggest relevance scores reorder matches and that 1074 // the keyword verbatim (lacking a suggested verbatim score) beats 1075 // the default provider verbatim. 1076 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", 1077 { { "a", true }, 1078 { "k a", false }, 1079 { "c", true }, 1080 { "b", true }, 1081 { kNotApplicable, false } }, 1082 std::string() }, 1083 // Again, check that relevance scores reorder matches, just this 1084 // time with navigation matches. This also checks that with 1085 // suggested relevance scores we allow multiple navsuggest results. 1086 // It's odd that navsuggest results that come from a keyword 1087 // provider are marked as not a keyword result. I think this 1088 // comes from them not going to a keyword search engine). 1089 // TODO(mpearson): Investigate the implications (if any) of 1090 // tagging these results appropriately. If so, do it because it 1091 // makes more sense. 1092 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[]," 1093 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1094 "\"google:suggestrelevance\":[1301, 1302, 1303]}]", 1095 { { "a", true }, 1096 { "d", true }, 1097 { "c.com", false }, 1098 { "b.com", false }, 1099 { "k a", false }, }, 1100 std::string() }, 1101 1102 // Without suggested relevance scores, we should only allow one 1103 // navsuggest result to be be displayed. 1104 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[]," 1105 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]", 1106 { { "a", true }, 1107 { "b.com", false }, 1108 { "k a", false }, 1109 { kNotApplicable, false }, 1110 { kNotApplicable, false } }, 1111 std::string() }, 1112 1113 // Ensure that verbatimrelevance scores reorder or suppress verbatim. 1114 // Negative values will have no effect; the calculated value will be used. 1115 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999," 1116 "\"google:suggestrelevance\":[9998]}]", 1117 { { "a", true }, 1118 { "a1", true }, 1119 { "k a", false }, 1120 { kNotApplicable, false }, 1121 { kNotApplicable, false } }, 1122 std::string() }, 1123 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998," 1124 "\"google:suggestrelevance\":[9999]}]", 1125 { { "a1", true }, 1126 { "a", true }, 1127 { "k a", false }, 1128 { kNotApplicable, false }, 1129 { kNotApplicable, false } }, 1130 "1" }, 1131 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0," 1132 "\"google:suggestrelevance\":[9999]}]", 1133 { { "a1", true }, 1134 { "k a", false }, 1135 { kNotApplicable, false }, 1136 { kNotApplicable, false }, 1137 { kNotApplicable, false } }, 1138 "1" }, 1139 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1," 1140 "\"google:suggestrelevance\":[9999]}]", 1141 { { "a1", true }, 1142 { "a", true }, 1143 { "k a", false }, 1144 { kNotApplicable, false }, 1145 { kNotApplicable, false } }, 1146 "1" }, 1147 { "[\"a\",[\"http://a.com\"],[],[]," 1148 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1149 "\"google:verbatimrelevance\":9999," 1150 "\"google:suggestrelevance\":[9998]}]", 1151 { { "a", true }, 1152 { "a.com", false }, 1153 { "k a", false }, 1154 { kNotApplicable, false }, 1155 { kNotApplicable, false } }, 1156 std::string() }, 1157 1158 // Ensure that both types of relevance scores reorder matches together. 1159 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997]," 1160 "\"google:verbatimrelevance\":9998}]", 1161 { { "a1", true }, 1162 { "a", true }, 1163 { "a2", true }, 1164 { "k a", false }, 1165 { kNotApplicable, false } }, 1166 "1" }, 1167 1168 // Ensure that only inlinable matches may be ranked as the highest result. 1169 // Ignore all suggested relevance scores if this constraint is violated. 1170 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]", 1171 { { "a", true }, 1172 { "b", true }, 1173 { "k a", false }, 1174 { kNotApplicable, false }, 1175 { kNotApplicable, false } }, 1176 std::string() }, 1177 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]," 1178 "\"google:verbatimrelevance\":0}]", 1179 { { "a", true }, 1180 { "b", true }, 1181 { "k a", false }, 1182 { kNotApplicable, false }, 1183 { kNotApplicable, false } }, 1184 std::string() }, 1185 { "[\"a\",[\"http://b.com\"],[],[]," 1186 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1187 "\"google:suggestrelevance\":[9999]}]", 1188 { { "a", true }, 1189 { "b.com", false }, 1190 { "k a", false }, 1191 { kNotApplicable, false }, 1192 { kNotApplicable, false } }, 1193 std::string() }, 1194 { "[\"a\",[\"http://b.com\"],[],[]," 1195 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1196 "\"google:suggestrelevance\":[9999]," 1197 "\"google:verbatimrelevance\":0}]", 1198 { { "a", true }, 1199 { "b.com", false }, 1200 { "k a", false }, 1201 { kNotApplicable, false }, 1202 { kNotApplicable, false } }, 1203 std::string() }, 1204 1205 // Ensure that the top result is ranked as highly as calculated verbatim. 1206 // Ignore the suggested verbatim relevance if this constraint is violated. 1207 // Note that keyword suggestions by default (not in suggested relevance 1208 // mode) score more highly than the default verbatim. 1209 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]", 1210 { { "a", true }, 1211 { "a1", true }, 1212 { "k a", false }, 1213 { kNotApplicable, false }, 1214 { kNotApplicable, false } }, 1215 std::string() }, 1216 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]", 1217 { { "a", true }, 1218 { "a1", true }, 1219 { "k a", false }, 1220 { kNotApplicable, false }, 1221 { kNotApplicable, false } }, 1222 std::string() }, 1223 // Continuing the same category of tests, but make sure we keep the 1224 // suggested relevance scores even as we discard the verbatim relevance 1225 // scores. 1226 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1]," 1227 "\"google:verbatimrelevance\":0}]", 1228 { { "a", true }, 1229 { "k a", false }, 1230 { "a1", true }, 1231 { kNotApplicable, false }, 1232 { kNotApplicable, false } }, 1233 std::string() }, 1234 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2]," 1235 "\"google:verbatimrelevance\":0}]", 1236 { { "a", true }, 1237 { "k a", false }, 1238 { "a2", true }, 1239 { "a1", true }, 1240 { kNotApplicable, false } }, 1241 std::string() }, 1242 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3]," 1243 "\"google:verbatimrelevance\":2}]", 1244 { { "a", true }, 1245 { "k a", false }, 1246 { "a2", true }, 1247 { "a1", true }, 1248 { kNotApplicable, false } }, 1249 std::string() }, 1250 1251 // Ensure that all suggestions are considered, regardless of order. 1252 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[]," 1253 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1254 { { "a", true }, 1255 { "k a", false }, 1256 { "h", true }, 1257 { "g", true }, 1258 { "f", true } }, 1259 std::string() }, 1260 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\"," 1261 "\"http://e.com\", \"http://f.com\", \"http://g.com\"," 1262 "\"http://h.com\"],[],[]," 1263 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"," 1264 "\"NAVIGATION\", \"NAVIGATION\"," 1265 "\"NAVIGATION\", \"NAVIGATION\"," 1266 "\"NAVIGATION\"]," 1267 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]", 1268 { { "a", true }, 1269 { "k a", false }, 1270 { "h.com", false }, 1271 { "g.com", false }, 1272 { "f.com", false } }, 1273 std::string() }, 1274 1275 // Ensure that incorrectly sized suggestion relevance lists are ignored. 1276 // Note that keyword suggestions by default (not in suggested relevance 1277 // mode) score more highly than the default verbatim. 1278 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]", 1279 { { "a", true }, 1280 { "a1", true }, 1281 { "a2", true }, 1282 { "k a", false }, 1283 { kNotApplicable, false } }, 1284 std::string() }, 1285 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]", 1286 { { "a", true }, 1287 { "a1", true }, 1288 { "k a", false }, 1289 { kNotApplicable, false }, 1290 { kNotApplicable, false } }, 1291 std::string() }, 1292 // In this case, ignored the suggested relevance scores means we keep 1293 // only one navsuggest result. 1294 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1295 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1296 "\"google:suggestrelevance\":[1]}]", 1297 { { "a", true }, 1298 { "a1.com", false }, 1299 { "k a", false }, 1300 { kNotApplicable, false }, 1301 { kNotApplicable, false } }, 1302 std::string() }, 1303 { "[\"a\",[\"http://a1.com\"],[],[]," 1304 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1305 "\"google:suggestrelevance\":[9999, 1]}]", 1306 { { "a", true }, 1307 { "a1.com", false }, 1308 { "k a", false }, 1309 { kNotApplicable, false }, 1310 { kNotApplicable, false } }, 1311 std::string() }, 1312 1313 // Ensure that all 'verbatim' results are merged with their maximum score. 1314 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1315 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1316 { { "a2", true }, 1317 { "a", true }, 1318 { "a1", true }, 1319 { "k a", false }, 1320 { kNotApplicable, false } }, 1321 "2" }, 1322 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[]," 1323 "{\"google:suggestrelevance\":[9998, 9997, 9999]," 1324 "\"google:verbatimrelevance\":0}]", 1325 { { "a2", true }, 1326 { "a", true }, 1327 { "a1", true }, 1328 { "k a", false }, 1329 { kNotApplicable, false } }, 1330 "2" }, 1331 1332 // Ensure that verbatim is always generated without other suggestions. 1333 // TODO(mpearson): Ensure the value of verbatimrelevance is respected 1334 // (except when suggested relevances are ignored). 1335 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]", 1336 { { "a", true }, 1337 { "k a", false }, 1338 { kNotApplicable, false }, 1339 { kNotApplicable, false }, 1340 { kNotApplicable, false } }, 1341 std::string() }, 1342 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]", 1343 { { "a", true }, 1344 { "k a", false }, 1345 { kNotApplicable, false }, 1346 { kNotApplicable, false }, 1347 { kNotApplicable, false } }, 1348 std::string() }, 1349 1350 // Check that navsuggestions will be demoted below queries. 1351 // (Navsuggestions are not allowed to appear first.) In the process, 1352 // make sure the navsuggestions still remain in the same order. 1353 // First, check the situation where navsuggest scores more than verbatim 1354 // and there are no query suggestions. 1355 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1356 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1357 "\"google:verbatimrelevance\":9990," 1358 "\"google:suggestrelevance\":[9998, 9999]}]", 1359 { { "a", true }, 1360 { "a2.com", false }, 1361 { "a1.com", false }, 1362 { "k a", false }, 1363 { kNotApplicable, false } }, 1364 std::string() }, 1365 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1366 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1367 "\"google:verbatimrelevance\":9990," 1368 "\"google:suggestrelevance\":[9999, 9998]}]", 1369 { { "a", true }, 1370 { "a1.com", false }, 1371 { "a2.com", false }, 1372 { "k a", false }, 1373 { kNotApplicable, false } }, 1374 std::string() }, 1375 // Check when navsuggest scores more than verbatim and there is query 1376 // suggestion but it scores lower. 1377 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1378 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1379 "\"google:verbatimrelevance\":9990," 1380 "\"google:suggestrelevance\":[9998, 9999, 1300]}]", 1381 { { "a", true }, 1382 { "a2.com", false }, 1383 { "a1.com", false }, 1384 { "a3", true }, 1385 { "k a", false } }, 1386 std::string() }, 1387 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1388 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1389 "\"google:verbatimrelevance\":9990," 1390 "\"google:suggestrelevance\":[9999, 9998, 1300]}]", 1391 { { "a", true }, 1392 { "a1.com", false }, 1393 { "a2.com", false }, 1394 { "a3", true }, 1395 { "k a", false } }, 1396 std::string() }, 1397 // Check when navsuggest scores more than a query suggestion. There is 1398 // a verbatim but it scores lower. 1399 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1400 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1401 "\"google:verbatimrelevance\":9990," 1402 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1403 { { "a3", true }, 1404 { "a2.com", false }, 1405 { "a1.com", false }, 1406 { "a", true }, 1407 { "k a", false } }, 1408 "3" }, 1409 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1410 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1411 "\"google:verbatimrelevance\":9990," 1412 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1413 { { "a3", true }, 1414 { "a1.com", false }, 1415 { "a2.com", false }, 1416 { "a", true }, 1417 { "k a", false } }, 1418 "3" }, 1419 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1420 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1421 "\"google:verbatimrelevance\":0," 1422 "\"google:suggestrelevance\":[9998, 9999, 9997]}]", 1423 { { "a3", true }, 1424 { "a2.com", false }, 1425 { "a1.com", false }, 1426 { "k a", false }, 1427 { kNotApplicable, false } }, 1428 "3" }, 1429 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1430 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1431 "\"google:verbatimrelevance\":0," 1432 "\"google:suggestrelevance\":[9999, 9998, 9997]}]", 1433 { { "a3", true }, 1434 { "a1.com", false }, 1435 { "a2.com", false }, 1436 { "k a", false }, 1437 { kNotApplicable, false } }, 1438 "3" }, 1439 // Check when there is neither verbatim nor a query suggestion that, 1440 // because we can't demote navsuggestions below a query suggestion, 1441 // we abandon suggested relevance scores entirely. One consequence is 1442 // that this means we restore the keyword verbatim match. Note 1443 // that in this case of abandoning suggested relevance scores, we still 1444 // keep the navsuggestions in the same order, but we revert to only allowing 1445 // one navigation to appear because the scores are completely local. 1446 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1447 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1448 "\"google:verbatimrelevance\":0," 1449 "\"google:suggestrelevance\":[9998, 9999]}]", 1450 { { "a", true }, 1451 { "a2.com", false }, 1452 { "k a", false }, 1453 { kNotApplicable, false }, 1454 { kNotApplicable, false } }, 1455 std::string() }, 1456 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[]," 1457 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]," 1458 "\"google:verbatimrelevance\":0," 1459 "\"google:suggestrelevance\":[9999, 9998]}]", 1460 { { "a", true }, 1461 { "a1.com", false }, 1462 { "k a", false }, 1463 { kNotApplicable, false }, 1464 { kNotApplicable, false } }, 1465 std::string() }, 1466 // More checks that everything works when it's not necessary to demote. 1467 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1468 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1469 "\"google:verbatimrelevance\":9990," 1470 "\"google:suggestrelevance\":[9997, 9998, 9999]}]", 1471 { { "a3", true }, 1472 { "a2.com", false }, 1473 { "a1.com", false }, 1474 { "a", true }, 1475 { "k a", false } }, 1476 "3" }, 1477 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[]," 1478 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"]," 1479 "\"google:verbatimrelevance\":9990," 1480 "\"google:suggestrelevance\":[9998, 9997, 9999]}]", 1481 { { "a3", true }, 1482 { "a1.com", false }, 1483 { "a2.com", false }, 1484 { "a", true }, 1485 { "k a", false } }, 1486 "3" }, 1487 }; 1488 1489 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1490 QueryForInput(ASCIIToUTF16("k a"), false, true); 1491 1492 // Set up a default fetcher with no results. 1493 net::TestURLFetcher* default_fetcher = 1494 test_factory_.GetFetcherByID( 1495 SearchProvider::kDefaultProviderURLFetcherID); 1496 ASSERT_TRUE(default_fetcher); 1497 default_fetcher->set_response_code(200); 1498 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher); 1499 default_fetcher = NULL; 1500 1501 // Set up a keyword fetcher with provided results. 1502 net::TestURLFetcher* keyword_fetcher = 1503 test_factory_.GetFetcherByID( 1504 SearchProvider::kKeywordProviderURLFetcherID); 1505 ASSERT_TRUE(keyword_fetcher); 1506 keyword_fetcher->set_response_code(200); 1507 keyword_fetcher->SetResponseString(cases[i].json); 1508 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); 1509 keyword_fetcher = NULL; 1510 RunTillProviderDone(); 1511 1512 const std::string description = "for input with json=" + cases[i].json; 1513 const ACMatches& matches = provider_->matches(); 1514 // The top match must inline and score as highly as calculated verbatim. 1515 ASSERT_FALSE(matches.empty()); 1516 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1517 matches[0].inline_autocompletion) << description; 1518 EXPECT_GE(matches[0].relevance, 1300) << description; 1519 1520 size_t j = 0; 1521 // Ensure that the returned matches equal the expectations. 1522 for (; j < matches.size(); ++j) { 1523 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents), 1524 matches[j].contents) << description; 1525 EXPECT_EQ(cases[i].matches[j].from_keyword, 1526 matches[j].keyword == ASCIIToUTF16("k")) << description; 1527 } 1528 // Ensure that no expected matches are missing. 1529 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1530 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) << 1531 "Case # " << i << " " << description; 1532 } 1533} 1534 1535TEST_F(SearchProviderTest, LocalAndRemoteRelevances) { 1536 // Enable Instant Extended in order to allow an increased number of 1537 // suggestions. 1538 chrome::EnableInstantExtendedAPIForTesting(); 1539 1540 // We hardcode the string "term1" below, so ensure that the search term that 1541 // got added to history already is that string. 1542 ASSERT_EQ(ASCIIToUTF16("term1"), term1_); 1543 string16 term = term1_.substr(0, term1_.length() - 1); 1544 1545 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2); 1546 profile_.BlockUntilHistoryProcessesPendingRequests(); 1547 1548 struct { 1549 const string16 input; 1550 const std::string json; 1551 const std::string matches[6]; 1552 } cases[] = { 1553 // The history results outscore the default verbatim score. term2 has more 1554 // visits so it outscores term1. The suggestions are still returned since 1555 // they're server-scored. 1556 { term, 1557 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 1558 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 1559 "\"google:suggestrelevance\":[1, 2, 3]}]", 1560 { "term2", "term1", "term", "a3", "a2", "a1" } }, 1561 // Because we already have three suggestions by the time we see the history 1562 // results, they don't get returned. 1563 { term, 1564 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[]," 1565 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"]," 1566 "\"google:verbatimrelevance\":1450," 1567 "\"google:suggestrelevance\":[1440, 1430, 1420]}]", 1568 { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } }, 1569 // If we only have two suggestions, we have room for a history result. 1570 { term, 1571 "[\"term\",[\"a1\", \"a2\"],[],[]," 1572 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," 1573 "\"google:verbatimrelevance\":1450," 1574 "\"google:suggestrelevance\":[1430, 1410]}]", 1575 { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } }, 1576 // If we have more than three suggestions, they should all be returned as 1577 // long as we have enough total space for them. 1578 { term, 1579 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 1580 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 1581 "\"google:verbatimrelevance\":1450," 1582 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]", 1583 { "term", "a1", "a2", "a3", "a4", kNotApplicable } }, 1584 { term, 1585 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[]," 1586 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"," 1587 "\"QUERY\", \"QUERY\"]," 1588 "\"google:verbatimrelevance\":1450," 1589 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]", 1590 { "term", "a1", "a2", "a3", "a4", "a5" } }, 1591 { term, 1592 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[]," 1593 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"]," 1594 "\"google:verbatimrelevance\":1450," 1595 "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]", 1596 { "term", "a1", "a2", "term2", "a3", "a4" } }, 1597 // When the input looks like a URL, we disallow having a query as the 1598 // highest-ranking result. If the query was provided by a suggestion, we 1599 // reset the suggest scores to enforce this (see 1600 // SearchProvider::UpdateMatches()). Even if we reset the suggest scores, 1601 // however, we should still allow navsuggestions to be treated as 1602 // server-provided. 1603 { ASCIIToUTF16("a.com"), 1604 "[\"a.com\",[\"a1\", \"a2\", \"a.com/1\", \"a.com/2\"],[],[]," 1605 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"NAVIGATION\"," 1606 "\"NAVIGATION\"]," 1607 // A verbatim query for URL-like input scores 850, so the navigation 1608 // scores here should bracket it. 1609 "\"google:suggestrelevance\":[9999, 9998, 900, 800]}]", 1610 { "a.com/1", "a.com", "a.com/2", "a1", kNotApplicable, kNotApplicable } }, 1611 }; 1612 1613 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1614 QueryForInput(cases[i].input, false, false); 1615 net::TestURLFetcher* fetcher = 1616 test_factory_.GetFetcherByID( 1617 SearchProvider::kDefaultProviderURLFetcherID); 1618 ASSERT_TRUE(fetcher); 1619 fetcher->set_response_code(200); 1620 fetcher->SetResponseString(cases[i].json); 1621 fetcher->delegate()->OnURLFetchComplete(fetcher); 1622 RunTillProviderDone(); 1623 1624 const std::string description = "for input with json=" + cases[i].json; 1625 const ACMatches& matches = provider_->matches(); 1626 1627 // Ensure no extra matches are present. 1628 ASSERT_LE(matches.size(), 6U); 1629 1630 size_t j = 0; 1631 // Ensure that the returned matches equal the expectations. 1632 for (; j < matches.size(); ++j) 1633 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), 1634 matches[j].contents) << description; 1635 // Ensure that no expected matches are missing. 1636 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) 1637 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) << 1638 "Case # " << i << " " << description; 1639 } 1640} 1641 1642// Verifies suggest relevance behavior for URL input. 1643TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) { 1644 struct { 1645 const std::string input; 1646 const std::string json; 1647 const std::string match_contents[4]; 1648 const AutocompleteMatch::Type match_types[4]; 1649 } cases[] = { 1650 // Ensure topmost NAVIGATION matches are allowed for URL input. 1651 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[]," 1652 "{\"google:suggesttype\":[\"NAVIGATION\"]," 1653 "\"google:suggestrelevance\":[9999]}]", 1654 { "a.com/a", "a.com", kNotApplicable, kNotApplicable }, 1655 { AutocompleteMatchType::NAVSUGGEST, 1656 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1657 AutocompleteMatchType::NUM_TYPES, 1658 AutocompleteMatchType::NUM_TYPES } }, 1659 1660 // Ensure topmost SUGGEST matches are not allowed for URL input. 1661 // SearchProvider disregards search and verbatim suggested relevances. 1662 { "a.com", "[\"a.com\",[\"a.com info\"],[],[]," 1663 "{\"google:suggestrelevance\":[9999]}]", 1664 { "a.com", "a.com info", kNotApplicable, kNotApplicable }, 1665 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1666 AutocompleteMatchType::SEARCH_SUGGEST, 1667 AutocompleteMatchType::NUM_TYPES, AutocompleteMatchType::NUM_TYPES } }, 1668 { "a.com", "[\"a.com\",[\"a.com/a\"],[],[]," 1669 "{\"google:suggestrelevance\":[9999]}]", 1670 { "a.com", "a.com/a", kNotApplicable, kNotApplicable }, 1671 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1672 AutocompleteMatchType::SEARCH_SUGGEST, 1673 AutocompleteMatchType::NUM_TYPES, AutocompleteMatchType::NUM_TYPES } }, 1674 1675 // Ensure the fallback mechanism allows inlinable NAVIGATION matches. 1676 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 1677 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1678 "\"google:suggestrelevance\":[9999, 9998]}]", 1679 { "a.com/b", "a.com", "a.com/a", kNotApplicable }, 1680 { AutocompleteMatchType::NAVSUGGEST, 1681 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1682 AutocompleteMatchType::SEARCH_SUGGEST, 1683 AutocompleteMatchType::NUM_TYPES } }, 1684 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[]," 1685 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1686 "\"google:suggestrelevance\":[9998, 9997]," 1687 "\"google:verbatimrelevance\":9999}]", 1688 { "a.com/b", "a.com", "a.com/a", kNotApplicable }, 1689 { AutocompleteMatchType::NAVSUGGEST, 1690 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1691 AutocompleteMatchType::SEARCH_SUGGEST, 1692 AutocompleteMatchType::NUM_TYPES } }, 1693 1694 // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches. 1695 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 1696 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1697 "\"google:suggestrelevance\":[9999, 9998]}]", 1698 { "a.com", "abc.com", "a.com/a", kNotApplicable }, 1699 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1700 AutocompleteMatchType::NAVSUGGEST, 1701 AutocompleteMatchType::SEARCH_SUGGEST, 1702 AutocompleteMatchType::NUM_TYPES } }, 1703 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[]," 1704 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," 1705 "\"google:suggestrelevance\":[9998, 9997]," 1706 "\"google:verbatimrelevance\":9999}]", 1707 { "a.com", "abc.com", "a.com/a", kNotApplicable }, 1708 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 1709 AutocompleteMatchType::NAVSUGGEST, 1710 AutocompleteMatchType::SEARCH_SUGGEST, 1711 AutocompleteMatchType::NUM_TYPES } }, 1712 }; 1713 1714 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1715 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 1716 net::TestURLFetcher* fetcher = 1717 test_factory_.GetFetcherByID( 1718 SearchProvider::kDefaultProviderURLFetcherID); 1719 ASSERT_TRUE(fetcher); 1720 fetcher->set_response_code(200); 1721 fetcher->SetResponseString(cases[i].json); 1722 fetcher->delegate()->OnURLFetchComplete(fetcher); 1723 RunTillProviderDone(); 1724 1725 size_t j = 0; 1726 const ACMatches& matches = provider_->matches(); 1727 // Ensure that the returned matches equal the expectations. 1728 for (; j < matches.size(); ++j) { 1729 EXPECT_EQ(ASCIIToUTF16(cases[i].match_contents[j]), matches[j].contents); 1730 EXPECT_EQ(cases[i].match_types[j], matches[j].type); 1731 } 1732 // Ensure that no expected matches are missing. 1733 for (; j < ARRAYSIZE_UNSAFE(cases[i].match_contents); ++j) { 1734 EXPECT_EQ(kNotApplicable, cases[i].match_contents[j]); 1735 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES, cases[i].match_types[j]); 1736 } 1737 } 1738} 1739 1740// A basic test that verifies the field trial triggered parsing logic. 1741TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) { 1742 QueryForInput(ASCIIToUTF16("foo"), false, false); 1743 1744 // Make sure the default providers suggest service was queried. 1745 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID( 1746 SearchProvider::kDefaultProviderURLFetcherID); 1747 ASSERT_TRUE(fetcher); 1748 1749 // Tell the SearchProvider the suggest query is done. 1750 fetcher->set_response_code(200); 1751 fetcher->SetResponseString( 1752 "[\"foo\",[\"foo bar\"],[\"\"],[]," 1753 "{\"google:suggesttype\":[\"QUERY\"]," 1754 "\"google:fieldtrialtriggered\":true}]"); 1755 fetcher->delegate()->OnURLFetchComplete(fetcher); 1756 fetcher = NULL; 1757 1758 // Run till the history results complete. 1759 RunTillProviderDone(); 1760 1761 { 1762 // Check for the match and field trial triggered bits. 1763 AutocompleteMatch match; 1764 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match)); 1765 ProvidersInfo providers_info; 1766 provider_->AddProviderInfo(&providers_info); 1767 ASSERT_EQ(1U, providers_info.size()); 1768 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 1769 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size()); 1770 } 1771 { 1772 // Reset the session and check that bits are reset. 1773 provider_->ResetSession(); 1774 ProvidersInfo providers_info; 1775 provider_->AddProviderInfo(&providers_info); 1776 ASSERT_EQ(1U, providers_info.size()); 1777 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size()); 1778 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size()); 1779 } 1780} 1781 1782// Verifies inline autocompletion of navigational results. 1783TEST_F(SearchProviderTest, NavigationInline) { 1784 struct { 1785 const std::string input; 1786 const std::string url; 1787 // Test the expected fill_into_edit, which may drop "http://". 1788 // Some cases do not trim "http://" to match from the start of the scheme. 1789 const std::string fill_into_edit; 1790 const std::string inline_autocompletion; 1791 } cases[] = { 1792 // Do not inline matches that do not contain the input; trim http as needed. 1793 { "x", "http://www.abc.com", 1794 "www.abc.com", std::string() }, 1795 { "https:", "http://www.abc.com", 1796 "www.abc.com", std::string() }, 1797 { "abc.com/", "http://www.abc.com", 1798 "www.abc.com", std::string() }, 1799 { "http://www.abc.com/a", "http://www.abc.com", 1800 "http://www.abc.com", std::string() }, 1801 { "http://www.abc.com", "https://www.abc.com", 1802 "https://www.abc.com", std::string() }, 1803 { "http://abc.com", "ftp://abc.com", 1804 "ftp://abc.com", std::string() }, 1805 { "https://www.abc.com", "http://www.abc.com", 1806 "www.abc.com", std::string() }, 1807 { "ftp://abc.com", "http://abc.com", 1808 "abc.com", std::string() }, 1809 1810 // Do not inline matches with invalid input prefixes; trim http as needed. 1811 { "ttp", "http://www.abc.com", 1812 "www.abc.com", std::string() }, 1813 { "://w", "http://www.abc.com", 1814 "www.abc.com", std::string() }, 1815 { "ww.", "http://www.abc.com", 1816 "www.abc.com", std::string() }, 1817 { ".ab", "http://www.abc.com", 1818 "www.abc.com", std::string() }, 1819 { "bc", "http://www.abc.com", 1820 "www.abc.com", std::string() }, 1821 { ".com", "http://www.abc.com", 1822 "www.abc.com", std::string() }, 1823 1824 // Do not inline matches that omit input domain labels; trim http as needed. 1825 { "www.a", "http://a.com", 1826 "a.com", std::string() }, 1827 { "http://www.a", "http://a.com", 1828 "http://a.com", std::string() }, 1829 { "www.a", "ftp://a.com", 1830 "ftp://a.com", std::string() }, 1831 { "ftp://www.a", "ftp://a.com", 1832 "ftp://a.com", std::string() }, 1833 1834 // Input matching but with nothing to inline will not yield an offset. 1835 { "abc.com", "http://www.abc.com", 1836 "www.abc.com", std::string() }, 1837 { "http://www.abc.com", "http://www.abc.com", 1838 "http://www.abc.com", std::string() }, 1839 1840 // Inline matches when the input is a leading substring of the scheme. 1841 { "h", "http://www.abc.com", 1842 "http://www.abc.com", "ttp://www.abc.com" }, 1843 { "http", "http://www.abc.com", 1844 "http://www.abc.com", "://www.abc.com" }, 1845 1846 // Inline matches when the input is a leading substring of the full URL. 1847 { "http:", "http://www.abc.com", 1848 "http://www.abc.com", "//www.abc.com" }, 1849 { "http://w", "http://www.abc.com", 1850 "http://www.abc.com", "ww.abc.com" }, 1851 { "http://www.", "http://www.abc.com", 1852 "http://www.abc.com", "abc.com" }, 1853 { "http://www.ab", "http://www.abc.com", 1854 "http://www.abc.com", "c.com" }, 1855 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 1856 "http://www.abc.com/path/file.htm?q=x#foo", 1857 "ath/file.htm?q=x#foo" }, 1858 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 1859 "http://abc.com/path/file.htm?q=x#foo", 1860 "ath/file.htm?q=x#foo"}, 1861 1862 // Inline matches with valid URLPrefixes; only trim "http://". 1863 { "w", "http://www.abc.com", 1864 "www.abc.com", "ww.abc.com" }, 1865 { "www.a", "http://www.abc.com", 1866 "www.abc.com", "bc.com" }, 1867 { "abc", "http://www.abc.com", 1868 "www.abc.com", ".com" }, 1869 { "abc.c", "http://www.abc.com", 1870 "www.abc.com", "om" }, 1871 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo", 1872 "www.abc.com/path/file.htm?q=x#foo", 1873 "ath/file.htm?q=x#foo" }, 1874 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo", 1875 "abc.com/path/file.htm?q=x#foo", 1876 "ath/file.htm?q=x#foo" }, 1877 1878 // Inline matches using the maximal URLPrefix components. 1879 { "h", "http://help.com", 1880 "help.com", "elp.com" }, 1881 { "http", "http://http.com", 1882 "http.com", ".com" }, 1883 { "h", "http://www.help.com", 1884 "www.help.com", "elp.com" }, 1885 { "http", "http://www.http.com", 1886 "www.http.com", ".com" }, 1887 { "w", "http://www.www.com", 1888 "www.www.com", "ww.com" }, 1889 1890 // Test similar behavior for the ftp and https schemes. 1891 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1892 "ftp://www.abc.com/path/file.htm?q=x#foo", 1893 "c.com/path/file.htm?q=x#foo" }, 1894 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1895 "ftp://www.abc.com/path/file.htm?q=x#foo", 1896 "c.com/path/file.htm?q=x#foo" }, 1897 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo", 1898 "ftp://www.abc.com/path/file.htm?q=x#foo", 1899 "c.com/path/file.htm?q=x#foo" }, 1900 { "ab", "ftp://abc.com/path/file.htm?q=x#foo", 1901 "ftp://abc.com/path/file.htm?q=x#foo", 1902 "c.com/path/file.htm?q=x#foo" }, 1903 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 1904 "https://www.abc.com/path/file.htm?q=x#foo", 1905 "c.com/path/file.htm?q=x#foo" }, 1906 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo", 1907 "https://www.abc.com/path/file.htm?q=x#foo", 1908 "c.com/path/file.htm?q=x#foo" }, 1909 { "ab", "https://www.abc.com/path/file.htm?q=x#foo", 1910 "https://www.abc.com/path/file.htm?q=x#foo", 1911 "c.com/path/file.htm?q=x#foo" }, 1912 { "ab", "https://abc.com/path/file.htm?q=x#foo", 1913 "https://abc.com/path/file.htm?q=x#foo", 1914 "c.com/path/file.htm?q=x#foo"}, 1915 1916 // Forced query input should inline and retain the "?" prefix. 1917 { "?http://www.ab", "http://www.abc.com", 1918 "?http://www.abc.com", "c.com" }, 1919 { "?www.ab", "http://www.abc.com", 1920 "?www.abc.com", "c.com" }, 1921 { "?ab", "http://www.abc.com", 1922 "?www.abc.com", "c.com" }, 1923 }; 1924 1925 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 1926 QueryForInput(ASCIIToUTF16(cases[i].input), false, false); 1927 AutocompleteMatch match( 1928 provider_->NavigationToMatch(SearchProvider::NavigationResult( 1929 *provider_.get(), GURL(cases[i].url), string16(), false, 0, 1930 false))); 1931 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion), 1932 match.inline_autocompletion); 1933 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit); 1934 } 1935} 1936 1937// Verifies that "http://" is not trimmed for input that is a leading substring. 1938TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) { 1939 const string16 input(ASCIIToUTF16("ht")); 1940 const string16 url(ASCIIToUTF16("http://a.com")); 1941 const SearchProvider::NavigationResult result( 1942 *provider_.get(), GURL(url), string16(), false, 0, false); 1943 1944 // Check the offset and strings when inline autocompletion is allowed. 1945 QueryForInput(input, false, false); 1946 AutocompleteMatch match_inline(provider_->NavigationToMatch(result)); 1947 EXPECT_EQ(url, match_inline.fill_into_edit); 1948 EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion); 1949 EXPECT_EQ(url, match_inline.contents); 1950 1951 // Check the same offset and strings when inline autocompletion is prevented. 1952 QueryForInput(input, true, false); 1953 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result)); 1954 EXPECT_TRUE(match_prevent.inline_autocompletion.empty()); 1955 EXPECT_EQ(url, match_prevent.fill_into_edit); 1956 EXPECT_EQ(url, match_prevent.contents); 1957} 1958 1959// Verifies that input "w" marks a more significant domain label than "www.". 1960TEST_F(SearchProviderTest, NavigationInlineDomainClassify) { 1961 QueryForInput(ASCIIToUTF16("w"), false, false); 1962 AutocompleteMatch match( 1963 provider_->NavigationToMatch(SearchProvider::NavigationResult( 1964 *provider_.get(), GURL("http://www.wow.com"), string16(), false, 0, 1965 false))); 1966 EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion); 1967 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit); 1968 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents); 1969 1970 // Ensure that the match for input "w" is marked on "wow" and not "www". 1971 ASSERT_EQ(3U, match.contents_class.size()); 1972 EXPECT_EQ(0U, match.contents_class[0].offset); 1973 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 1974 match.contents_class[0].style); 1975 EXPECT_EQ(4U, match.contents_class[1].offset); 1976 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL | 1977 AutocompleteMatch::ACMatchClassification::MATCH, 1978 match.contents_class[1].style); 1979 EXPECT_EQ(5U, match.contents_class[2].offset); 1980 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL, 1981 match.contents_class[2].style); 1982} 1983 1984TEST_F(SearchProviderTest, RemoveStaleResultsTest) { 1985 // TODO(mpearson): Consider expanding this test to explicitly cover 1986 // testing staleness for keyword results. 1987 struct { 1988 const std::string omnibox_input; 1989 const int verbatim_relevance; 1990 // These cached suggestions should already be sorted. 1991 // The particular number 5 as the length of the array is 1992 // unimportant; it's merely enough cached results to fully test 1993 // the functioning of RemoveAllStaleResults(). 1994 struct { 1995 const std::string suggestion; 1996 const bool is_navigation_result; 1997 const int relevance; 1998 // |expect_match| is true if this result should survive 1999 // RemoveAllStaleResults() filtering against |omnibox_input| below. 2000 const bool expect_match; 2001 } results[5]; 2002 } cases[] = { 2003 // Simple case: multiple query suggestions and no navsuggestions. 2004 // All query suggestions score less than search-what-you-typed and 2005 // thus none should be filtered because none will appear first. 2006 { "x", 1300, 2007 { { "food", false, 1299, true }, 2008 { "foobar", false, 1298, true }, 2009 { "crazy", false, 1297, true }, 2010 { "friend", false, 1296, true }, 2011 { kNotApplicable, false, 0, false } } }, 2012 2013 // Similarly simple cases, but the query suggestion appears first. 2014 { "f", 1200, 2015 { { "food", false, 1299, true }, 2016 { "foobar", false, 1298, true }, 2017 { "crazy", false, 1297, true }, 2018 { "friend", false, 1296, true }, 2019 { kNotApplicable, false, 0, false } } }, 2020 { "c", 1200, 2021 { { "food", false, 1299, false }, 2022 { "foobar", false, 1298, false }, 2023 { "crazy", false, 1297, true }, 2024 { "friend", false, 1296, true }, 2025 { kNotApplicable, false, 0, false } } }, 2026 { "x", 1200, 2027 { { "food", false, 1299, false }, 2028 { "foobar", false, 1298, false }, 2029 { "crazy", false, 1297, false }, 2030 { "friend", false, 1296, false }, 2031 { kNotApplicable, false, 0, false } } }, 2032 2033 // The same sort of cases, just using a mix of queries and navsuggestions. 2034 { "x", 1300, 2035 { { "http://food.com/", true, 1299, true }, 2036 { "foobar", false, 1298, true }, 2037 { "http://crazy.com/", true, 1297, true }, 2038 { "friend", false, 1296, true }, 2039 { "http://friend.com/", true, 1295, true } } }, 2040 { "f", 1200, 2041 { { "http://food.com/", true, 1299, true }, 2042 { "foobar", false, 1298, true }, 2043 { "http://crazy.com/", true, 1297, true }, 2044 { "friend", false, 1296, true }, 2045 { "http://friend.com/", true, 1295, true } } }, 2046 { "c", 1200, 2047 { { "http://food.com/", true, 1299, false }, 2048 { "foobar", false, 1298, false }, 2049 { "http://crazy.com/", true, 1297, true }, 2050 { "friend", false, 1296, true }, 2051 { "http://friend.com/", true, 1295, true } } }, 2052 { "x", 1200, 2053 { { "http://food.com/", true, 1299, false }, 2054 { "foobar", false, 1298, false }, 2055 { "http://crazy.com/", true, 1297, false }, 2056 { "friend", false, 1296, false }, 2057 { "http://friend.com/", true, 1295, false } } }, 2058 2059 // Run the three tests immediately above again, just with verbatim 2060 // suppressed. Note that in the last case, all results are filtered. 2061 // Because verbatim is also suppressed, SearchProvider will realize 2062 // in UpdateMatches() that it needs to restore verbatim to fulfill 2063 // its constraints. This restoration does not happen in 2064 // RemoveAllStaleResults() and hence is not tested here. This restoration 2065 // is tested in the DefaultFetcherSuggestRelevance test. 2066 { "f", 0, 2067 { { "http://food.com/", true, 1299, true }, 2068 { "foobar", false, 1298, true }, 2069 { "http://crazy.com/", true, 1297, true }, 2070 { "friend", false, 1296, true }, 2071 { "http://friend.com/", true, 1295, true } } }, 2072 { "c", 0, 2073 { { "http://food.com/", true, 1299, false }, 2074 { "foobar", false, 1298, false }, 2075 { "http://crazy.com/", true, 1297, true }, 2076 { "friend", false, 1296, true }, 2077 { "http://friend.com/", true, 1295, true } } }, 2078 { "x", 0, 2079 { { "http://food.com/", true, 1299, false }, 2080 { "foobar", false, 1298, false }, 2081 { "http://crazy.com/", true, 1297, false }, 2082 { "friend", false, 1296, false }, 2083 { "http://friend.com/", true, 1295, false } } }, 2084 2085 // The same sort of tests again, just with verbatim with a score 2086 // that would place it in between other suggestions. 2087 { "f", 1290, 2088 { { "http://food.com/", true, 1299, true }, 2089 { "foobar", false, 1288, true }, 2090 { "http://crazy.com/", true, 1277, true }, 2091 { "friend", false, 1266, true }, 2092 { "http://friend.com/", true, 1255, true } } }, 2093 { "c", 1290, 2094 { { "http://food.com/", true, 1299, false }, 2095 { "foobar", false, 1288, true }, 2096 { "http://crazy.com/", true, 1277, true }, 2097 { "friend", false, 1266, true }, 2098 { "http://friend.com/", true, 1255, true } } }, 2099 { "c", 1270, 2100 { { "http://food.com/", true, 1299, false }, 2101 { "foobar", false, 1288, false }, 2102 { "http://crazy.com/", true, 1277, true }, 2103 { "friend", false, 1266, true }, 2104 { "http://friend.com/", true, 1255, true } } }, 2105 { "x", 1280, 2106 { { "http://food.com/", true, 1299, false }, 2107 { "foobar", false, 1288, false }, 2108 { "http://crazy.com/", true, 1277, true }, 2109 { "friend", false, 1266, true }, 2110 { "http://friend.com/", true, 1255, true } } }, 2111 }; 2112 2113 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 2114 // Initialize cached results for this test case. 2115 provider_->default_results_.verbatim_relevance = 2116 cases[i].verbatim_relevance; 2117 provider_->default_results_.navigation_results.clear(); 2118 provider_->default_results_.suggest_results.clear(); 2119 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) { 2120 const std::string& suggestion = cases[i].results[j].suggestion; 2121 if (suggestion == kNotApplicable) 2122 break; 2123 if (cases[i].results[j].is_navigation_result) { 2124 provider_->default_results_.navigation_results.push_back( 2125 SearchProvider::NavigationResult( 2126 *provider_.get(), GURL(suggestion), string16(), false, 2127 cases[i].results[j].relevance, false)); 2128 } else { 2129 provider_->default_results_.suggest_results.push_back( 2130 SearchProvider::SuggestResult(ASCIIToUTF16(suggestion), false, 2131 cases[i].results[j].relevance, 2132 false)); 2133 } 2134 } 2135 2136 provider_->input_ = AutocompleteInput( 2137 ASCIIToUTF16(cases[i].omnibox_input), string16::npos, string16(), 2138 GURL(), AutocompleteInput::INVALID_SPEC, false, false, true, 2139 AutocompleteInput::ALL_MATCHES); 2140 provider_->RemoveAllStaleResults(); 2141 2142 // Check cached results. 2143 SearchProvider::SuggestResults::const_iterator sug_it = 2144 provider_->default_results_.suggest_results.begin(); 2145 const SearchProvider::SuggestResults::const_iterator sug_end = 2146 provider_->default_results_.suggest_results.end(); 2147 SearchProvider::NavigationResults::const_iterator nav_it = 2148 provider_->default_results_.navigation_results.begin(); 2149 const SearchProvider::NavigationResults::const_iterator nav_end = 2150 provider_->default_results_.navigation_results.end(); 2151 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) { 2152 const std::string& suggestion = cases[i].results[j].suggestion; 2153 if (suggestion == kNotApplicable) 2154 continue; 2155 if (!cases[i].results[j].expect_match) 2156 continue; 2157 if (cases[i].results[j].is_navigation_result) { 2158 ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion; 2159 EXPECT_EQ(suggestion, nav_it->url().spec()); 2160 ++nav_it; 2161 } else { 2162 ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion; 2163 EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion()); 2164 ++sug_it; 2165 } 2166 } 2167 EXPECT_EQ(sug_end, sug_it); 2168 EXPECT_EQ(nav_end, nav_it); 2169 } 2170} 2171