1// Copyright (c) 2013 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/omnibox/omnibox_field_trial.h"
6
7#include "base/basictypes.h"
8#include "base/command_line.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/metrics/field_trial.h"
11#include "base/strings/string16.h"
12#include "chrome/browser/autocomplete/autocomplete_input.h"
13#include "chrome/browser/search/search.h"
14#include "chrome/common/chrome_switches.h"
15#include "chrome/common/metrics/variations/variations_util.h"
16#include "components/variations/entropy_provider.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19class OmniboxFieldTrialTest : public testing::Test {
20 public:
21  OmniboxFieldTrialTest() {
22    ResetFieldTrialList();
23  }
24
25  void ResetFieldTrialList() {
26    // Destroy the existing FieldTrialList before creating a new one to avoid
27    // a DCHECK.
28    field_trial_list_.reset();
29    field_trial_list_.reset(new base::FieldTrialList(
30        new metrics::SHA1EntropyProvider("foo")));
31    chrome_variations::testing::ClearAllVariationParams();
32    OmniboxFieldTrial::ActivateDynamicTrials();
33  }
34
35  // Creates and activates a field trial.
36  static base::FieldTrial* CreateTestTrial(const std::string& name,
37                                           const std::string& group_name) {
38    base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
39        name, group_name);
40    trial->group();
41    return trial;
42  }
43
44  // EXPECTS that demotions[match_type] exists with value expected_value.
45  static void VerifyDemotion(
46      const OmniboxFieldTrial::DemotionMultipliers& demotions,
47      AutocompleteMatchType::Type match_type,
48      float expected_value);
49
50  // EXPECT()s that OmniboxFieldTrial::GetValueForRuleInContext(|rule|,
51  // |page_classification|) returns |rule_value|.
52  static void ExpectRuleValue(
53      const std::string& rule_value,
54      const std::string& rule,
55      AutocompleteInput::PageClassification page_classification);
56
57 private:
58  scoped_ptr<base::FieldTrialList> field_trial_list_;
59
60  DISALLOW_COPY_AND_ASSIGN(OmniboxFieldTrialTest);
61};
62
63// static
64void OmniboxFieldTrialTest::VerifyDemotion(
65    const OmniboxFieldTrial::DemotionMultipliers& demotions,
66    AutocompleteMatchType::Type match_type,
67    float expected_value) {
68  OmniboxFieldTrial::DemotionMultipliers::const_iterator demotion_it =
69      demotions.find(match_type);
70  ASSERT_TRUE(demotion_it != demotions.end());
71  EXPECT_FLOAT_EQ(expected_value, demotion_it->second);
72}
73
74// static
75void OmniboxFieldTrialTest::ExpectRuleValue(
76    const std::string& rule_value,
77    const std::string& rule,
78    AutocompleteInput::PageClassification page_classification) {
79  EXPECT_EQ(rule_value,
80            OmniboxFieldTrial::GetValueForRuleInContext(
81                rule, page_classification));
82}
83
84// Test if GetDisabledProviderTypes() properly parses various field trial
85// group names.
86TEST_F(OmniboxFieldTrialTest, GetDisabledProviderTypes) {
87  EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
88
89  {
90    SCOPED_TRACE("Invalid groups");
91    CreateTestTrial("AutocompleteDynamicTrial_0", "DisabledProviders_");
92    EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
93    ResetFieldTrialList();
94    CreateTestTrial("AutocompleteDynamicTrial_1", "DisabledProviders_XXX");
95    EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
96    ResetFieldTrialList();
97    CreateTestTrial("AutocompleteDynamicTrial_1", "DisabledProviders_12abc");
98    EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
99  }
100
101  {
102    SCOPED_TRACE("Valid group name, unsupported trial name.");
103    ResetFieldTrialList();
104    CreateTestTrial("UnsupportedTrialName", "DisabledProviders_20");
105    EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
106  }
107
108  {
109    SCOPED_TRACE("Valid field and group name.");
110    ResetFieldTrialList();
111    CreateTestTrial("AutocompleteDynamicTrial_2", "DisabledProviders_3");
112    EXPECT_EQ(3, OmniboxFieldTrial::GetDisabledProviderTypes());
113    // Two groups should be OR-ed together.
114    CreateTestTrial("AutocompleteDynamicTrial_3", "DisabledProviders_6");
115    EXPECT_EQ(7, OmniboxFieldTrial::GetDisabledProviderTypes());
116  }
117}
118
119// Test if InZeroSuggestFieldTrial() properly parses various field trial
120// group names.
121TEST_F(OmniboxFieldTrialTest, ZeroSuggestFieldTrial) {
122  EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
123
124  {
125    SCOPED_TRACE("Valid group name, unsupported trial name.");
126    ResetFieldTrialList();
127    CreateTestTrial("UnsupportedTrialName", "EnableZeroSuggest");
128    EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
129
130    ResetFieldTrialList();
131    CreateTestTrial("UnsupportedTrialName", "EnableZeroSuggest_Queries");
132    EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
133
134    ResetFieldTrialList();
135    CreateTestTrial("UnsupportedTrialName", "EnableZeroSuggest_URLS");
136    EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
137  }
138
139  {
140    SCOPED_TRACE("Valid trial name, unsupported group name.");
141    ResetFieldTrialList();
142    CreateTestTrial("AutocompleteDynamicTrial_2", "UnrelatedGroup");
143    EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
144  }
145
146  {
147    SCOPED_TRACE("Valid field and group name.");
148    ResetFieldTrialList();
149    CreateTestTrial("AutocompleteDynamicTrial_2", "EnableZeroSuggest");
150    EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
151
152    ResetFieldTrialList();
153    CreateTestTrial("AutocompleteDynamicTrial_2", "EnableZeroSuggest_Queries");
154    EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
155
156    ResetFieldTrialList();
157    CreateTestTrial("AutocompleteDynamicTrial_3", "EnableZeroSuggest_URLs");
158    EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
159  }
160}
161
162TEST_F(OmniboxFieldTrialTest, GetDemotionsByTypeWithFallback) {
163  {
164    std::map<std::string, std::string> params;
165    params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":1:*"] =
166        "1:50,2:0";
167    params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":3:*"] =
168        "5:100";
169    params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":*:*"] = "1:25";
170    ASSERT_TRUE(chrome_variations::AssociateVariationParams(
171        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
172  }
173  base::FieldTrialList::CreateFieldTrial(
174      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
175  OmniboxFieldTrial::DemotionMultipliers demotions_by_type;
176  OmniboxFieldTrial::GetDemotionsByType(
177      AutocompleteInput::NTP, &demotions_by_type);
178  ASSERT_EQ(2u, demotions_by_type.size());
179  VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_URL, 0.5);
180  VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_TITLE, 0.0);
181  OmniboxFieldTrial::GetDemotionsByType(
182      AutocompleteInput::HOME_PAGE, &demotions_by_type);
183  ASSERT_EQ(1u, demotions_by_type.size());
184  VerifyDemotion(demotions_by_type, AutocompleteMatchType::NAVSUGGEST, 1.0);
185  OmniboxFieldTrial::GetDemotionsByType(
186      AutocompleteInput::BLANK, &demotions_by_type);
187  ASSERT_EQ(1u, demotions_by_type.size());
188  VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_URL, 0.25);
189}
190
191TEST_F(OmniboxFieldTrialTest, GetUndemotableTopTypes) {
192  {
193    std::map<std::string, std::string> params;
194    const std::string rule(OmniboxFieldTrial::kUndemotableTopTypeRule);
195    params[rule + ":1:*"] = "1,3";
196    params[rule + ":3:*"] = "5";
197    params[rule + ":*:*"] = "2";
198    ASSERT_TRUE(chrome_variations::AssociateVariationParams(
199        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
200  }
201  base::FieldTrialList::CreateFieldTrial(
202      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
203  OmniboxFieldTrial::UndemotableTopMatchTypes undemotable_types;
204  undemotable_types = OmniboxFieldTrial::GetUndemotableTopTypes(
205      AutocompleteInput::NTP);
206  ASSERT_EQ(2u, undemotable_types.size());
207  ASSERT_EQ(1u, undemotable_types.count(AutocompleteMatchType::HISTORY_URL));
208  ASSERT_EQ(1u, undemotable_types.count(AutocompleteMatchType::HISTORY_BODY));
209  undemotable_types = OmniboxFieldTrial::GetUndemotableTopTypes(
210      AutocompleteInput::HOME_PAGE);
211  ASSERT_EQ(1u, undemotable_types.size());
212  ASSERT_EQ(1u, undemotable_types.count(AutocompleteMatchType::NAVSUGGEST));
213  undemotable_types = OmniboxFieldTrial::GetUndemotableTopTypes(
214      AutocompleteInput::BLANK);
215  ASSERT_EQ(1u, undemotable_types.size());
216  ASSERT_EQ(1u, undemotable_types.count(AutocompleteMatchType::HISTORY_TITLE));
217}
218
219TEST_F(OmniboxFieldTrialTest, GetValueForRuleInContext) {
220  {
221    std::map<std::string, std::string> params;
222    // Rule 1 has some exact matches and fallbacks at every level.
223    params["rule1:1:0"] = "rule1-1-0-value";  // NTP
224    params["rule1:3:0"] = "rule1-3-0-value";  // HOME_PAGE
225    params["rule1:4:1"] = "rule1-4-1-value";  // OTHER
226    params["rule1:4:*"] = "rule1-4-*-value";  // OTHER
227    params["rule1:*:1"] = "rule1-*-1-value";  // global
228    params["rule1:*:*"] = "rule1-*-*-value";  // global
229    // Rule 2 has no exact matches but has fallbacks.
230    params["rule2:*:0"] = "rule2-*-0-value";  // global
231    params["rule2:1:*"] = "rule2-1-*-value";  // NTP
232    params["rule2:*:*"] = "rule2-*-*-value";  // global
233    // Rule 3 has only a global fallback.
234    params["rule3:*:*"] = "rule3-*-*-value";  // global
235    // Rule 4 has an exact match but no fallbacks.
236    params["rule4:4:0"] = "rule4-4-0-value";  // OTHER
237    // Add a malformed rule to make sure it doesn't screw things up.
238    params["unrecognized"] = "unrecognized-value";
239    ASSERT_TRUE(chrome_variations::AssociateVariationParams(
240        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
241  }
242
243  base::FieldTrialList::CreateFieldTrial(
244      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
245
246  if (chrome::IsInstantExtendedAPIEnabled()) {
247    // Tests with Instant Extended enabled.
248    // Tests for rule 1.
249    ExpectRuleValue("rule1-4-1-value",
250                    "rule1", AutocompleteInput::OTHER);    // exact match
251    ExpectRuleValue("rule1-*-1-value",
252                    "rule1", AutocompleteInput::BLANK);    // partial fallback
253    ExpectRuleValue("rule1-*-1-value",
254                    "rule1",
255                    AutocompleteInput::NTP);               // partial fallback
256
257    // Tests for rule 2.
258    ExpectRuleValue("rule2-1-*-value",
259                    "rule2",
260                    AutocompleteInput::NTP);               // partial fallback
261    ExpectRuleValue("rule2-*-*-value",
262                    "rule2", AutocompleteInput::OTHER);    // global fallback
263
264    // Tests for rule 3.
265    ExpectRuleValue("rule3-*-*-value",
266                    "rule3",
267                    AutocompleteInput::HOME_PAGE);         // global fallback
268    ExpectRuleValue("rule3-*-*-value",
269                    "rule3",
270                    AutocompleteInput::OTHER);             // global fallback
271
272    // Tests for rule 4.
273    ExpectRuleValue("",
274                    "rule4",
275                    AutocompleteInput::BLANK);             // no global fallback
276    ExpectRuleValue("",
277                    "rule4",
278                    AutocompleteInput::HOME_PAGE);         // no global fallback
279
280    // Tests for rule 5 (a missing rule).
281    ExpectRuleValue("",
282                    "rule5", AutocompleteInput::OTHER);    // no rule at all
283  } else {
284    // Tests for rule 1.
285    ExpectRuleValue("rule1-1-0-value",
286                    "rule1", AutocompleteInput::NTP);      // exact match
287    ExpectRuleValue("rule1-1-0-value",
288                    "rule1", AutocompleteInput::NTP);      // exact match
289    ExpectRuleValue("rule1-*-*-value",
290                    "rule1", AutocompleteInput::BLANK);    // fallback to global
291    ExpectRuleValue("rule1-3-0-value",
292                    "rule1",
293                    AutocompleteInput::HOME_PAGE);         // exact match
294    ExpectRuleValue("rule1-4-*-value",
295                    "rule1", AutocompleteInput::OTHER);    // partial fallback
296    ExpectRuleValue("rule1-*-*-value",
297                    "rule1",
298                    AutocompleteInput::                    // fallback to global
299                    SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT);
300    // Tests for rule 2.
301    ExpectRuleValue("rule2-*-0-value",
302                    "rule2",
303                    AutocompleteInput::HOME_PAGE);         // partial fallback
304    ExpectRuleValue("rule2-*-0-value",
305                    "rule2", AutocompleteInput::OTHER);    // partial fallback
306
307    // Tests for rule 3.
308    ExpectRuleValue("rule3-*-*-value",
309                    "rule3",
310                    AutocompleteInput::HOME_PAGE);         // fallback to global
311    ExpectRuleValue("rule3-*-*-value",
312                    "rule3", AutocompleteInput::OTHER);    // fallback to global
313
314    // Tests for rule 4.
315    ExpectRuleValue("",
316                    "rule4", AutocompleteInput::BLANK);    // no global fallback
317    ExpectRuleValue("",
318                    "rule4",
319                    AutocompleteInput::HOME_PAGE);         // no global fallback
320    ExpectRuleValue("rule4-4-0-value",
321                    "rule4", AutocompleteInput::OTHER);    // exact match
322
323    // Tests for rule 5 (a missing rule).
324    ExpectRuleValue("",
325                    "rule5", AutocompleteInput::OTHER);    // no rule at all
326  }
327}
328