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