| // Copyright 2014 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "components/omnibox/autocomplete_result.h" | 
 |  | 
 | #include <vector> | 
 |  | 
 | #include "base/memory/scoped_ptr.h" | 
 | #include "base/metrics/field_trial.h" | 
 | #include "base/strings/string_number_conversions.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "components/metrics/proto/omnibox_event.pb.h" | 
 | #include "components/omnibox/autocomplete_input.h" | 
 | #include "components/omnibox/autocomplete_match.h" | 
 | #include "components/omnibox/autocomplete_match_type.h" | 
 | #include "components/omnibox/autocomplete_provider.h" | 
 | #include "components/omnibox/omnibox_field_trial.h" | 
 | #include "components/omnibox/test_scheme_classifier.h" | 
 | #include "components/search_engines/template_url_service.h" | 
 | #include "components/variations/entropy_provider.h" | 
 | #include "components/variations/variations_associated_data.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | using metrics::OmniboxEventProto; | 
 |  | 
 | namespace { | 
 |  | 
 | struct AutocompleteMatchTestData { | 
 |   std::string destination_url; | 
 |   AutocompleteMatch::Type type; | 
 | }; | 
 |  | 
 | const AutocompleteMatchTestData kVerbatimMatches[] = { | 
 |   { "http://search-what-you-typed/", | 
 |     AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, | 
 |   { "http://url-what-you-typed/", AutocompleteMatchType::URL_WHAT_YOU_TYPED }, | 
 | }; | 
 |  | 
 | const AutocompleteMatchTestData kNonVerbatimMatches[] = { | 
 |   { "http://search-history/", AutocompleteMatchType::SEARCH_HISTORY }, | 
 |   { "http://history-title/", AutocompleteMatchType::HISTORY_TITLE }, | 
 | }; | 
 |  | 
 | // Adds |count| AutocompleteMatches to |matches|. | 
 | void PopulateAutocompleteMatchesFromTestData( | 
 |     const AutocompleteMatchTestData* data, | 
 |     size_t count, | 
 |     ACMatches* matches) { | 
 |   ASSERT_TRUE(matches != NULL); | 
 |   for (size_t i = 0; i < count; ++i) { | 
 |     AutocompleteMatch match; | 
 |     match.destination_url = GURL(data[i].destination_url); | 
 |     match.relevance = | 
 |         matches->empty() ? 1300 : (matches->back().relevance - 100); | 
 |     match.allowed_to_be_default_match = true; | 
 |     match.type = data[i].type; | 
 |     matches->push_back(match); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | class AutocompleteResultTest : public testing::Test  { | 
 |  public: | 
 |   struct TestData { | 
 |     // Used to build a url for the AutocompleteMatch. The URL becomes | 
 |     // "http://" + ('a' + |url_id|) (e.g. an ID of 2 yields "http://b"). | 
 |     int url_id; | 
 |  | 
 |     // ID of the provider. | 
 |     int provider_id; | 
 |  | 
 |     // Relevance score. | 
 |     int relevance; | 
 |  | 
 |     // Duplicate matches. | 
 |     std::vector<AutocompleteMatch> duplicate_matches; | 
 |   }; | 
 |  | 
 |   AutocompleteResultTest() { | 
 |     // Destroy the existing FieldTrialList before creating a new one to avoid | 
 |     // a DCHECK. | 
 |     field_trial_list_.reset(); | 
 |     field_trial_list_.reset(new base::FieldTrialList( | 
 |         new metrics::SHA1EntropyProvider("foo"))); | 
 |     variations::testing::ClearAllVariationParams(); | 
 |   } | 
 |  | 
 |   void SetUp() override { | 
 |     template_url_service_.reset(new TemplateURLService(NULL, 0)); | 
 |     template_url_service_->Load(); | 
 |   } | 
 |  | 
 |   // Configures |match| from |data|. | 
 |   static void PopulateAutocompleteMatch(const TestData& data, | 
 |                                         AutocompleteMatch* match); | 
 |  | 
 |   // Adds |count| AutocompleteMatches to |matches|. | 
 |   static void PopulateAutocompleteMatches(const TestData* data, | 
 |                                           size_t count, | 
 |                                           ACMatches* matches); | 
 |  | 
 |   // Asserts that |result| has |expected_count| matches matching |expected|. | 
 |   void AssertResultMatches(const AutocompleteResult& result, | 
 |                            const TestData* expected, | 
 |                            size_t expected_count); | 
 |  | 
 |   // Creates an AutocompleteResult from |last| and |current|. The two are | 
 |   // merged by |CopyOldMatches| and compared by |AssertResultMatches|. | 
 |   void RunCopyOldMatchesTest(const TestData* last, size_t last_size, | 
 |                              const TestData* current, size_t current_size, | 
 |                              const TestData* expected, size_t expected_size); | 
 |  | 
 |  protected: | 
 |   scoped_ptr<TemplateURLService> template_url_service_; | 
 |  | 
 |  private: | 
 |   scoped_ptr<base::FieldTrialList> field_trial_list_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(AutocompleteResultTest); | 
 | }; | 
 |  | 
 | // static | 
 | void AutocompleteResultTest::PopulateAutocompleteMatch( | 
 |     const TestData& data, | 
 |     AutocompleteMatch* match) { | 
 |   match->provider = reinterpret_cast<AutocompleteProvider*>(data.provider_id); | 
 |   match->fill_into_edit = base::IntToString16(data.url_id); | 
 |   std::string url_id(1, data.url_id + 'a'); | 
 |   match->destination_url = GURL("http://" + url_id); | 
 |   match->relevance = data.relevance; | 
 |   match->allowed_to_be_default_match = true; | 
 |   match->duplicate_matches = data.duplicate_matches; | 
 | } | 
 |  | 
 | // static | 
 | void AutocompleteResultTest::PopulateAutocompleteMatches( | 
 |     const TestData* data, | 
 |     size_t count, | 
 |     ACMatches* matches) { | 
 |   for (size_t i = 0; i < count; ++i) { | 
 |     AutocompleteMatch match; | 
 |     PopulateAutocompleteMatch(data[i], &match); | 
 |     matches->push_back(match); | 
 |   } | 
 | } | 
 |  | 
 | void AutocompleteResultTest::AssertResultMatches( | 
 |     const AutocompleteResult& result, | 
 |     const TestData* expected, | 
 |     size_t expected_count) { | 
 |   ASSERT_EQ(expected_count, result.size()); | 
 |   for (size_t i = 0; i < expected_count; ++i) { | 
 |     AutocompleteMatch expected_match; | 
 |     PopulateAutocompleteMatch(expected[i], &expected_match); | 
 |     const AutocompleteMatch& match = *(result.begin() + i); | 
 |     EXPECT_EQ(expected_match.provider, match.provider) << i; | 
 |     EXPECT_EQ(expected_match.relevance, match.relevance) << i; | 
 |     EXPECT_EQ(expected_match.destination_url.spec(), | 
 |               match.destination_url.spec()) << i; | 
 |   } | 
 | } | 
 |  | 
 | void AutocompleteResultTest::RunCopyOldMatchesTest( | 
 |     const TestData* last, size_t last_size, | 
 |     const TestData* current, size_t current_size, | 
 |     const TestData* expected, size_t expected_size) { | 
 |   AutocompleteInput input(base::ASCIIToUTF16("a"), base::string16::npos, | 
 |                           std::string(), GURL(), | 
 |                           OmniboxEventProto::INVALID_SPEC, false, false, false, | 
 |                           true, | 
 |                           TestSchemeClassifier()); | 
 |  | 
 |   ACMatches last_matches; | 
 |   PopulateAutocompleteMatches(last, last_size, &last_matches); | 
 |   AutocompleteResult last_result; | 
 |   last_result.AppendMatches(last_matches); | 
 |   last_result.SortAndCull(input, template_url_service_.get()); | 
 |  | 
 |   ACMatches current_matches; | 
 |   PopulateAutocompleteMatches(current, current_size, ¤t_matches); | 
 |   AutocompleteResult current_result; | 
 |   current_result.AppendMatches(current_matches); | 
 |   current_result.SortAndCull(input, template_url_service_.get()); | 
 |   current_result.CopyOldMatches( | 
 |       input, last_result, template_url_service_.get()); | 
 |  | 
 |   AssertResultMatches(current_result, expected, expected_size); | 
 | } | 
 |  | 
 | // Assertion testing for AutocompleteResult::Swap. | 
 | TEST_F(AutocompleteResultTest, Swap) { | 
 |   AutocompleteResult r1; | 
 |   AutocompleteResult r2; | 
 |  | 
 |   // Swap with empty shouldn't do anything interesting. | 
 |   r1.Swap(&r2); | 
 |   EXPECT_EQ(r1.end(), r1.default_match()); | 
 |   EXPECT_EQ(r2.end(), r2.default_match()); | 
 |  | 
 |   // Swap with a single match. | 
 |   ACMatches matches; | 
 |   AutocompleteMatch match; | 
 |   match.relevance = 1; | 
 |   match.allowed_to_be_default_match = true; | 
 |   AutocompleteInput input(base::ASCIIToUTF16("a"), base::string16::npos, | 
 |                           std::string(), GURL(), | 
 |                           OmniboxEventProto::INVALID_SPEC, false, false, false, | 
 |                           true, TestSchemeClassifier()); | 
 |   matches.push_back(match); | 
 |   r1.AppendMatches(matches); | 
 |   r1.SortAndCull(input, template_url_service_.get()); | 
 |   EXPECT_EQ(r1.begin(), r1.default_match()); | 
 |   EXPECT_EQ("http://a/", r1.alternate_nav_url().spec()); | 
 |   r1.Swap(&r2); | 
 |   EXPECT_TRUE(r1.empty()); | 
 |   EXPECT_EQ(r1.end(), r1.default_match()); | 
 |   EXPECT_TRUE(r1.alternate_nav_url().is_empty()); | 
 |   ASSERT_FALSE(r2.empty()); | 
 |   EXPECT_EQ(r2.begin(), r2.default_match()); | 
 |   EXPECT_EQ("http://a/", r2.alternate_nav_url().spec()); | 
 | } | 
 |  | 
 | // Tests that if the new results have a lower max relevance score than last, | 
 | // any copied results have their relevance shifted down. | 
 | TEST_F(AutocompleteResultTest, CopyOldMatches) { | 
 |   TestData last[] = { | 
 |     { 0, 0, 1000 }, | 
 |     { 1, 0, 500 }, | 
 |   }; | 
 |   TestData current[] = { | 
 |     { 2, 0, 400 }, | 
 |   }; | 
 |   TestData result[] = { | 
 |     { 2, 0, 400 }, | 
 |     { 1, 0, 399 }, | 
 |   }; | 
 |  | 
 |   ASSERT_NO_FATAL_FAILURE(RunCopyOldMatchesTest(last, arraysize(last), | 
 |                                                 current, arraysize(current), | 
 |                                                 result, arraysize(result))); | 
 | } | 
 |  | 
 | // Tests that matches are copied correctly from two distinct providers. | 
 | TEST_F(AutocompleteResultTest, CopyOldMatches2) { | 
 |   TestData last[] = { | 
 |     { 0, 0, 1000 }, | 
 |     { 1, 1, 500 }, | 
 |     { 2, 0, 400 }, | 
 |     { 3, 1, 300 }, | 
 |   }; | 
 |   TestData current[] = { | 
 |     { 4, 0, 1100 }, | 
 |     { 5, 1, 550 }, | 
 |   }; | 
 |   TestData result[] = { | 
 |     { 4, 0, 1100 }, | 
 |     { 5, 1, 550 }, | 
 |     { 2, 0, 400 }, | 
 |     { 3, 1, 300 }, | 
 |   }; | 
 |  | 
 |   ASSERT_NO_FATAL_FAILURE(RunCopyOldMatchesTest(last, arraysize(last), | 
 |                                                 current, arraysize(current), | 
 |                                                 result, arraysize(result))); | 
 | } | 
 |  | 
 | // Tests that matches with empty destination URLs aren't treated as duplicates | 
 | // and culled. | 
 | TEST_F(AutocompleteResultTest, SortAndCullEmptyDestinationURLs) { | 
 |   TestData data[] = { | 
 |     { 1, 0, 500 }, | 
 |     { 0, 0, 1100 }, | 
 |     { 1, 0, 1000 }, | 
 |     { 0, 0, 1300 }, | 
 |     { 0, 0, 1200 }, | 
 |   }; | 
 |  | 
 |   ACMatches matches; | 
 |   PopulateAutocompleteMatches(data, arraysize(data), &matches); | 
 |   matches[1].destination_url = GURL(); | 
 |   matches[3].destination_url = GURL(); | 
 |   matches[4].destination_url = GURL(); | 
 |  | 
 |   AutocompleteResult result; | 
 |   result.AppendMatches(matches); | 
 |   AutocompleteInput input(base::string16(), base::string16::npos, | 
 |                           std::string(), GURL(), | 
 |                           OmniboxEventProto::INVALID_SPEC, false, false, false, | 
 |                           true, | 
 |                           TestSchemeClassifier()); | 
 |   result.SortAndCull(input, template_url_service_.get()); | 
 |  | 
 |   // Of the two results with the same non-empty destination URL, the | 
 |   // lower-relevance one should be dropped.  All of the results with empty URLs | 
 |   // should be kept. | 
 |   ASSERT_EQ(4U, result.size()); | 
 |   EXPECT_TRUE(result.match_at(0)->destination_url.is_empty()); | 
 |   EXPECT_EQ(1300, result.match_at(0)->relevance); | 
 |   EXPECT_TRUE(result.match_at(1)->destination_url.is_empty()); | 
 |   EXPECT_EQ(1200, result.match_at(1)->relevance); | 
 |   EXPECT_TRUE(result.match_at(2)->destination_url.is_empty()); | 
 |   EXPECT_EQ(1100, result.match_at(2)->relevance); | 
 |   EXPECT_EQ("http://b/", result.match_at(3)->destination_url.spec()); | 
 |   EXPECT_EQ(1000, result.match_at(3)->relevance); | 
 | } | 
 |  | 
 | TEST_F(AutocompleteResultTest, SortAndCullDuplicateSearchURLs) { | 
 |   // Register a template URL that corresponds to 'foo' search engine. | 
 |   TemplateURLData url_data; | 
 |   url_data.short_name = base::ASCIIToUTF16("unittest"); | 
 |   url_data.SetKeyword(base::ASCIIToUTF16("foo")); | 
 |   url_data.SetURL("http://www.foo.com/s?q={searchTerms}"); | 
 |   template_url_service_.get()->Add(new TemplateURL(url_data)); | 
 |  | 
 |   TestData data[] = { | 
 |     { 0, 0, 1300 }, | 
 |     { 1, 0, 1200 }, | 
 |     { 2, 0, 1100 }, | 
 |     { 3, 0, 1000 }, | 
 |     { 4, 1, 900 }, | 
 |   }; | 
 |  | 
 |   ACMatches matches; | 
 |   PopulateAutocompleteMatches(data, arraysize(data), &matches); | 
 |   matches[0].destination_url = GURL("http://www.foo.com/s?q=foo"); | 
 |   matches[1].destination_url = GURL("http://www.foo.com/s?q=foo2"); | 
 |   matches[2].destination_url = GURL("http://www.foo.com/s?q=foo&oq=f"); | 
 |   matches[3].destination_url = GURL("http://www.foo.com/s?q=foo&aqs=0"); | 
 |   matches[4].destination_url = GURL("http://www.foo.com/"); | 
 |  | 
 |   AutocompleteResult result; | 
 |   result.AppendMatches(matches); | 
 |   AutocompleteInput input(base::string16(), base::string16::npos, | 
 |                           std::string(), GURL(), | 
 |                           OmniboxEventProto::INVALID_SPEC, false, false, false, | 
 |                           true, | 
 |                           TestSchemeClassifier()); | 
 |   result.SortAndCull(input, template_url_service_.get()); | 
 |  | 
 |   // We expect the 3rd and 4th results to be removed. | 
 |   ASSERT_EQ(3U, result.size()); | 
 |   EXPECT_EQ("http://www.foo.com/s?q=foo", | 
 |             result.match_at(0)->destination_url.spec()); | 
 |   EXPECT_EQ(1300, result.match_at(0)->relevance); | 
 |   EXPECT_EQ("http://www.foo.com/s?q=foo2", | 
 |             result.match_at(1)->destination_url.spec()); | 
 |   EXPECT_EQ(1200, result.match_at(1)->relevance); | 
 |   EXPECT_EQ("http://www.foo.com/", | 
 |             result.match_at(2)->destination_url.spec()); | 
 |   EXPECT_EQ(900, result.match_at(2)->relevance); | 
 | } | 
 |  | 
 | TEST_F(AutocompleteResultTest, SortAndCullWithMatchDups) { | 
 |   // Register a template URL that corresponds to 'foo' search engine. | 
 |   TemplateURLData url_data; | 
 |   url_data.short_name = base::ASCIIToUTF16("unittest"); | 
 |   url_data.SetKeyword(base::ASCIIToUTF16("foo")); | 
 |   url_data.SetURL("http://www.foo.com/s?q={searchTerms}"); | 
 |   template_url_service_.get()->Add(new TemplateURL(url_data)); | 
 |  | 
 |   AutocompleteMatch dup_match; | 
 |   dup_match.destination_url = GURL("http://www.foo.com/s?q=foo&oq=dup"); | 
 |   std::vector<AutocompleteMatch> dups; | 
 |   dups.push_back(dup_match); | 
 |  | 
 |   TestData data[] = { | 
 |     { 0, 0, 1300, dups }, | 
 |     { 1, 0, 1200 }, | 
 |     { 2, 0, 1100 }, | 
 |     { 3, 0, 1000, dups }, | 
 |     { 4, 1, 900 }, | 
 |     { 5, 0, 800 }, | 
 |   }; | 
 |  | 
 |   ACMatches matches; | 
 |   PopulateAutocompleteMatches(data, arraysize(data), &matches); | 
 |   matches[0].destination_url = GURL("http://www.foo.com/s?q=foo"); | 
 |   matches[1].destination_url = GURL("http://www.foo.com/s?q=foo2"); | 
 |   matches[2].destination_url = GURL("http://www.foo.com/s?q=foo&oq=f"); | 
 |   matches[3].destination_url = GURL("http://www.foo.com/s?q=foo&aqs=0"); | 
 |   matches[4].destination_url = GURL("http://www.foo.com/"); | 
 |   matches[5].destination_url = GURL("http://www.foo.com/s?q=foo2&oq=f"); | 
 |  | 
 |   AutocompleteResult result; | 
 |   result.AppendMatches(matches); | 
 |   AutocompleteInput input(base::string16(), base::string16::npos, | 
 |                           std::string(), GURL(), | 
 |                           OmniboxEventProto::INVALID_SPEC, false, false, false, | 
 |                           true, | 
 |                           TestSchemeClassifier()); | 
 |   result.SortAndCull(input, template_url_service_.get()); | 
 |  | 
 |   // Expect 3 unique results after SortAndCull(). | 
 |   ASSERT_EQ(3U, result.size()); | 
 |  | 
 |   // Check that 3rd and 4th result got added to the first result as dups | 
 |   // and also duplicates of the 4th match got copied. | 
 |   ASSERT_EQ(4U, result.match_at(0)->duplicate_matches.size()); | 
 |   const AutocompleteMatch* first_match = result.match_at(0); | 
 |   EXPECT_EQ(matches[2].destination_url, | 
 |             first_match->duplicate_matches.at(1).destination_url); | 
 |   EXPECT_EQ(dup_match.destination_url, | 
 |             first_match->duplicate_matches.at(2).destination_url); | 
 |   EXPECT_EQ(matches[3].destination_url, | 
 |             first_match->duplicate_matches.at(3).destination_url); | 
 |  | 
 |   // Check that 6th result started a new list of dups for the second result. | 
 |   ASSERT_EQ(1U, result.match_at(1)->duplicate_matches.size()); | 
 |   EXPECT_EQ(matches[5].destination_url, | 
 |             result.match_at(1)->duplicate_matches.at(0).destination_url); | 
 | } | 
 |  | 
 | TEST_F(AutocompleteResultTest, SortAndCullWithDemotionsByType) { | 
 |   // Add some matches. | 
 |   ACMatches matches; | 
 |   const AutocompleteMatchTestData data[] = { | 
 |     { "http://history-url/", AutocompleteMatchType::HISTORY_URL }, | 
 |     { "http://search-what-you-typed/", | 
 |       AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, | 
 |     { "http://history-title/", AutocompleteMatchType::HISTORY_TITLE }, | 
 |     { "http://search-history/", AutocompleteMatchType::SEARCH_HISTORY }, | 
 |   }; | 
 |   PopulateAutocompleteMatchesFromTestData(data, arraysize(data), &matches); | 
 |  | 
 |   // Demote the search history match relevance score. | 
 |   matches.back().relevance = 500; | 
 |  | 
 |   // Add a rule demoting history-url and killing history-title. | 
 |   { | 
 |     std::map<std::string, std::string> params; | 
 |     params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":3:*"] = | 
 |         "1:50,7:100,2:0";  // 3 == HOME_PAGE | 
 |     ASSERT_TRUE(variations::AssociateVariationParams( | 
 |         OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); | 
 |   } | 
 |   base::FieldTrialList::CreateFieldTrial( | 
 |       OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); | 
 |  | 
 |   AutocompleteResult result; | 
 |   result.AppendMatches(matches); | 
 |   AutocompleteInput input(base::string16(), base::string16::npos, | 
 |                           std::string(), GURL(), | 
 |                           OmniboxEventProto::HOME_PAGE, false, false, false, | 
 |                           true, | 
 |                           TestSchemeClassifier()); | 
 |   result.SortAndCull(input, template_url_service_.get()); | 
 |  | 
 |   // Check the new ordering.  The history-title results should be omitted. | 
 |   // We cannot check relevance scores because the matches are sorted by | 
 |   // demoted relevance but the actual relevance scores are not modified. | 
 |   ASSERT_EQ(3u, result.size()); | 
 |   EXPECT_EQ("http://search-what-you-typed/", | 
 |             result.match_at(0)->destination_url.spec()); | 
 |   EXPECT_EQ("http://history-url/", | 
 |             result.match_at(1)->destination_url.spec()); | 
 |   EXPECT_EQ("http://search-history/", | 
 |             result.match_at(2)->destination_url.spec()); | 
 | } | 
 |  | 
 | TEST_F(AutocompleteResultTest, SortAndCullWithMatchDupsAndDemotionsByType) { | 
 |   // Add some matches. | 
 |   ACMatches matches; | 
 |   const AutocompleteMatchTestData data[] = { | 
 |     { "http://search-what-you-typed/", | 
 |       AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED }, | 
 |     { "http://dup-url/", AutocompleteMatchType::HISTORY_URL }, | 
 |     { "http://dup-url/", AutocompleteMatchType::NAVSUGGEST }, | 
 |     { "http://search-url/", AutocompleteMatchType::SEARCH_SUGGEST }, | 
 |     { "http://history-url/", AutocompleteMatchType::HISTORY_URL }, | 
 |   }; | 
 |   PopulateAutocompleteMatchesFromTestData(data, arraysize(data), &matches); | 
 |  | 
 |   // Add a rule demoting HISTORY_URL. | 
 |   { | 
 |     std::map<std::string, std::string> params; | 
 |     params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":8:*"] = | 
 |         "1:50";  // 8 == INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS | 
 |     ASSERT_TRUE(variations::AssociateVariationParams( | 
 |         OmniboxFieldTrial::kBundledExperimentFieldTrialName, "C", params)); | 
 |   } | 
 |   base::FieldTrialList::CreateFieldTrial( | 
 |       OmniboxFieldTrial::kBundledExperimentFieldTrialName, "C"); | 
 |  | 
 |   { | 
 |     AutocompleteResult result; | 
 |     result.AppendMatches(matches); | 
 |     AutocompleteInput input( | 
 |         base::string16(), base::string16::npos, std::string(), GURL(), | 
 |         OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS, false, | 
 |         false, false, true, | 
 |         TestSchemeClassifier()); | 
 |     result.SortAndCull(input, template_url_service_.get()); | 
 |  | 
 |     // The NAVSUGGEST dup-url stay above search-url since the navsuggest | 
 |     // variant should not be demoted. | 
 |     ASSERT_EQ(4u, result.size()); | 
 |     EXPECT_EQ("http://search-what-you-typed/", | 
 |               result.match_at(0)->destination_url.spec()); | 
 |     EXPECT_EQ("http://dup-url/", | 
 |               result.match_at(1)->destination_url.spec()); | 
 |     EXPECT_EQ(AutocompleteMatchType::NAVSUGGEST, | 
 |               result.match_at(1)->type); | 
 |     EXPECT_EQ("http://search-url/", | 
 |               result.match_at(2)->destination_url.spec()); | 
 |     EXPECT_EQ("http://history-url/", | 
 |               result.match_at(3)->destination_url.spec()); | 
 |   } | 
 | } | 
 |  | 
 | TEST_F(AutocompleteResultTest, SortAndCullReorderForDefaultMatch) { | 
 |   TestData data[] = { | 
 |     { 0, 0, 1300 }, | 
 |     { 1, 0, 1200 }, | 
 |     { 2, 0, 1100 }, | 
 |     { 3, 0, 1000 } | 
 |   }; | 
 |  | 
 |   { | 
 |     // Check that reorder doesn't do anything if the top result | 
 |     // is already a legal default match (which is the default from | 
 |     // PopulateAutocompleteMatches()). | 
 |     ACMatches matches; | 
 |     PopulateAutocompleteMatches(data, arraysize(data), &matches); | 
 |     AutocompleteResult result; | 
 |     result.AppendMatches(matches); | 
 |     AutocompleteInput input(base::string16(), base::string16::npos, | 
 |                             std::string(), GURL(), | 
 |                             OmniboxEventProto::HOME_PAGE, false, false, false, | 
 |                             true, | 
 |                             TestSchemeClassifier()); | 
 |     result.SortAndCull(input, template_url_service_.get()); | 
 |     AssertResultMatches(result, data, 4); | 
 |   } | 
 |  | 
 |   { | 
 |     // Check that reorder swaps up a result appropriately. | 
 |     ACMatches matches; | 
 |     PopulateAutocompleteMatches(data, arraysize(data), &matches); | 
 |     matches[0].allowed_to_be_default_match = false; | 
 |     matches[1].allowed_to_be_default_match = false; | 
 |     AutocompleteResult result; | 
 |     result.AppendMatches(matches); | 
 |     AutocompleteInput input(base::string16(), base::string16::npos, | 
 |                             std::string(), GURL(), | 
 |                             OmniboxEventProto::HOME_PAGE, false, false, false, | 
 |                             true, | 
 |                             TestSchemeClassifier()); | 
 |     result.SortAndCull(input, template_url_service_.get()); | 
 |     ASSERT_EQ(4U, result.size()); | 
 |     EXPECT_EQ("http://c/", result.match_at(0)->destination_url.spec()); | 
 |     EXPECT_EQ("http://a/", result.match_at(1)->destination_url.spec()); | 
 |     EXPECT_EQ("http://b/", result.match_at(2)->destination_url.spec()); | 
 |     EXPECT_EQ("http://d/", result.match_at(3)->destination_url.spec()); | 
 |   } | 
 | } | 
 |  | 
 | TEST_F(AutocompleteResultTest, ShouldHideTopMatch) { | 
 |   base::FieldTrialList::CreateFieldTrial("InstantExtended", | 
 |                                          "Group1 hide_verbatim:1"); | 
 |   ACMatches matches; | 
 |  | 
 |   // Case 1: Top match is a verbatim match. | 
 |   PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 1, &matches); | 
 |   AutocompleteResult result; | 
 |   result.AppendMatches(matches); | 
 |   EXPECT_TRUE(result.ShouldHideTopMatch()); | 
 |   matches.clear(); | 
 |   result.Reset(); | 
 |  | 
 |   // Case 2: If the verbatim first match is followed by another verbatim match, | 
 |   // don't hide the top verbatim match. | 
 |   PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, | 
 |                                           arraysize(kVerbatimMatches), | 
 |                                           &matches); | 
 |   result.AppendMatches(matches); | 
 |   EXPECT_FALSE(result.ShouldHideTopMatch()); | 
 |   matches.clear(); | 
 |   result.Reset(); | 
 |  | 
 |   // Case 3: Top match is not a verbatim match. Do not hide the top match. | 
 |   PopulateAutocompleteMatchesFromTestData(kNonVerbatimMatches, 1, &matches); | 
 |   PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, | 
 |                                           arraysize(kVerbatimMatches), | 
 |                                           &matches); | 
 |   result.AppendMatches(matches); | 
 |   EXPECT_FALSE(result.ShouldHideTopMatch()); | 
 | } | 
 |  | 
 | TEST_F(AutocompleteResultTest, ShouldHideTopMatchAfterCopy) { | 
 |   base::FieldTrialList::CreateFieldTrial("InstantExtended", | 
 |                                          "Group1 hide_verbatim:1"); | 
 |   ACMatches matches; | 
 |  | 
 |   // Case 1: Top match is a verbatim match followed by only copied matches. | 
 |   PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, | 
 |                                           arraysize(kVerbatimMatches), | 
 |                                           &matches); | 
 |   for (size_t i = 1; i < arraysize(kVerbatimMatches); ++i) | 
 |     matches[i].from_previous = true; | 
 |   AutocompleteResult result; | 
 |   result.AppendMatches(matches); | 
 |   EXPECT_TRUE(result.ShouldHideTopMatch()); | 
 |   result.Reset(); | 
 |  | 
 |   // Case 2: The copied matches are then followed by a non-verbatim match. | 
 |   PopulateAutocompleteMatchesFromTestData(kNonVerbatimMatches, 1, &matches); | 
 |   result.AppendMatches(matches); | 
 |   EXPECT_TRUE(result.ShouldHideTopMatch()); | 
 |   result.Reset(); | 
 |  | 
 |   // Case 3: The copied matches are instead followed by a verbatim match. | 
 |   matches.back().from_previous = true; | 
 |   PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 1, &matches); | 
 |   result.AppendMatches(matches); | 
 |   EXPECT_FALSE(result.ShouldHideTopMatch()); | 
 | } | 
 |  | 
 | TEST_F(AutocompleteResultTest, DoNotHideTopMatch_FieldTrialFlagDisabled) { | 
 |   // This test config is identical to ShouldHideTopMatch test ("Case 1") except | 
 |   // that the "hide_verbatim" flag is disabled in the field trials. | 
 |   base::FieldTrialList::CreateFieldTrial("InstantExtended", | 
 |                                          "Group1 hide_verbatim:0"); | 
 |   ACMatches matches; | 
 |   PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 1, &matches); | 
 |   AutocompleteResult result; | 
 |   result.AppendMatches(matches); | 
 |   // Field trial flag "hide_verbatim" is disabled. Do not hide top match. | 
 |   EXPECT_FALSE(result.ShouldHideTopMatch()); | 
 | } | 
 |  | 
 | TEST_F(AutocompleteResultTest, TopMatchIsStandaloneVerbatimMatch) { | 
 |   ACMatches matches; | 
 |   AutocompleteResult result; | 
 |   result.AppendMatches(matches); | 
 |  | 
 |   // Case 1: Result set is empty. | 
 |   EXPECT_FALSE(result.TopMatchIsStandaloneVerbatimMatch()); | 
 |  | 
 |   // Case 2: Top match is not a verbatim match. | 
 |   PopulateAutocompleteMatchesFromTestData(kNonVerbatimMatches, 1, &matches); | 
 |   result.AppendMatches(matches); | 
 |   EXPECT_FALSE(result.TopMatchIsStandaloneVerbatimMatch()); | 
 |   result.Reset(); | 
 |   matches.clear(); | 
 |  | 
 |   // Case 3: Top match is a verbatim match. | 
 |   PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 1, &matches); | 
 |   result.AppendMatches(matches); | 
 |   EXPECT_TRUE(result.TopMatchIsStandaloneVerbatimMatch()); | 
 |   result.Reset(); | 
 |   matches.clear(); | 
 |  | 
 |   // Case 4: Standalone verbatim match found in AutocompleteResult. | 
 |   PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 1, &matches); | 
 |   PopulateAutocompleteMatchesFromTestData(kNonVerbatimMatches, 1, &matches); | 
 |   result.AppendMatches(matches); | 
 |   EXPECT_TRUE(result.TopMatchIsStandaloneVerbatimMatch()); | 
 |   result.Reset(); | 
 |   matches.clear(); | 
 |  | 
 |   // Case 5: Multiple verbatim matches found in AutocompleteResult. | 
 |   PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, | 
 |                                           arraysize(kVerbatimMatches), | 
 |                                           &matches); | 
 |   result.AppendMatches(matches); | 
 |   EXPECT_FALSE(result.ShouldHideTopMatch()); | 
 | } |