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