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
27using metrics::OmniboxEventProto;
28
29namespace {
30
31struct AutocompleteMatchTestData {
32  std::string destination_url;
33  AutocompleteMatch::Type type;
34};
35
36const 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
42const AutocompleteMatchTestData kNonVerbatimMatches[] = {
43  { "http://search-history/", AutocompleteMatchType::SEARCH_HISTORY },
44  { "http://history-title/", AutocompleteMatchType::HISTORY_TITLE },
45};
46
47// Adds |count| AutocompleteMatches to |matches|.
48void 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
66class 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
131void 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
144void 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
155void 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
171void 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.
199TEST_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.
233TEST_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.
253TEST_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.
279TEST_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
317TEST_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
363TEST_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
423TEST_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
470TEST_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
520TEST_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
569TEST_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
733TEST_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
765TEST_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
794TEST_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
807TEST_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