- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / autocomplete / autocomplete_result_unittest.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/autocomplete/autocomplete_result.h"
6
7 #include "base/memory/scoped_ptr.h"
8 #include "base/metrics/field_trial.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/autocomplete/autocomplete_input.h"
13 #include "chrome/browser/autocomplete/autocomplete_match.h"
14 #include "chrome/browser/autocomplete/autocomplete_provider.h"
15 #include "chrome/browser/omnibox/omnibox_field_trial.h"
16 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
17 #include "chrome/browser/search_engines/template_url_service.h"
18 #include "chrome/browser/search_engines/template_url_service_test_util.h"
19 #include "chrome/common/autocomplete_match_type.h"
20 #include "chrome/common/metrics/variations/variations_util.h"
21 #include "chrome/test/base/testing_profile.h"
22 #include "components/variations/entropy_provider.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 class AutocompleteResultTest : public testing::Test  {
26  public:
27   struct TestData {
28     // Used to build a url for the AutocompleteMatch. The URL becomes
29     // "http://" + ('a' + |url_id|) (e.g. an ID of 2 yields "http://b").
30     int url_id;
31
32     // ID of the provider.
33     int provider_id;
34
35     // Relevance score.
36     int relevance;
37   };
38
39   AutocompleteResultTest() {
40     // Destroy the existing FieldTrialList before creating a new one to avoid
41     // a DCHECK.
42     field_trial_list_.reset();
43     field_trial_list_.reset(new base::FieldTrialList(
44         new metrics::SHA1EntropyProvider("foo")));
45     chrome_variations::testing::ClearAllVariationParams();
46   }
47
48   virtual void SetUp() OVERRIDE {
49 #if defined(OS_ANDROID)
50     TemplateURLPrepopulateData::InitCountryCode(
51         std::string() /* unknown country code */);
52 #endif
53     test_util_.SetUp();
54     test_util_.VerifyLoad();
55   }
56
57   virtual void TearDown() OVERRIDE {
58     test_util_.TearDown();
59   }
60
61   // Configures |match| from |data|.
62   static void PopulateAutocompleteMatch(const TestData& data,
63                                         AutocompleteMatch* match);
64
65   // Adds |count| AutocompleteMatches to |matches|.
66   static void PopulateAutocompleteMatches(const TestData* data,
67                                           size_t count,
68                                           ACMatches* matches);
69
70   // Asserts that |result| has |expected_count| matches matching |expected|.
71   void AssertResultMatches(const AutocompleteResult& result,
72                            const TestData* expected,
73                            size_t expected_count);
74
75   // Creates an AutocompleteResult from |last| and |current|. The two are
76   // merged by |CopyOldMatches| and compared by |AssertResultMatches|.
77   void RunCopyOldMatchesTest(const TestData* last, size_t last_size,
78                              const TestData* current, size_t current_size,
79                              const TestData* expected, size_t expected_size);
80
81  protected:
82   TemplateURLServiceTestUtil test_util_;
83
84  private:
85   scoped_ptr<base::FieldTrialList> field_trial_list_;
86
87   DISALLOW_COPY_AND_ASSIGN(AutocompleteResultTest);
88 };
89
90 // static
91 void AutocompleteResultTest::PopulateAutocompleteMatch(
92     const TestData& data,
93     AutocompleteMatch* match) {
94   match->provider = reinterpret_cast<AutocompleteProvider*>(data.provider_id);
95   match->fill_into_edit = base::IntToString16(data.url_id);
96   std::string url_id(1, data.url_id + 'a');
97   match->destination_url = GURL("http://" + url_id);
98   match->relevance = data.relevance;
99   match->allowed_to_be_default_match = true;
100 }
101
102 // static
103 void AutocompleteResultTest::PopulateAutocompleteMatches(
104     const TestData* data,
105     size_t count,
106     ACMatches* matches) {
107   for (size_t i = 0; i < count; ++i) {
108     AutocompleteMatch match;
109     PopulateAutocompleteMatch(data[i], &match);
110     matches->push_back(match);
111   }
112 }
113
114 void AutocompleteResultTest::AssertResultMatches(
115     const AutocompleteResult& result,
116     const TestData* expected,
117     size_t expected_count) {
118   ASSERT_EQ(expected_count, result.size());
119   for (size_t i = 0; i < expected_count; ++i) {
120     AutocompleteMatch expected_match;
121     PopulateAutocompleteMatch(expected[i], &expected_match);
122     const AutocompleteMatch& match = *(result.begin() + i);
123     EXPECT_EQ(expected_match.provider, match.provider) << i;
124     EXPECT_EQ(expected_match.relevance, match.relevance) << i;
125     EXPECT_EQ(expected_match.destination_url.spec(),
126               match.destination_url.spec()) << i;
127   }
128 }
129
130 void AutocompleteResultTest::RunCopyOldMatchesTest(
131     const TestData* last, size_t last_size,
132     const TestData* current, size_t current_size,
133     const TestData* expected, size_t expected_size) {
134   AutocompleteInput input(ASCIIToUTF16("a"), string16::npos, string16(), GURL(),
135                           AutocompleteInput::INVALID_SPEC, false, false, false,
136                           AutocompleteInput::ALL_MATCHES);
137
138   ACMatches last_matches;
139   PopulateAutocompleteMatches(last, last_size, &last_matches);
140   AutocompleteResult last_result;
141   last_result.AppendMatches(last_matches);
142   last_result.SortAndCull(input, test_util_.profile());
143
144   ACMatches current_matches;
145   PopulateAutocompleteMatches(current, current_size, &current_matches);
146   AutocompleteResult current_result;
147   current_result.AppendMatches(current_matches);
148   current_result.SortAndCull(input, test_util_.profile());
149   current_result.CopyOldMatches(input, last_result, test_util_.profile());
150
151   AssertResultMatches(current_result, expected, expected_size);
152 }
153
154 // Assertion testing for AutocompleteResult::Swap.
155 TEST_F(AutocompleteResultTest, Swap) {
156   AutocompleteResult r1;
157   AutocompleteResult r2;
158
159   // Swap with empty shouldn't do anything interesting.
160   r1.Swap(&r2);
161   EXPECT_EQ(r1.end(), r1.default_match());
162   EXPECT_EQ(r2.end(), r2.default_match());
163
164   // Swap with a single match.
165   ACMatches matches;
166   AutocompleteMatch match;
167   match.relevance = 1;
168   match.allowed_to_be_default_match = true;
169   AutocompleteInput input(ASCIIToUTF16("a"), string16::npos, string16(), GURL(),
170                           AutocompleteInput::INVALID_SPEC, false, false, false,
171                           AutocompleteInput::ALL_MATCHES);
172   matches.push_back(match);
173   r1.AppendMatches(matches);
174   r1.SortAndCull(input, test_util_.profile());
175   EXPECT_EQ(r1.begin(), r1.default_match());
176   EXPECT_EQ("http://a/", r1.alternate_nav_url().spec());
177   r1.Swap(&r2);
178   EXPECT_TRUE(r1.empty());
179   EXPECT_EQ(r1.end(), r1.default_match());
180   EXPECT_TRUE(r1.alternate_nav_url().is_empty());
181   ASSERT_FALSE(r2.empty());
182   EXPECT_EQ(r2.begin(), r2.default_match());
183   EXPECT_EQ("http://a/", r2.alternate_nav_url().spec());
184 }
185
186 // Tests that if the new results have a lower max relevance score than last,
187 // any copied results have their relevance shifted down.
188 TEST_F(AutocompleteResultTest, CopyOldMatches) {
189   TestData last[] = {
190     { 0, 0, 1000 },
191     { 1, 0, 500 },
192   };
193   TestData current[] = {
194     { 2, 0, 400 },
195   };
196   TestData result[] = {
197     { 2, 0, 400 },
198     { 1, 0, 399 },
199   };
200
201   ASSERT_NO_FATAL_FAILURE(
202       RunCopyOldMatchesTest(last, ARRAYSIZE_UNSAFE(last),
203                             current, ARRAYSIZE_UNSAFE(current),
204                             result, ARRAYSIZE_UNSAFE(result)));
205 }
206
207 // Tests that matches are copied correctly from two distinct providers.
208 TEST_F(AutocompleteResultTest, CopyOldMatches2) {
209   TestData last[] = {
210     { 0, 0, 1000 },
211     { 1, 1, 500 },
212     { 2, 0, 400 },
213     { 3, 1, 300 },
214   };
215   TestData current[] = {
216     { 4, 0, 1100 },
217     { 5, 1, 550 },
218   };
219   TestData result[] = {
220     { 4, 0, 1100 },
221     { 5, 1, 550 },
222     { 2, 0, 400 },
223     { 3, 1, 300 },
224   };
225
226   ASSERT_NO_FATAL_FAILURE(
227       RunCopyOldMatchesTest(last, ARRAYSIZE_UNSAFE(last),
228                             current, ARRAYSIZE_UNSAFE(current),
229                             result, ARRAYSIZE_UNSAFE(result)));
230 }
231
232 // Tests that matches with empty destination URLs aren't treated as duplicates
233 // and culled.
234 TEST_F(AutocompleteResultTest, SortAndCullEmptyDestinationURLs) {
235   TestData data[] = {
236     { 1, 0, 500 },
237     { 0, 0, 1100 },
238     { 1, 0, 1000 },
239     { 0, 0, 1300 },
240     { 0, 0, 1200 },
241   };
242
243   ACMatches matches;
244   PopulateAutocompleteMatches(data, arraysize(data), &matches);
245   matches[1].destination_url = GURL();
246   matches[3].destination_url = GURL();
247   matches[4].destination_url = GURL();
248
249   AutocompleteResult result;
250   result.AppendMatches(matches);
251   AutocompleteInput input(string16(), string16::npos, string16(), GURL(),
252                           AutocompleteInput::INVALID_SPEC, false, false, false,
253                           AutocompleteInput::ALL_MATCHES);
254   result.SortAndCull(input, test_util_.profile());
255
256   // Of the two results with the same non-empty destination URL, the
257   // lower-relevance one should be dropped.  All of the results with empty URLs
258   // should be kept.
259   ASSERT_EQ(4U, result.size());
260   EXPECT_TRUE(result.match_at(0)->destination_url.is_empty());
261   EXPECT_EQ(1300, result.match_at(0)->relevance);
262   EXPECT_TRUE(result.match_at(1)->destination_url.is_empty());
263   EXPECT_EQ(1200, result.match_at(1)->relevance);
264   EXPECT_TRUE(result.match_at(2)->destination_url.is_empty());
265   EXPECT_EQ(1100, result.match_at(2)->relevance);
266   EXPECT_EQ("http://b/", result.match_at(3)->destination_url.spec());
267   EXPECT_EQ(1000, result.match_at(3)->relevance);
268 }
269
270 TEST_F(AutocompleteResultTest, SortAndCullDuplicateSearchURLs) {
271   // Register a template URL that corresponds to 'foo' search engine.
272   TemplateURLData url_data;
273   url_data.short_name = ASCIIToUTF16("unittest");
274   url_data.SetKeyword(ASCIIToUTF16("foo"));
275   url_data.SetURL("http://www.foo.com/s?q={searchTerms}");
276   test_util_.model()->Add(new TemplateURL(test_util_.profile(), url_data));
277
278   TestData data[] = {
279     { 0, 0, 1300 },
280     { 1, 0, 1200 },
281     { 2, 0, 1100 },
282     { 3, 0, 1000 },
283     { 4, 1, 900 },
284   };
285
286   ACMatches matches;
287   PopulateAutocompleteMatches(data, arraysize(data), &matches);
288   matches[0].destination_url = GURL("http://www.foo.com/s?q=foo");
289   matches[1].destination_url = GURL("http://www.foo.com/s?q=foo2");
290   matches[2].destination_url = GURL("http://www.foo.com/s?q=foo&oq=f");
291   matches[3].destination_url = GURL("http://www.foo.com/s?q=foo&aqs=0");
292   matches[4].destination_url = GURL("http://www.foo.com/");
293
294   AutocompleteResult result;
295   result.AppendMatches(matches);
296   AutocompleteInput input(string16(), string16::npos, string16(), GURL(),
297                           AutocompleteInput::INVALID_SPEC, false, false, false,
298                           AutocompleteInput::ALL_MATCHES);
299   result.SortAndCull(input, test_util_.profile());
300
301   // We expect the 3rd and 4th results to be removed.
302   ASSERT_EQ(3U, result.size());
303   EXPECT_EQ("http://www.foo.com/s?q=foo",
304             result.match_at(0)->destination_url.spec());
305   EXPECT_EQ(1300, result.match_at(0)->relevance);
306   EXPECT_EQ("http://www.foo.com/s?q=foo2",
307             result.match_at(1)->destination_url.spec());
308   EXPECT_EQ(1200, result.match_at(1)->relevance);
309   EXPECT_EQ("http://www.foo.com/",
310             result.match_at(2)->destination_url.spec());
311   EXPECT_EQ(900, result.match_at(2)->relevance);
312 }
313
314 TEST_F(AutocompleteResultTest, SortAndCullWithDemotionsByType) {
315   // Add some matches.
316   ACMatches matches;
317   {
318     AutocompleteMatch match;
319     match.destination_url = GURL("http://history-url/");
320     match.relevance = 1400;
321     match.allowed_to_be_default_match = true;
322     match.type = AutocompleteMatchType::HISTORY_URL;
323     matches.push_back(match);
324   }
325   {
326     AutocompleteMatch match;
327     match.destination_url = GURL("http://search-what-you-typed/");
328     match.relevance = 1300;
329     match.allowed_to_be_default_match = true;
330     match.type = AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED;
331     matches.push_back(match);
332   }
333   {
334     AutocompleteMatch match;
335     match.destination_url = GURL("http://history-title/");
336     match.relevance = 1200;
337     match.allowed_to_be_default_match = true;
338     match.type = AutocompleteMatchType::HISTORY_TITLE;
339     matches.push_back(match);
340   }
341   {
342     AutocompleteMatch match;
343     match.destination_url = GURL("http://search-history/");
344     match.relevance = 500;
345     match.allowed_to_be_default_match = true;
346     match.type = AutocompleteMatchType::SEARCH_HISTORY;
347     matches.push_back(match);
348   }
349
350   // Add a rule demoting history-url and killing history-title.
351   {
352     std::map<std::string, std::string> params;
353     params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":3:*"] =
354         "1:50,7:100,2:0";  // 3 == HOME_PAGE
355     ASSERT_TRUE(chrome_variations::AssociateVariationParams(
356         OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
357   }
358   base::FieldTrialList::CreateFieldTrial(
359       OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
360
361   AutocompleteResult result;
362   result.AppendMatches(matches);
363   AutocompleteInput input(string16(), string16::npos, string16(), GURL(),
364                           AutocompleteInput::HOME_PAGE, false, false, false,
365                           AutocompleteInput::ALL_MATCHES);
366   result.SortAndCull(input, test_util_.profile());
367
368   // Check the new ordering.  The history-title results should be omitted.
369   // We cannot check relevance scores because the matches are sorted by
370   // demoted relevance but the actual relevance scores are not modified.
371   ASSERT_EQ(3u, result.size());
372   EXPECT_EQ("http://search-what-you-typed/",
373             result.match_at(0)->destination_url.spec());
374   EXPECT_EQ("http://history-url/",
375             result.match_at(1)->destination_url.spec());
376   EXPECT_EQ("http://search-history/",
377             result.match_at(2)->destination_url.spec());
378 }
379
380 TEST_F(AutocompleteResultTest, SortAndCullReorderForDefaultMatch) {
381   TestData data[] = {
382     { 0, 0, 1300 },
383     { 1, 0, 1200 },
384     { 2, 0, 1100 },
385     { 3, 0, 1000 }
386   };
387
388   std::map<std::string, std::string> params;
389   // Enable reorder for omnibox inputs on the user's home page.
390   params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
391          ":3:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleEnabled;
392   ASSERT_TRUE(chrome_variations::AssociateVariationParams(
393       OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
394   base::FieldTrialList::CreateFieldTrial(
395       OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
396
397   {
398     // Check that reorder doesn't do anything if the top result
399     // is already a legal default match (which is the default from
400     // PopulateAutocompleteMatches()).
401     ACMatches matches;
402     PopulateAutocompleteMatches(data, arraysize(data), &matches);
403     AutocompleteResult result;
404     result.AppendMatches(matches);
405     AutocompleteInput input(string16(), string16::npos, string16(), GURL(),
406                             AutocompleteInput::HOME_PAGE, false, false, false,
407                             AutocompleteInput::ALL_MATCHES);
408     result.SortAndCull(input, test_util_.profile());
409     AssertResultMatches(result, data, 4);
410   }
411
412   {
413     // Check that reorder swaps up a result appropriately.
414     ACMatches matches;
415     PopulateAutocompleteMatches(data, arraysize(data), &matches);
416     matches[0].allowed_to_be_default_match = false;
417     matches[1].allowed_to_be_default_match = false;
418     AutocompleteResult result;
419     result.AppendMatches(matches);
420     AutocompleteInput input(string16(), string16::npos, string16(), GURL(),
421                             AutocompleteInput::HOME_PAGE, false, false, false,
422                             AutocompleteInput::ALL_MATCHES);
423     result.SortAndCull(input, test_util_.profile());
424     ASSERT_EQ(4U, result.size());
425     EXPECT_EQ("http://c/", result.match_at(0)->destination_url.spec());
426     EXPECT_EQ("http://a/", result.match_at(1)->destination_url.spec());
427     EXPECT_EQ("http://b/", result.match_at(2)->destination_url.spec());
428     EXPECT_EQ("http://d/", result.match_at(3)->destination_url.spec());
429   }
430 }