1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "components/omnibox/autocomplete_provider.h" 6 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/strings/string16.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_util.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "chrome/browser/autocomplete/autocomplete_controller.h" 16 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h" 17 #include "chrome/browser/chrome_notification_types.h" 18 #include "chrome/browser/search_engines/template_url_service_factory.h" 19 #include "chrome/test/base/testing_browser_process.h" 20 #include "chrome/test/base/testing_profile.h" 21 #include "components/metrics/proto/omnibox_event.pb.h" 22 #include "components/omnibox/autocomplete_input.h" 23 #include "components/omnibox/autocomplete_match.h" 24 #include "components/omnibox/autocomplete_provider_listener.h" 25 #include "components/omnibox/keyword_provider.h" 26 #include "components/omnibox/search_provider.h" 27 #include "components/search_engines/search_engines_switches.h" 28 #include "components/search_engines/template_url.h" 29 #include "components/search_engines/template_url_service.h" 30 #include "content/public/browser/notification_observer.h" 31 #include "content/public/browser/notification_registrar.h" 32 #include "content/public/browser/notification_source.h" 33 #include "testing/gtest/include/gtest/gtest.h" 34 35 static std::ostream& operator<<(std::ostream& os, 36 const AutocompleteResult::const_iterator& it) { 37 return os << static_cast<const AutocompleteMatch*>(&(*it)); 38 } 39 40 namespace { 41 const size_t kResultsPerProvider = 3; 42 const char kTestTemplateURLKeyword[] = "t"; 43 } 44 45 // Autocomplete provider that provides known results. Note that this is 46 // refcounted so that it can also be a task on the message loop. 47 class TestProvider : public AutocompleteProvider { 48 public: 49 TestProvider(int relevance, const base::string16& prefix, 50 Profile* profile, 51 const base::string16 match_keyword) 52 : AutocompleteProvider(AutocompleteProvider::TYPE_SEARCH), 53 listener_(NULL), 54 profile_(profile), 55 relevance_(relevance), 56 prefix_(prefix), 57 match_keyword_(match_keyword) { 58 } 59 60 virtual void Start(const AutocompleteInput& input, 61 bool minimal_changes) OVERRIDE; 62 63 void set_listener(AutocompleteProviderListener* listener) { 64 listener_ = listener; 65 } 66 67 private: 68 virtual ~TestProvider() {} 69 70 void Run(); 71 72 void AddResults(int start_at, int num); 73 void AddResultsWithSearchTermsArgs( 74 int start_at, 75 int num, 76 AutocompleteMatch::Type type, 77 const TemplateURLRef::SearchTermsArgs& search_terms_args); 78 79 AutocompleteProviderListener* listener_; 80 Profile* profile_; 81 int relevance_; 82 const base::string16 prefix_; 83 const base::string16 match_keyword_; 84 }; 85 86 void TestProvider::Start(const AutocompleteInput& input, 87 bool minimal_changes) { 88 if (minimal_changes) 89 return; 90 91 matches_.clear(); 92 93 // Generate 4 results synchronously, the rest later. 94 AddResults(0, 1); 95 AddResultsWithSearchTermsArgs( 96 1, 1, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 97 TemplateURLRef::SearchTermsArgs(base::ASCIIToUTF16("echo"))); 98 AddResultsWithSearchTermsArgs( 99 2, 1, AutocompleteMatchType::NAVSUGGEST, 100 TemplateURLRef::SearchTermsArgs(base::ASCIIToUTF16("nav"))); 101 AddResultsWithSearchTermsArgs( 102 3, 1, AutocompleteMatchType::SEARCH_SUGGEST, 103 TemplateURLRef::SearchTermsArgs(base::ASCIIToUTF16("query"))); 104 105 if (input.want_asynchronous_matches()) { 106 done_ = false; 107 base::MessageLoop::current()->PostTask( 108 FROM_HERE, base::Bind(&TestProvider::Run, this)); 109 } 110 } 111 112 void TestProvider::Run() { 113 DCHECK_GT(kResultsPerProvider, 0U); 114 AddResults(1, kResultsPerProvider); 115 done_ = true; 116 DCHECK(listener_); 117 listener_->OnProviderUpdate(true); 118 } 119 120 void TestProvider::AddResults(int start_at, int num) { 121 AddResultsWithSearchTermsArgs(start_at, 122 num, 123 AutocompleteMatchType::URL_WHAT_YOU_TYPED, 124 TemplateURLRef::SearchTermsArgs( 125 base::string16())); 126 } 127 128 void TestProvider::AddResultsWithSearchTermsArgs( 129 int start_at, 130 int num, 131 AutocompleteMatch::Type type, 132 const TemplateURLRef::SearchTermsArgs& search_terms_args) { 133 for (int i = start_at; i < num; i++) { 134 AutocompleteMatch match(this, relevance_ - i, false, type); 135 136 match.fill_into_edit = prefix_ + base::UTF8ToUTF16(base::IntToString(i)); 137 match.destination_url = GURL(base::UTF16ToUTF8(match.fill_into_edit)); 138 match.allowed_to_be_default_match = true; 139 140 match.contents = match.fill_into_edit; 141 match.contents_class.push_back( 142 ACMatchClassification(0, ACMatchClassification::NONE)); 143 match.description = match.fill_into_edit; 144 match.description_class.push_back( 145 ACMatchClassification(0, ACMatchClassification::NONE)); 146 match.search_terms_args.reset( 147 new TemplateURLRef::SearchTermsArgs(search_terms_args)); 148 if (!match_keyword_.empty()) { 149 match.keyword = match_keyword_; 150 TemplateURLService* service = 151 TemplateURLServiceFactory::GetForProfile(profile_); 152 ASSERT_TRUE(match.GetTemplateURL(service, false) != NULL); 153 } 154 155 matches_.push_back(match); 156 } 157 } 158 159 class AutocompleteProviderTest : public testing::Test, 160 public content::NotificationObserver { 161 protected: 162 struct KeywordTestData { 163 const base::string16 fill_into_edit; 164 const base::string16 keyword; 165 const base::string16 expected_associated_keyword; 166 }; 167 168 struct AssistedQueryStatsTestData { 169 const AutocompleteMatch::Type match_type; 170 const std::string expected_aqs; 171 }; 172 173 protected: 174 // Registers a test TemplateURL under the given keyword. 175 void RegisterTemplateURL(const base::string16 keyword, 176 const std::string& template_url); 177 178 // Resets |controller_| with two TestProviders. |provider1_ptr| and 179 // |provider2_ptr| are updated to point to the new providers if non-NULL. 180 void ResetControllerWithTestProviders(bool same_destinations, 181 TestProvider** provider1_ptr, 182 TestProvider** provider2_ptr); 183 184 // Runs a query on the input "a", and makes sure both providers' input is 185 // properly collected. 186 void RunTest(); 187 188 // Constructs an AutocompleteResult from |match_data|, sets the |controller_| 189 // to pretend it was running against input |input|, calls the |controller_|'s 190 // UpdateAssociatedKeywords, and checks that the matches have associated 191 // keywords as expected. 192 void RunKeywordTest(const base::string16& input, 193 const KeywordTestData* match_data, 194 size_t size); 195 196 void RunAssistedQueryStatsTest( 197 const AssistedQueryStatsTestData* aqs_test_data, 198 size_t size); 199 200 void RunQuery(const base::string16 query); 201 202 void ResetControllerWithKeywordAndSearchProviders(); 203 void ResetControllerWithKeywordProvider(); 204 void RunExactKeymatchTest(bool allow_exact_keyword_match); 205 206 void CopyResults(); 207 208 // Returns match.destination_url as it would be set by 209 // AutocompleteController::UpdateMatchDestinationURL(). 210 GURL GetDestinationURL(AutocompleteMatch match, 211 base::TimeDelta query_formulation_time) const; 212 213 AutocompleteResult result_; 214 scoped_ptr<AutocompleteController> controller_; 215 216 private: 217 // content::NotificationObserver: 218 virtual void Observe(int type, 219 const content::NotificationSource& source, 220 const content::NotificationDetails& details) OVERRIDE; 221 222 base::MessageLoopForUI message_loop_; 223 content::NotificationRegistrar registrar_; 224 TestingProfile profile_; 225 }; 226 227 void AutocompleteProviderTest::RegisterTemplateURL( 228 const base::string16 keyword, 229 const std::string& template_url) { 230 if (TemplateURLServiceFactory::GetForProfile(&profile_) == NULL) { 231 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 232 &profile_, &TemplateURLServiceFactory::BuildInstanceFor); 233 } 234 TemplateURLData data; 235 data.SetURL(template_url); 236 data.SetKeyword(keyword); 237 TemplateURL* default_t_url = new TemplateURL(data); 238 TemplateURLService* turl_model = 239 TemplateURLServiceFactory::GetForProfile(&profile_); 240 turl_model->Add(default_t_url); 241 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url); 242 turl_model->Load(); 243 TemplateURLID default_provider_id = default_t_url->id(); 244 ASSERT_NE(0, default_provider_id); 245 } 246 247 void AutocompleteProviderTest::ResetControllerWithTestProviders( 248 bool same_destinations, 249 TestProvider** provider1_ptr, 250 TestProvider** provider2_ptr) { 251 // TODO: Move it outside this method, after refactoring the existing 252 // unit tests. Specifically: 253 // (1) Make sure that AutocompleteMatch.keyword is set iff there is 254 // a corresponding call to RegisterTemplateURL; otherwise the 255 // controller flow will crash; this practically means that 256 // RunTests/ResetControllerXXX/RegisterTemplateURL should 257 // be coordinated with each other. 258 // (2) Inject test arguments rather than rely on the hardcoded values, e.g. 259 // don't rely on kResultsPerProvided and default relevance ordering 260 // (B > A). 261 RegisterTemplateURL(base::ASCIIToUTF16(kTestTemplateURLKeyword), 262 "http://aqs/{searchTerms}/{google:assistedQueryStats}"); 263 264 AutocompleteController::Providers providers; 265 266 // Construct two new providers, with either the same or different prefixes. 267 TestProvider* provider1 = new TestProvider( 268 kResultsPerProvider, 269 base::ASCIIToUTF16("http://a"), 270 &profile_, 271 base::ASCIIToUTF16(kTestTemplateURLKeyword)); 272 providers.push_back(provider1); 273 274 TestProvider* provider2 = new TestProvider( 275 kResultsPerProvider * 2, 276 same_destinations ? base::ASCIIToUTF16("http://a") 277 : base::ASCIIToUTF16("http://b"), 278 &profile_, 279 base::string16()); 280 providers.push_back(provider2); 281 282 // Reset the controller to contain our new providers. 283 controller_.reset(new AutocompleteController( 284 &profile_, TemplateURLServiceFactory::GetForProfile(&profile_), NULL, 0)); 285 // We're going to swap the providers vector, but the old vector should be 286 // empty so no elements need to be freed at this point. 287 EXPECT_TRUE(controller_->providers_.empty()); 288 controller_->providers_.swap(providers); 289 provider1->set_listener(controller_.get()); 290 provider2->set_listener(controller_.get()); 291 292 // The providers don't complete synchronously, so listen for "result updated" 293 // notifications. 294 registrar_.Add(this, 295 chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY, 296 content::Source<AutocompleteController>(controller_.get())); 297 298 if (provider1_ptr) 299 *provider1_ptr = provider1; 300 if (provider2_ptr) 301 *provider2_ptr = provider2; 302 } 303 304 void AutocompleteProviderTest:: 305 ResetControllerWithKeywordAndSearchProviders() { 306 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 307 &profile_, &TemplateURLServiceFactory::BuildInstanceFor); 308 309 // Reset the default TemplateURL. 310 TemplateURLData data; 311 data.SetURL("http://defaultturl/{searchTerms}"); 312 TemplateURL* default_t_url = new TemplateURL(data); 313 TemplateURLService* turl_model = 314 TemplateURLServiceFactory::GetForProfile(&profile_); 315 turl_model->Add(default_t_url); 316 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url); 317 TemplateURLID default_provider_id = default_t_url->id(); 318 ASSERT_NE(0, default_provider_id); 319 320 // Create another TemplateURL for KeywordProvider. 321 TemplateURLData data2; 322 data2.short_name = base::ASCIIToUTF16("k"); 323 data2.SetKeyword(base::ASCIIToUTF16("k")); 324 data2.SetURL("http://keyword/{searchTerms}"); 325 TemplateURL* keyword_t_url = new TemplateURL(data2); 326 turl_model->Add(keyword_t_url); 327 ASSERT_NE(0, keyword_t_url->id()); 328 329 controller_.reset(new AutocompleteController( 330 &profile_, TemplateURLServiceFactory::GetForProfile(&profile_), NULL, 331 AutocompleteProvider::TYPE_KEYWORD | AutocompleteProvider::TYPE_SEARCH)); 332 } 333 334 void AutocompleteProviderTest::ResetControllerWithKeywordProvider() { 335 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 336 &profile_, &TemplateURLServiceFactory::BuildInstanceFor); 337 338 TemplateURLService* turl_model = 339 TemplateURLServiceFactory::GetForProfile(&profile_); 340 341 // Create a TemplateURL for KeywordProvider. 342 TemplateURLData data; 343 data.short_name = base::ASCIIToUTF16("foo.com"); 344 data.SetKeyword(base::ASCIIToUTF16("foo.com")); 345 data.SetURL("http://foo.com/{searchTerms}"); 346 TemplateURL* keyword_t_url = new TemplateURL(data); 347 turl_model->Add(keyword_t_url); 348 ASSERT_NE(0, keyword_t_url->id()); 349 350 // Make a TemplateURL for KeywordProvider that a shorter version of the 351 // first. 352 data.short_name = base::ASCIIToUTF16("f"); 353 data.SetKeyword(base::ASCIIToUTF16("f")); 354 data.SetURL("http://f.com/{searchTerms}"); 355 keyword_t_url = new TemplateURL(data); 356 turl_model->Add(keyword_t_url); 357 ASSERT_NE(0, keyword_t_url->id()); 358 359 // Create another TemplateURL for KeywordProvider. 360 data.short_name = base::ASCIIToUTF16("bar.com"); 361 data.SetKeyword(base::ASCIIToUTF16("bar.com")); 362 data.SetURL("http://bar.com/{searchTerms}"); 363 keyword_t_url = new TemplateURL(data); 364 turl_model->Add(keyword_t_url); 365 ASSERT_NE(0, keyword_t_url->id()); 366 367 controller_.reset(new AutocompleteController( 368 &profile_, TemplateURLServiceFactory::GetForProfile(&profile_), NULL, 369 AutocompleteProvider::TYPE_KEYWORD)); 370 } 371 372 void AutocompleteProviderTest::RunTest() { 373 RunQuery(base::ASCIIToUTF16("a")); 374 } 375 376 void AutocompleteProviderTest::RunKeywordTest(const base::string16& input, 377 const KeywordTestData* match_data, 378 size_t size) { 379 ACMatches matches; 380 for (size_t i = 0; i < size; ++i) { 381 AutocompleteMatch match; 382 match.relevance = 1000; // Arbitrary non-zero value. 383 match.allowed_to_be_default_match = true; 384 match.fill_into_edit = match_data[i].fill_into_edit; 385 match.transition = ui::PAGE_TRANSITION_KEYWORD; 386 match.keyword = match_data[i].keyword; 387 matches.push_back(match); 388 } 389 390 AutocompleteResult result; 391 result.AppendMatches(matches); 392 controller_->input_ = AutocompleteInput( 393 input, base::string16::npos, base::string16(), GURL(), 394 metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS, 395 false, true, true, true, ChromeAutocompleteSchemeClassifier(&profile_)); 396 controller_->UpdateAssociatedKeywords(&result); 397 398 for (size_t j = 0; j < result.size(); ++j) { 399 EXPECT_EQ(match_data[j].expected_associated_keyword, 400 result.match_at(j)->associated_keyword.get() ? 401 result.match_at(j)->associated_keyword->keyword : 402 base::string16()); 403 } 404 } 405 406 void AutocompleteProviderTest::RunAssistedQueryStatsTest( 407 const AssistedQueryStatsTestData* aqs_test_data, 408 size_t size) { 409 // Prepare input. 410 const size_t kMaxRelevance = 1000; 411 ACMatches matches; 412 for (size_t i = 0; i < size; ++i) { 413 AutocompleteMatch match(NULL, kMaxRelevance - i, false, 414 aqs_test_data[i].match_type); 415 match.allowed_to_be_default_match = true; 416 match.keyword = base::ASCIIToUTF16(kTestTemplateURLKeyword); 417 match.search_terms_args.reset( 418 new TemplateURLRef::SearchTermsArgs(base::string16())); 419 matches.push_back(match); 420 } 421 result_.Reset(); 422 result_.AppendMatches(matches); 423 424 // Update AQS. 425 controller_->UpdateAssistedQueryStats(&result_); 426 427 // Verify data. 428 for (size_t i = 0; i < size; ++i) { 429 EXPECT_EQ(aqs_test_data[i].expected_aqs, 430 result_.match_at(i)->search_terms_args->assisted_query_stats); 431 } 432 } 433 434 void AutocompleteProviderTest::RunQuery(const base::string16 query) { 435 result_.Reset(); 436 controller_->Start(AutocompleteInput( 437 query, base::string16::npos, base::string16(), GURL(), 438 metrics::OmniboxEventProto::INVALID_SPEC, true, false, true, true, 439 ChromeAutocompleteSchemeClassifier(&profile_))); 440 441 if (!controller_->done()) 442 // The message loop will terminate when all autocomplete input has been 443 // collected. 444 base::MessageLoop::current()->Run(); 445 } 446 447 void AutocompleteProviderTest::RunExactKeymatchTest( 448 bool allow_exact_keyword_match) { 449 // Send the controller input which exactly matches the keyword provider we 450 // created in ResetControllerWithKeywordAndSearchProviders(). The default 451 // match should thus be a search-other-engine match iff 452 // |allow_exact_keyword_match| is true. Regardless, the match should 453 // be from SearchProvider. (It provides all verbatim search matches, 454 // keyword or not.) 455 controller_->Start(AutocompleteInput( 456 base::ASCIIToUTF16("k test"), base::string16::npos, base::string16(), 457 GURL(), metrics::OmniboxEventProto::INVALID_SPEC, true, false, 458 allow_exact_keyword_match, false, 459 ChromeAutocompleteSchemeClassifier(&profile_))); 460 EXPECT_TRUE(controller_->done()); 461 EXPECT_EQ(AutocompleteProvider::TYPE_SEARCH, 462 controller_->result().default_match()->provider->type()); 463 EXPECT_EQ(allow_exact_keyword_match ? 464 AutocompleteMatchType::SEARCH_OTHER_ENGINE : 465 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 466 controller_->result().default_match()->type); 467 } 468 469 void AutocompleteProviderTest::CopyResults() { 470 result_.CopyFrom(controller_->result()); 471 } 472 473 GURL AutocompleteProviderTest::GetDestinationURL( 474 AutocompleteMatch match, 475 base::TimeDelta query_formulation_time) const { 476 controller_->UpdateMatchDestinationURLWithQueryFormulationTime( 477 query_formulation_time, &match); 478 return match.destination_url; 479 } 480 481 void AutocompleteProviderTest::Observe( 482 int type, 483 const content::NotificationSource& source, 484 const content::NotificationDetails& details) { 485 if (controller_->done()) { 486 CopyResults(); 487 base::MessageLoop::current()->Quit(); 488 } 489 } 490 491 // Tests that the default selection is set properly when updating results. 492 TEST_F(AutocompleteProviderTest, Query) { 493 TestProvider* provider1 = NULL; 494 TestProvider* provider2 = NULL; 495 ResetControllerWithTestProviders(false, &provider1, &provider2); 496 RunTest(); 497 498 // Make sure the default match gets set to the highest relevance match. The 499 // highest relevance matches should come from the second provider. 500 EXPECT_EQ(kResultsPerProvider * 2, result_.size()); 501 ASSERT_NE(result_.end(), result_.default_match()); 502 EXPECT_EQ(provider2, result_.default_match()->provider); 503 } 504 505 // Tests assisted query stats. 506 TEST_F(AutocompleteProviderTest, AssistedQueryStats) { 507 ResetControllerWithTestProviders(false, NULL, NULL); 508 RunTest(); 509 510 ASSERT_EQ(kResultsPerProvider * 2, result_.size()); 511 512 // Now, check the results from the second provider, as they should not have 513 // assisted query stats set. 514 for (size_t i = 0; i < kResultsPerProvider; ++i) { 515 EXPECT_TRUE( 516 result_.match_at(i)->search_terms_args->assisted_query_stats.empty()); 517 } 518 // The first provider has a test keyword, so AQS should be non-empty. 519 for (size_t i = kResultsPerProvider; i < kResultsPerProvider * 2; ++i) { 520 EXPECT_FALSE( 521 result_.match_at(i)->search_terms_args->assisted_query_stats.empty()); 522 } 523 } 524 525 TEST_F(AutocompleteProviderTest, RemoveDuplicates) { 526 TestProvider* provider1 = NULL; 527 TestProvider* provider2 = NULL; 528 ResetControllerWithTestProviders(true, &provider1, &provider2); 529 RunTest(); 530 531 // Make sure all the first provider's results were eliminated by the second 532 // provider's. 533 EXPECT_EQ(kResultsPerProvider, result_.size()); 534 for (AutocompleteResult::const_iterator i(result_.begin()); 535 i != result_.end(); ++i) 536 EXPECT_EQ(provider2, i->provider); 537 } 538 539 TEST_F(AutocompleteProviderTest, AllowExactKeywordMatch) { 540 ResetControllerWithKeywordAndSearchProviders(); 541 RunExactKeymatchTest(true); 542 RunExactKeymatchTest(false); 543 } 544 545 // Ensures matches from (only) the default search provider respect any extra 546 // query params set on the command line. 547 TEST_F(AutocompleteProviderTest, ExtraQueryParams) { 548 ResetControllerWithKeywordAndSearchProviders(); 549 CommandLine::ForCurrentProcess()->AppendSwitchASCII( 550 switches::kExtraSearchQueryParams, "a=b"); 551 RunExactKeymatchTest(true); 552 CopyResults(); 553 ASSERT_EQ(2U, result_.size()); 554 EXPECT_EQ("http://keyword/test", 555 result_.match_at(0)->destination_url.possibly_invalid_spec()); 556 EXPECT_EQ("http://defaultturl/k%20test?a=b", 557 result_.match_at(1)->destination_url.possibly_invalid_spec()); 558 } 559 560 // Test that redundant associated keywords are removed. 561 TEST_F(AutocompleteProviderTest, RedundantKeywordsIgnoredInResult) { 562 ResetControllerWithKeywordProvider(); 563 564 { 565 KeywordTestData duplicate_url[] = { 566 { base::ASCIIToUTF16("fo"), base::string16(), base::string16() }, 567 { base::ASCIIToUTF16("foo.com"), base::string16(), 568 base::ASCIIToUTF16("foo.com") }, 569 { base::ASCIIToUTF16("foo.com"), base::string16(), base::string16() } 570 }; 571 572 SCOPED_TRACE("Duplicate url"); 573 RunKeywordTest(base::ASCIIToUTF16("fo"), duplicate_url, 574 ARRAYSIZE_UNSAFE(duplicate_url)); 575 } 576 577 { 578 KeywordTestData keyword_match[] = { 579 { base::ASCIIToUTF16("foo.com"), base::ASCIIToUTF16("foo.com"), 580 base::string16() }, 581 { base::ASCIIToUTF16("foo.com"), base::string16(), base::string16() } 582 }; 583 584 SCOPED_TRACE("Duplicate url with keyword match"); 585 RunKeywordTest(base::ASCIIToUTF16("fo"), keyword_match, 586 ARRAYSIZE_UNSAFE(keyword_match)); 587 } 588 589 { 590 KeywordTestData multiple_keyword[] = { 591 { base::ASCIIToUTF16("fo"), base::string16(), base::string16() }, 592 { base::ASCIIToUTF16("foo.com"), base::string16(), 593 base::ASCIIToUTF16("foo.com") }, 594 { base::ASCIIToUTF16("foo.com"), base::string16(), base::string16() }, 595 { base::ASCIIToUTF16("bar.com"), base::string16(), 596 base::ASCIIToUTF16("bar.com") }, 597 }; 598 599 SCOPED_TRACE("Duplicate url with multiple keywords"); 600 RunKeywordTest(base::ASCIIToUTF16("fo"), multiple_keyword, 601 ARRAYSIZE_UNSAFE(multiple_keyword)); 602 } 603 } 604 605 // Test that exact match keywords trump keywords associated with 606 // the match. 607 TEST_F(AutocompleteProviderTest, ExactMatchKeywords) { 608 ResetControllerWithKeywordProvider(); 609 610 { 611 KeywordTestData keyword_match[] = { 612 { base::ASCIIToUTF16("foo.com"), base::string16(), 613 base::ASCIIToUTF16("foo.com") } 614 }; 615 616 SCOPED_TRACE("keyword match as usual"); 617 RunKeywordTest(base::ASCIIToUTF16("fo"), keyword_match, 618 ARRAYSIZE_UNSAFE(keyword_match)); 619 } 620 621 // The same result set with an input of "f" (versus "fo") should get 622 // a different associated keyword because "f" is an exact match for 623 // a keyword and that should trump the keyword normally associated with 624 // this match. 625 { 626 KeywordTestData keyword_match[] = { 627 { base::ASCIIToUTF16("foo.com"), base::string16(), 628 base::ASCIIToUTF16("f") } 629 }; 630 631 SCOPED_TRACE("keyword exact match"); 632 RunKeywordTest(base::ASCIIToUTF16("f"), keyword_match, 633 ARRAYSIZE_UNSAFE(keyword_match)); 634 } 635 } 636 637 TEST_F(AutocompleteProviderTest, UpdateAssistedQueryStats) { 638 ResetControllerWithTestProviders(false, NULL, NULL); 639 640 { 641 AssistedQueryStatsTestData test_data[] = { 642 // MSVC doesn't support zero-length arrays, so supply some dummy data. 643 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, "" } 644 }; 645 SCOPED_TRACE("No matches"); 646 // Note: We pass 0 here to ignore the dummy data above. 647 RunAssistedQueryStatsTest(test_data, 0); 648 } 649 650 { 651 AssistedQueryStatsTestData test_data[] = { 652 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, "chrome..69i57" } 653 }; 654 SCOPED_TRACE("One match"); 655 RunAssistedQueryStatsTest(test_data, ARRAYSIZE_UNSAFE(test_data)); 656 } 657 658 { 659 AssistedQueryStatsTestData test_data[] = { 660 { AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, 661 "chrome..69i57j69i58j5l2j0l3j69i59" }, 662 { AutocompleteMatchType::URL_WHAT_YOU_TYPED, 663 "chrome..69i57j69i58j5l2j0l3j69i59" }, 664 { AutocompleteMatchType::NAVSUGGEST, 665 "chrome.2.69i57j69i58j5l2j0l3j69i59" }, 666 { AutocompleteMatchType::NAVSUGGEST, 667 "chrome.3.69i57j69i58j5l2j0l3j69i59" }, 668 { AutocompleteMatchType::SEARCH_SUGGEST, 669 "chrome.4.69i57j69i58j5l2j0l3j69i59" }, 670 { AutocompleteMatchType::SEARCH_SUGGEST, 671 "chrome.5.69i57j69i58j5l2j0l3j69i59" }, 672 { AutocompleteMatchType::SEARCH_SUGGEST, 673 "chrome.6.69i57j69i58j5l2j0l3j69i59" }, 674 { AutocompleteMatchType::SEARCH_HISTORY, 675 "chrome.7.69i57j69i58j5l2j0l3j69i59" }, 676 }; 677 SCOPED_TRACE("Multiple matches"); 678 RunAssistedQueryStatsTest(test_data, ARRAYSIZE_UNSAFE(test_data)); 679 } 680 } 681 682 TEST_F(AutocompleteProviderTest, GetDestinationURL) { 683 ResetControllerWithKeywordAndSearchProviders(); 684 685 // For the destination URL to have aqs parameters for query formulation time 686 // and the field trial triggered bit, many conditions need to be satisfied. 687 AutocompleteMatch match(NULL, 1100, false, 688 AutocompleteMatchType::SEARCH_SUGGEST); 689 GURL url(GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456))); 690 EXPECT_TRUE(url.path().empty()); 691 692 // The protocol needs to be https. 693 RegisterTemplateURL(base::ASCIIToUTF16(kTestTemplateURLKeyword), 694 "https://aqs/{searchTerms}/{google:assistedQueryStats}"); 695 url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456)); 696 EXPECT_TRUE(url.path().empty()); 697 698 // There needs to be a keyword provider. 699 match.keyword = base::ASCIIToUTF16(kTestTemplateURLKeyword); 700 url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456)); 701 EXPECT_TRUE(url.path().empty()); 702 703 // search_terms_args needs to be set. 704 match.search_terms_args.reset( 705 new TemplateURLRef::SearchTermsArgs(base::string16())); 706 url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456)); 707 EXPECT_TRUE(url.path().empty()); 708 709 // assisted_query_stats needs to have been previously set. 710 match.search_terms_args->assisted_query_stats = 711 "chrome.0.69i57j69i58j5l2j0l3j69i59"; 712 url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456)); 713 EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j0j0&", url.path()); 714 715 // Test field trial triggered bit set. 716 controller_->search_provider_->field_trial_triggered_in_session_ = true; 717 EXPECT_TRUE( 718 controller_->search_provider_->field_trial_triggered_in_session()); 719 url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456)); 720 EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j1j0&", url.path()); 721 722 // Test page classification set. 723 controller_->input_.current_page_classification_ = 724 metrics::OmniboxEventProto::OTHER; 725 controller_->search_provider_->field_trial_triggered_in_session_ = false; 726 EXPECT_FALSE( 727 controller_->search_provider_->field_trial_triggered_in_session()); 728 url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456)); 729 EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j0j4&", url.path()); 730 731 // Test page classification and field trial triggered set. 732 controller_->search_provider_->field_trial_triggered_in_session_ = true; 733 EXPECT_TRUE( 734 controller_->search_provider_->field_trial_triggered_in_session()); 735 url = GetDestinationURL(match, base::TimeDelta::FromMilliseconds(2456)); 736 EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j1j4&", url.path()); 737 } 738