• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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|.
PopulateAutocompleteMatchesFromTestData(const AutocompleteMatchTestData * data,size_t count,ACMatches * 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 
AutocompleteResultTest()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 
SetUp()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
PopulateAutocompleteMatch(const TestData & data,AutocompleteMatch * match)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
PopulateAutocompleteMatches(const TestData * data,size_t count,ACMatches * matches)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 
AssertResultMatches(const AutocompleteResult & result,const TestData * expected,size_t expected_count)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 
RunCopyOldMatchesTest(const TestData * last,size_t last_size,const TestData * current,size_t current_size,const TestData * expected,size_t expected_size)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.
TEST_F(AutocompleteResultTest,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.
TEST_F(AutocompleteResultTest,CopyOldMatches)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.
TEST_F(AutocompleteResultTest,CopyOldMatches2)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.
TEST_F(AutocompleteResultTest,SortAndCullEmptyDestinationURLs)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 
TEST_F(AutocompleteResultTest,SortAndCullDuplicateSearchURLs)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 
TEST_F(AutocompleteResultTest,SortAndCullWithMatchDups)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 
TEST_F(AutocompleteResultTest,SortAndCullWithDemotionsByType)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 
TEST_F(AutocompleteResultTest,SortAndCullWithMatchDupsAndDemotionsByType)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 
TEST_F(AutocompleteResultTest,SortAndCullReorderForDefaultMatch)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 
TEST_F(AutocompleteResultTest,SortAndCullWithDisableInlining)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 
TEST_F(AutocompleteResultTest,ShouldHideTopMatch)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 
TEST_F(AutocompleteResultTest,ShouldHideTopMatchAfterCopy)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 
TEST_F(AutocompleteResultTest,DoNotHideTopMatch_FieldTrialFlagDisabled)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 
TEST_F(AutocompleteResultTest,TopMatchIsStandaloneVerbatimMatch)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