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 "chrome/browser/autocomplete/autocomplete_result.h" 6 7 #include <vector> 8 9 #include "base/memory/scoped_ptr.h" 10 #include "base/metrics/field_trial.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "chrome/browser/autocomplete/autocomplete_input.h" 15 #include "chrome/browser/autocomplete/autocomplete_match.h" 16 #include "chrome/browser/autocomplete/autocomplete_provider.h" 17 #include "chrome/browser/omnibox/omnibox_field_trial.h" 18 #include "chrome/browser/search_engines/template_url_prepopulate_data.h" 19 #include "chrome/browser/search_engines/template_url_service.h" 20 #include "chrome/browser/search_engines/template_url_service_test_util.h" 21 #include "chrome/common/autocomplete_match_type.h" 22 #include "chrome/test/base/testing_profile.h" 23 #include "components/metrics/proto/omnibox_event.pb.h" 24 #include "components/variations/entropy_provider.h" 25 #include "components/variations/variations_associated_data.h" 26 #include "testing/gtest/include/gtest/gtest.h" 27 28 using metrics::OmniboxEventProto; 29 30 namespace { 31 32 struct AutocompleteMatchTestData { 33 std::string destination_url; 34 AutocompleteMatch::Type type; 35 }; 36 37 const AutocompleteMatchTestData kVerbatimMatches[] = { 38 { "http://search-what-you-typed/", 39 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 40 { "http://url-what-you-typed/", AutocompleteMatchType::URL_WHAT_YOU_TYPED }, 41 }; 42 43 const AutocompleteMatchTestData kNonVerbatimMatches[] = { 44 { "http://search-history/", AutocompleteMatchType::SEARCH_HISTORY }, 45 { "http://history-title/", AutocompleteMatchType::HISTORY_TITLE }, 46 }; 47 48 // Adds |count| AutocompleteMatches to |matches|. 49 void PopulateAutocompleteMatchesFromTestData( 50 const AutocompleteMatchTestData* data, 51 size_t count, 52 ACMatches* matches) { 53 ASSERT_TRUE(matches != NULL); 54 for (size_t i = 0; i < count; ++i) { 55 AutocompleteMatch match; 56 match.destination_url = GURL(data[i].destination_url); 57 match.relevance = 58 matches->empty() ? 1300 : (matches->back().relevance - 100); 59 match.allowed_to_be_default_match = true; 60 match.type = data[i].type; 61 matches->push_back(match); 62 } 63 } 64 65 } // namespace 66 67 class AutocompleteResultTest : public testing::Test { 68 public: 69 struct TestData { 70 // Used to build a url for the AutocompleteMatch. The URL becomes 71 // "http://" + ('a' + |url_id|) (e.g. an ID of 2 yields "http://b"). 72 int url_id; 73 74 // ID of the provider. 75 int provider_id; 76 77 // Relevance score. 78 int relevance; 79 80 // Duplicate matches. 81 std::vector<AutocompleteMatch> duplicate_matches; 82 }; 83 84 AutocompleteResultTest() { 85 // Destroy the existing FieldTrialList before creating a new one to avoid 86 // a DCHECK. 87 field_trial_list_.reset(); 88 field_trial_list_.reset(new base::FieldTrialList( 89 new metrics::SHA1EntropyProvider("foo"))); 90 chrome_variations::testing::ClearAllVariationParams(); 91 } 92 93 virtual void SetUp() OVERRIDE { 94 #if defined(OS_ANDROID) 95 TemplateURLPrepopulateData::InitCountryCode( 96 std::string() /* unknown country code */); 97 #endif 98 test_util_.SetUp(); 99 test_util_.VerifyLoad(); 100 } 101 102 virtual void TearDown() OVERRIDE { 103 test_util_.TearDown(); 104 } 105 106 // Configures |match| from |data|. 107 static void PopulateAutocompleteMatch(const TestData& data, 108 AutocompleteMatch* match); 109 110 // Adds |count| AutocompleteMatches to |matches|. 111 static void PopulateAutocompleteMatches(const TestData* data, 112 size_t count, 113 ACMatches* matches); 114 115 // Asserts that |result| has |expected_count| matches matching |expected|. 116 void AssertResultMatches(const AutocompleteResult& result, 117 const TestData* expected, 118 size_t expected_count); 119 120 // Creates an AutocompleteResult from |last| and |current|. The two are 121 // merged by |CopyOldMatches| and compared by |AssertResultMatches|. 122 void RunCopyOldMatchesTest(const TestData* last, size_t last_size, 123 const TestData* current, size_t current_size, 124 const TestData* expected, size_t expected_size); 125 126 protected: 127 TemplateURLServiceTestUtil test_util_; 128 129 private: 130 scoped_ptr<base::FieldTrialList> field_trial_list_; 131 132 DISALLOW_COPY_AND_ASSIGN(AutocompleteResultTest); 133 }; 134 135 // static 136 void AutocompleteResultTest::PopulateAutocompleteMatch( 137 const TestData& data, 138 AutocompleteMatch* match) { 139 match->provider = reinterpret_cast<AutocompleteProvider*>(data.provider_id); 140 match->fill_into_edit = base::IntToString16(data.url_id); 141 std::string url_id(1, data.url_id + 'a'); 142 match->destination_url = GURL("http://" + url_id); 143 match->relevance = data.relevance; 144 match->allowed_to_be_default_match = true; 145 match->duplicate_matches = data.duplicate_matches; 146 } 147 148 // static 149 void AutocompleteResultTest::PopulateAutocompleteMatches( 150 const TestData* data, 151 size_t count, 152 ACMatches* matches) { 153 for (size_t i = 0; i < count; ++i) { 154 AutocompleteMatch match; 155 PopulateAutocompleteMatch(data[i], &match); 156 matches->push_back(match); 157 } 158 } 159 160 void AutocompleteResultTest::AssertResultMatches( 161 const AutocompleteResult& result, 162 const TestData* expected, 163 size_t expected_count) { 164 ASSERT_EQ(expected_count, result.size()); 165 for (size_t i = 0; i < expected_count; ++i) { 166 AutocompleteMatch expected_match; 167 PopulateAutocompleteMatch(expected[i], &expected_match); 168 const AutocompleteMatch& match = *(result.begin() + i); 169 EXPECT_EQ(expected_match.provider, match.provider) << i; 170 EXPECT_EQ(expected_match.relevance, match.relevance) << i; 171 EXPECT_EQ(expected_match.destination_url.spec(), 172 match.destination_url.spec()) << i; 173 } 174 } 175 176 void AutocompleteResultTest::RunCopyOldMatchesTest( 177 const TestData* last, size_t last_size, 178 const TestData* current, size_t current_size, 179 const TestData* expected, size_t expected_size) { 180 AutocompleteInput input(base::ASCIIToUTF16("a"), base::string16::npos, 181 base::string16(), GURL(), 182 OmniboxEventProto::INVALID_SPEC, false, false, false, 183 true); 184 185 ACMatches last_matches; 186 PopulateAutocompleteMatches(last, last_size, &last_matches); 187 AutocompleteResult last_result; 188 last_result.AppendMatches(last_matches); 189 last_result.SortAndCull(input, test_util_.profile()); 190 191 ACMatches current_matches; 192 PopulateAutocompleteMatches(current, current_size, ¤t_matches); 193 AutocompleteResult current_result; 194 current_result.AppendMatches(current_matches); 195 current_result.SortAndCull(input, test_util_.profile()); 196 current_result.CopyOldMatches(input, last_result, test_util_.profile()); 197 198 AssertResultMatches(current_result, expected, expected_size); 199 } 200 201 // Assertion testing for AutocompleteResult::Swap. 202 TEST_F(AutocompleteResultTest, Swap) { 203 AutocompleteResult r1; 204 AutocompleteResult r2; 205 206 // Swap with empty shouldn't do anything interesting. 207 r1.Swap(&r2); 208 EXPECT_EQ(r1.end(), r1.default_match()); 209 EXPECT_EQ(r2.end(), r2.default_match()); 210 211 // Swap with a single match. 212 ACMatches matches; 213 AutocompleteMatch match; 214 match.relevance = 1; 215 match.allowed_to_be_default_match = true; 216 AutocompleteInput input(base::ASCIIToUTF16("a"), base::string16::npos, 217 base::string16(), GURL(), 218 OmniboxEventProto::INVALID_SPEC, false, false, false, 219 true); 220 matches.push_back(match); 221 r1.AppendMatches(matches); 222 r1.SortAndCull(input, test_util_.profile()); 223 EXPECT_EQ(r1.begin(), r1.default_match()); 224 EXPECT_EQ("http://a/", r1.alternate_nav_url().spec()); 225 r1.Swap(&r2); 226 EXPECT_TRUE(r1.empty()); 227 EXPECT_EQ(r1.end(), r1.default_match()); 228 EXPECT_TRUE(r1.alternate_nav_url().is_empty()); 229 ASSERT_FALSE(r2.empty()); 230 EXPECT_EQ(r2.begin(), r2.default_match()); 231 EXPECT_EQ("http://a/", r2.alternate_nav_url().spec()); 232 } 233 234 // Tests that if the new results have a lower max relevance score than last, 235 // any copied results have their relevance shifted down. 236 TEST_F(AutocompleteResultTest, CopyOldMatches) { 237 TestData last[] = { 238 { 0, 0, 1000 }, 239 { 1, 0, 500 }, 240 }; 241 TestData current[] = { 242 { 2, 0, 400 }, 243 }; 244 TestData result[] = { 245 { 2, 0, 400 }, 246 { 1, 0, 399 }, 247 }; 248 249 ASSERT_NO_FATAL_FAILURE( 250 RunCopyOldMatchesTest(last, ARRAYSIZE_UNSAFE(last), 251 current, ARRAYSIZE_UNSAFE(current), 252 result, ARRAYSIZE_UNSAFE(result))); 253 } 254 255 // Tests that matches are copied correctly from two distinct providers. 256 TEST_F(AutocompleteResultTest, CopyOldMatches2) { 257 TestData last[] = { 258 { 0, 0, 1000 }, 259 { 1, 1, 500 }, 260 { 2, 0, 400 }, 261 { 3, 1, 300 }, 262 }; 263 TestData current[] = { 264 { 4, 0, 1100 }, 265 { 5, 1, 550 }, 266 }; 267 TestData result[] = { 268 { 4, 0, 1100 }, 269 { 5, 1, 550 }, 270 { 2, 0, 400 }, 271 { 3, 1, 300 }, 272 }; 273 274 ASSERT_NO_FATAL_FAILURE( 275 RunCopyOldMatchesTest(last, ARRAYSIZE_UNSAFE(last), 276 current, ARRAYSIZE_UNSAFE(current), 277 result, ARRAYSIZE_UNSAFE(result))); 278 } 279 280 // Tests that matches with empty destination URLs aren't treated as duplicates 281 // and culled. 282 TEST_F(AutocompleteResultTest, SortAndCullEmptyDestinationURLs) { 283 TestData data[] = { 284 { 1, 0, 500 }, 285 { 0, 0, 1100 }, 286 { 1, 0, 1000 }, 287 { 0, 0, 1300 }, 288 { 0, 0, 1200 }, 289 }; 290 291 ACMatches matches; 292 PopulateAutocompleteMatches(data, arraysize(data), &matches); 293 matches[1].destination_url = GURL(); 294 matches[3].destination_url = GURL(); 295 matches[4].destination_url = GURL(); 296 297 AutocompleteResult result; 298 result.AppendMatches(matches); 299 AutocompleteInput input(base::string16(), base::string16::npos, 300 base::string16(), GURL(), 301 OmniboxEventProto::INVALID_SPEC, false, false, false, 302 true); 303 result.SortAndCull(input, test_util_.profile()); 304 305 // Of the two results with the same non-empty destination URL, the 306 // lower-relevance one should be dropped. All of the results with empty URLs 307 // should be kept. 308 ASSERT_EQ(4U, result.size()); 309 EXPECT_TRUE(result.match_at(0)->destination_url.is_empty()); 310 EXPECT_EQ(1300, result.match_at(0)->relevance); 311 EXPECT_TRUE(result.match_at(1)->destination_url.is_empty()); 312 EXPECT_EQ(1200, result.match_at(1)->relevance); 313 EXPECT_TRUE(result.match_at(2)->destination_url.is_empty()); 314 EXPECT_EQ(1100, result.match_at(2)->relevance); 315 EXPECT_EQ("http://b/", result.match_at(3)->destination_url.spec()); 316 EXPECT_EQ(1000, result.match_at(3)->relevance); 317 } 318 319 TEST_F(AutocompleteResultTest, SortAndCullDuplicateSearchURLs) { 320 // Register a template URL that corresponds to 'foo' search engine. 321 TemplateURLData url_data; 322 url_data.short_name = base::ASCIIToUTF16("unittest"); 323 url_data.SetKeyword(base::ASCIIToUTF16("foo")); 324 url_data.SetURL("http://www.foo.com/s?q={searchTerms}"); 325 test_util_.model()->Add(new TemplateURL(url_data)); 326 327 TestData data[] = { 328 { 0, 0, 1300 }, 329 { 1, 0, 1200 }, 330 { 2, 0, 1100 }, 331 { 3, 0, 1000 }, 332 { 4, 1, 900 }, 333 }; 334 335 ACMatches matches; 336 PopulateAutocompleteMatches(data, arraysize(data), &matches); 337 matches[0].destination_url = GURL("http://www.foo.com/s?q=foo"); 338 matches[1].destination_url = GURL("http://www.foo.com/s?q=foo2"); 339 matches[2].destination_url = GURL("http://www.foo.com/s?q=foo&oq=f"); 340 matches[3].destination_url = GURL("http://www.foo.com/s?q=foo&aqs=0"); 341 matches[4].destination_url = GURL("http://www.foo.com/"); 342 343 AutocompleteResult result; 344 result.AppendMatches(matches); 345 AutocompleteInput input(base::string16(), base::string16::npos, 346 base::string16(), GURL(), 347 OmniboxEventProto::INVALID_SPEC, false, false, false, 348 true); 349 result.SortAndCull(input, test_util_.profile()); 350 351 // We expect the 3rd and 4th results to be removed. 352 ASSERT_EQ(3U, result.size()); 353 EXPECT_EQ("http://www.foo.com/s?q=foo", 354 result.match_at(0)->destination_url.spec()); 355 EXPECT_EQ(1300, result.match_at(0)->relevance); 356 EXPECT_EQ("http://www.foo.com/s?q=foo2", 357 result.match_at(1)->destination_url.spec()); 358 EXPECT_EQ(1200, result.match_at(1)->relevance); 359 EXPECT_EQ("http://www.foo.com/", 360 result.match_at(2)->destination_url.spec()); 361 EXPECT_EQ(900, result.match_at(2)->relevance); 362 } 363 364 TEST_F(AutocompleteResultTest, SortAndCullWithMatchDups) { 365 // Register a template URL that corresponds to 'foo' search engine. 366 TemplateURLData url_data; 367 url_data.short_name = base::ASCIIToUTF16("unittest"); 368 url_data.SetKeyword(base::ASCIIToUTF16("foo")); 369 url_data.SetURL("http://www.foo.com/s?q={searchTerms}"); 370 test_util_.model()->Add(new TemplateURL(url_data)); 371 372 AutocompleteMatch dup_match; 373 dup_match.destination_url = GURL("http://www.foo.com/s?q=foo&oq=dup"); 374 std::vector<AutocompleteMatch> dups; 375 dups.push_back(dup_match); 376 377 TestData data[] = { 378 { 0, 0, 1300, dups }, 379 { 1, 0, 1200 }, 380 { 2, 0, 1100 }, 381 { 3, 0, 1000, dups }, 382 { 4, 1, 900 }, 383 { 5, 0, 800 }, 384 }; 385 386 ACMatches matches; 387 PopulateAutocompleteMatches(data, arraysize(data), &matches); 388 matches[0].destination_url = GURL("http://www.foo.com/s?q=foo"); 389 matches[1].destination_url = GURL("http://www.foo.com/s?q=foo2"); 390 matches[2].destination_url = GURL("http://www.foo.com/s?q=foo&oq=f"); 391 matches[3].destination_url = GURL("http://www.foo.com/s?q=foo&aqs=0"); 392 matches[4].destination_url = GURL("http://www.foo.com/"); 393 matches[5].destination_url = GURL("http://www.foo.com/s?q=foo2&oq=f"); 394 395 AutocompleteResult result; 396 result.AppendMatches(matches); 397 AutocompleteInput input(base::string16(), base::string16::npos, 398 base::string16(), GURL(), 399 OmniboxEventProto::INVALID_SPEC, false, false, false, 400 true); 401 result.SortAndCull(input, test_util_.profile()); 402 403 // Expect 3 unique results after SortAndCull(). 404 ASSERT_EQ(3U, result.size()); 405 406 // Check that 3rd and 4th result got added to the first result as dups 407 // and also duplicates of the 4th match got copied. 408 ASSERT_EQ(4U, result.match_at(0)->duplicate_matches.size()); 409 const AutocompleteMatch* first_match = result.match_at(0); 410 EXPECT_EQ(matches[2].destination_url, 411 first_match->duplicate_matches.at(1).destination_url); 412 EXPECT_EQ(dup_match.destination_url, 413 first_match->duplicate_matches.at(2).destination_url); 414 EXPECT_EQ(matches[3].destination_url, 415 first_match->duplicate_matches.at(3).destination_url); 416 417 // Check that 6th result started a new list of dups for the second result. 418 ASSERT_EQ(1U, result.match_at(1)->duplicate_matches.size()); 419 EXPECT_EQ(matches[5].destination_url, 420 result.match_at(1)->duplicate_matches.at(0).destination_url); 421 } 422 423 TEST_F(AutocompleteResultTest, SortAndCullWithDemotionsByType) { 424 // Add some matches. 425 ACMatches matches; 426 const AutocompleteMatchTestData data[] = { 427 { "http://history-url/", AutocompleteMatchType::HISTORY_URL }, 428 { "http://search-what-you-typed/", 429 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 430 { "http://history-title/", AutocompleteMatchType::HISTORY_TITLE }, 431 { "http://search-history/", AutocompleteMatchType::SEARCH_HISTORY }, 432 }; 433 PopulateAutocompleteMatchesFromTestData(data, arraysize(data), &matches); 434 435 // Demote the search history match relevance score. 436 matches.back().relevance = 500; 437 438 // Add a rule demoting history-url and killing history-title. 439 { 440 std::map<std::string, std::string> params; 441 params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":3:*"] = 442 "1:50,7:100,2:0"; // 3 == HOME_PAGE 443 ASSERT_TRUE(chrome_variations::AssociateVariationParams( 444 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); 445 } 446 base::FieldTrialList::CreateFieldTrial( 447 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); 448 449 AutocompleteResult result; 450 result.AppendMatches(matches); 451 AutocompleteInput input(base::string16(), base::string16::npos, 452 base::string16(), GURL(), 453 OmniboxEventProto::HOME_PAGE, false, false, false, 454 true); 455 result.SortAndCull(input, test_util_.profile()); 456 457 // Check the new ordering. The history-title results should be omitted. 458 // We cannot check relevance scores because the matches are sorted by 459 // demoted relevance but the actual relevance scores are not modified. 460 ASSERT_EQ(3u, result.size()); 461 EXPECT_EQ("http://search-what-you-typed/", 462 result.match_at(0)->destination_url.spec()); 463 EXPECT_EQ("http://history-url/", 464 result.match_at(1)->destination_url.spec()); 465 EXPECT_EQ("http://search-history/", 466 result.match_at(2)->destination_url.spec()); 467 } 468 469 TEST_F(AutocompleteResultTest, SortAndCullWithMatchDupsAndDemotionsByType) { 470 // Add some matches. 471 ACMatches matches; 472 const AutocompleteMatchTestData data[] = { 473 { "http://search-what-you-typed/", 474 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, 475 { "http://dup-url/", AutocompleteMatchType::HISTORY_URL }, 476 { "http://dup-url/", AutocompleteMatchType::NAVSUGGEST }, 477 { "http://search-url/", AutocompleteMatchType::SEARCH_SUGGEST }, 478 { "http://history-url/", AutocompleteMatchType::HISTORY_URL }, 479 }; 480 PopulateAutocompleteMatchesFromTestData(data, arraysize(data), &matches); 481 482 // Add a rule demoting HISTORY_URL. 483 { 484 std::map<std::string, std::string> params; 485 params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":8:*"] = 486 "1:50"; // 8 == INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS 487 ASSERT_TRUE(chrome_variations::AssociateVariationParams( 488 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "C", params)); 489 } 490 base::FieldTrialList::CreateFieldTrial( 491 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "C"); 492 493 { 494 AutocompleteResult result; 495 result.AppendMatches(matches); 496 AutocompleteInput input( 497 base::string16(), base::string16::npos, base::string16(), GURL(), 498 OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS, false, 499 false, false, true); 500 result.SortAndCull(input, test_util_.profile()); 501 502 // The NAVSUGGEST dup-url stay above search-url since the navsuggest 503 // variant should not be demoted. 504 ASSERT_EQ(4u, result.size()); 505 EXPECT_EQ("http://search-what-you-typed/", 506 result.match_at(0)->destination_url.spec()); 507 EXPECT_EQ("http://dup-url/", 508 result.match_at(1)->destination_url.spec()); 509 EXPECT_EQ(AutocompleteMatchType::NAVSUGGEST, 510 result.match_at(1)->type); 511 EXPECT_EQ("http://search-url/", 512 result.match_at(2)->destination_url.spec()); 513 EXPECT_EQ("http://history-url/", 514 result.match_at(3)->destination_url.spec()); 515 } 516 } 517 518 TEST_F(AutocompleteResultTest, SortAndCullReorderForDefaultMatch) { 519 TestData data[] = { 520 { 0, 0, 1300 }, 521 { 1, 0, 1200 }, 522 { 2, 0, 1100 }, 523 { 3, 0, 1000 } 524 }; 525 526 { 527 // Check that reorder doesn't do anything if the top result 528 // is already a legal default match (which is the default from 529 // PopulateAutocompleteMatches()). 530 ACMatches matches; 531 PopulateAutocompleteMatches(data, arraysize(data), &matches); 532 AutocompleteResult result; 533 result.AppendMatches(matches); 534 AutocompleteInput input(base::string16(), base::string16::npos, 535 base::string16(), GURL(), 536 OmniboxEventProto::HOME_PAGE, false, false, false, 537 true); 538 result.SortAndCull(input, test_util_.profile()); 539 AssertResultMatches(result, data, 4); 540 } 541 542 { 543 // Check that reorder swaps up a result appropriately. 544 ACMatches matches; 545 PopulateAutocompleteMatches(data, arraysize(data), &matches); 546 matches[0].allowed_to_be_default_match = false; 547 matches[1].allowed_to_be_default_match = false; 548 AutocompleteResult result; 549 result.AppendMatches(matches); 550 AutocompleteInput input(base::string16(), base::string16::npos, 551 base::string16(), GURL(), 552 OmniboxEventProto::HOME_PAGE, false, false, false, 553 true); 554 result.SortAndCull(input, test_util_.profile()); 555 ASSERT_EQ(4U, result.size()); 556 EXPECT_EQ("http://c/", result.match_at(0)->destination_url.spec()); 557 EXPECT_EQ("http://a/", result.match_at(1)->destination_url.spec()); 558 EXPECT_EQ("http://b/", result.match_at(2)->destination_url.spec()); 559 EXPECT_EQ("http://d/", result.match_at(3)->destination_url.spec()); 560 } 561 } 562 563 564 565 TEST_F(AutocompleteResultTest, SortAndCullWithDisableInlining) { 566 TestData data[] = { 567 { 0, 0, 1300 }, 568 { 1, 0, 1200 }, 569 { 2, 0, 1100 }, 570 { 3, 0, 1000 } 571 }; 572 573 { 574 // Check that with the field trial disabled, we keep keep the first match 575 // first even if it has an inline autocompletion. 576 ACMatches matches; 577 PopulateAutocompleteMatches(data, arraysize(data), &matches); 578 matches[0].inline_autocompletion = base::ASCIIToUTF16("completion"); 579 AutocompleteResult result; 580 result.AppendMatches(matches); 581 AutocompleteInput input(base::string16(), base::string16::npos, 582 base::string16(), GURL(), 583 OmniboxEventProto::HOME_PAGE, false, false, false, 584 true); 585 result.SortAndCull(input, test_util_.profile()); 586 AssertResultMatches(result, data, 4); 587 } 588 589 // Enable the field trial to disable inlining. 590 { 591 std::map<std::string, std::string> params; 592 params[OmniboxFieldTrial::kDisableInliningRule] = "true"; 593 ASSERT_TRUE(chrome_variations::AssociateVariationParams( 594 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "D", params)); 595 } 596 base::FieldTrialList::CreateFieldTrial( 597 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "D"); 598 599 { 600 // Now the first match should be demoted past the second. 601 ACMatches matches; 602 PopulateAutocompleteMatches(data, arraysize(data), &matches); 603 matches[0].inline_autocompletion = base::ASCIIToUTF16("completion"); 604 AutocompleteResult result; 605 result.AppendMatches(matches); 606 AutocompleteInput input(base::string16(), base::string16::npos, 607 base::string16(), GURL(), 608 OmniboxEventProto::HOME_PAGE, false, false, false, 609 true); 610 result.SortAndCull(input, test_util_.profile()); 611 ASSERT_EQ(4U, result.size()); 612 EXPECT_EQ("http://b/", result.match_at(0)->destination_url.spec()); 613 EXPECT_EQ("http://a/", result.match_at(1)->destination_url.spec()); 614 EXPECT_EQ("http://c/", result.match_at(2)->destination_url.spec()); 615 EXPECT_EQ("http://d/", result.match_at(3)->destination_url.spec()); 616 } 617 618 { 619 // But if there was no inline autocompletion on the first match, then 620 // the order should stay the same. This is true even if there are 621 // inline autocompletions elsewhere. 622 ACMatches matches; 623 PopulateAutocompleteMatches(data, arraysize(data), &matches); 624 matches[2].inline_autocompletion = base::ASCIIToUTF16("completion"); 625 AutocompleteResult result; 626 result.AppendMatches(matches); 627 AutocompleteInput input(base::string16(), base::string16::npos, 628 base::string16(), GURL(), 629 OmniboxEventProto::HOME_PAGE, false, false, false, 630 true); 631 result.SortAndCull(input, test_util_.profile()); 632 AssertResultMatches(result, data, 4); 633 } 634 635 { 636 // Try a more complicated situation. 637 ACMatches matches; 638 PopulateAutocompleteMatches(data, arraysize(data), &matches); 639 matches[0].allowed_to_be_default_match = false; 640 matches[1].inline_autocompletion = base::ASCIIToUTF16("completion"); 641 AutocompleteResult result; 642 result.AppendMatches(matches); 643 AutocompleteInput input(base::string16(), base::string16::npos, 644 base::string16(), GURL(), 645 OmniboxEventProto::HOME_PAGE, false, false, false, 646 true); 647 result.SortAndCull(input, test_util_.profile()); 648 ASSERT_EQ(4U, result.size()); 649 EXPECT_EQ("http://c/", result.match_at(0)->destination_url.spec()); 650 EXPECT_EQ("http://a/", result.match_at(1)->destination_url.spec()); 651 EXPECT_EQ("http://b/", result.match_at(2)->destination_url.spec()); 652 EXPECT_EQ("http://d/", result.match_at(3)->destination_url.spec()); 653 } 654 655 { 656 // Try another complicated situation. 657 ACMatches matches; 658 PopulateAutocompleteMatches(data, arraysize(data), &matches); 659 matches[0].inline_autocompletion = base::ASCIIToUTF16("completion"); 660 matches[1].allowed_to_be_default_match = false; 661 AutocompleteResult result; 662 result.AppendMatches(matches); 663 AutocompleteInput input(base::string16(), base::string16::npos, 664 base::string16(), GURL(), 665 OmniboxEventProto::HOME_PAGE, false, false, false, 666 true); 667 result.SortAndCull(input, test_util_.profile()); 668 ASSERT_EQ(4U, result.size()); 669 EXPECT_EQ("http://c/", result.match_at(0)->destination_url.spec()); 670 EXPECT_EQ("http://a/", result.match_at(1)->destination_url.spec()); 671 EXPECT_EQ("http://b/", result.match_at(2)->destination_url.spec()); 672 EXPECT_EQ("http://d/", result.match_at(3)->destination_url.spec()); 673 } 674 675 { 676 // Check that disaster doesn't strike if we can't demote the top inline 677 // autocompletion because every match either has a completion or isn't 678 // allowed to be the default match. In this case, we should leave 679 // everything untouched. 680 ACMatches matches; 681 PopulateAutocompleteMatches(data, arraysize(data), &matches); 682 matches[0].inline_autocompletion = base::ASCIIToUTF16("completion"); 683 matches[1].allowed_to_be_default_match = false; 684 matches[2].allowed_to_be_default_match = false; 685 matches[3].inline_autocompletion = base::ASCIIToUTF16("completion"); 686 AutocompleteResult result; 687 result.AppendMatches(matches); 688 AutocompleteInput input(base::string16(), base::string16::npos, 689 base::string16(), GURL(), 690 OmniboxEventProto::HOME_PAGE, false, false, false, 691 true); 692 result.SortAndCull(input, test_util_.profile()); 693 AssertResultMatches(result, data, 4); 694 } 695 696 { 697 // Check a similar situation, except in this case the top match is not 698 // allowed to the default match, so it still needs to be demoted so we 699 // get a legal default match first. That match will have an inline 700 // autocompletion because we don't have any better options. 701 ACMatches matches; 702 PopulateAutocompleteMatches(data, arraysize(data), &matches); 703 matches[0].allowed_to_be_default_match = false; 704 matches[1].inline_autocompletion = base::ASCIIToUTF16("completion"); 705 matches[2].allowed_to_be_default_match = false; 706 matches[3].inline_autocompletion = base::ASCIIToUTF16("completion"); 707 AutocompleteResult result; 708 result.AppendMatches(matches); 709 AutocompleteInput input(base::string16(), base::string16::npos, 710 base::string16(), GURL(), 711 OmniboxEventProto::HOME_PAGE, false, false, false, 712 true); 713 result.SortAndCull(input, test_util_.profile()); 714 ASSERT_EQ(4U, result.size()); 715 EXPECT_EQ("http://b/", result.match_at(0)->destination_url.spec()); 716 EXPECT_EQ("http://a/", result.match_at(1)->destination_url.spec()); 717 EXPECT_EQ("http://c/", result.match_at(2)->destination_url.spec()); 718 EXPECT_EQ("http://d/", result.match_at(3)->destination_url.spec()); 719 } 720 } 721 722 TEST_F(AutocompleteResultTest, ShouldHideTopMatch) { 723 base::FieldTrialList::CreateFieldTrial("InstantExtended", 724 "Group1 hide_verbatim:1"); 725 ACMatches matches; 726 727 // Case 1: Top match is a verbatim match. 728 PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 1, &matches); 729 AutocompleteResult result; 730 result.AppendMatches(matches); 731 EXPECT_TRUE(result.ShouldHideTopMatch()); 732 matches.clear(); 733 result.Reset(); 734 735 // Case 2: If the verbatim first match is followed by another verbatim match, 736 // don't hide the top verbatim match. 737 PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 738 arraysize(kVerbatimMatches), 739 &matches); 740 result.AppendMatches(matches); 741 EXPECT_FALSE(result.ShouldHideTopMatch()); 742 matches.clear(); 743 result.Reset(); 744 745 // Case 3: Top match is not a verbatim match. Do not hide the top match. 746 PopulateAutocompleteMatchesFromTestData(kNonVerbatimMatches, 1, &matches); 747 PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 748 arraysize(kVerbatimMatches), 749 &matches); 750 result.AppendMatches(matches); 751 EXPECT_FALSE(result.ShouldHideTopMatch()); 752 } 753 754 TEST_F(AutocompleteResultTest, ShouldHideTopMatchAfterCopy) { 755 base::FieldTrialList::CreateFieldTrial("InstantExtended", 756 "Group1 hide_verbatim:1"); 757 ACMatches matches; 758 759 // Case 1: Top match is a verbatim match followed by only copied matches. 760 PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 761 arraysize(kVerbatimMatches), 762 &matches); 763 for (size_t i = 1; i < arraysize(kVerbatimMatches); ++i) 764 matches[i].from_previous = true; 765 AutocompleteResult result; 766 result.AppendMatches(matches); 767 EXPECT_TRUE(result.ShouldHideTopMatch()); 768 result.Reset(); 769 770 // Case 2: The copied matches are then followed by a non-verbatim match. 771 PopulateAutocompleteMatchesFromTestData(kNonVerbatimMatches, 1, &matches); 772 result.AppendMatches(matches); 773 EXPECT_TRUE(result.ShouldHideTopMatch()); 774 result.Reset(); 775 776 // Case 3: The copied matches are instead followed by a verbatim match. 777 matches.back().from_previous = true; 778 PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 1, &matches); 779 result.AppendMatches(matches); 780 EXPECT_FALSE(result.ShouldHideTopMatch()); 781 } 782 783 TEST_F(AutocompleteResultTest, DoNotHideTopMatch_FieldTrialFlagDisabled) { 784 // This test config is identical to ShouldHideTopMatch test ("Case 1") except 785 // that the "hide_verbatim" flag is disabled in the field trials. 786 base::FieldTrialList::CreateFieldTrial("InstantExtended", 787 "Group1 hide_verbatim:0"); 788 ACMatches matches; 789 PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 1, &matches); 790 AutocompleteResult result; 791 result.AppendMatches(matches); 792 // Field trial flag "hide_verbatim" is disabled. Do not hide top match. 793 EXPECT_FALSE(result.ShouldHideTopMatch()); 794 } 795 796 TEST_F(AutocompleteResultTest, TopMatchIsStandaloneVerbatimMatch) { 797 ACMatches matches; 798 AutocompleteResult result; 799 result.AppendMatches(matches); 800 801 // Case 1: Result set is empty. 802 EXPECT_FALSE(result.TopMatchIsStandaloneVerbatimMatch()); 803 804 // Case 2: Top match is not a verbatim match. 805 PopulateAutocompleteMatchesFromTestData(kNonVerbatimMatches, 1, &matches); 806 result.AppendMatches(matches); 807 EXPECT_FALSE(result.TopMatchIsStandaloneVerbatimMatch()); 808 result.Reset(); 809 matches.clear(); 810 811 // Case 3: Top match is a verbatim match. 812 PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 1, &matches); 813 result.AppendMatches(matches); 814 EXPECT_TRUE(result.TopMatchIsStandaloneVerbatimMatch()); 815 result.Reset(); 816 matches.clear(); 817 818 // Case 4: Standalone verbatim match found in AutocompleteResult. 819 PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 1, &matches); 820 PopulateAutocompleteMatchesFromTestData(kNonVerbatimMatches, 1, &matches); 821 result.AppendMatches(matches); 822 EXPECT_TRUE(result.TopMatchIsStandaloneVerbatimMatch()); 823 result.Reset(); 824 matches.clear(); 825 826 // Case 5: Multiple verbatim matches found in AutocompleteResult. 827 PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 828 arraysize(kVerbatimMatches), 829 &matches); 830 result.AppendMatches(matches); 831 EXPECT_FALSE(result.ShouldHideTopMatch()); 832 } 833