Home | History | Annotate | Download | only in autocomplete
      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, &current_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