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/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 "components/metrics/proto/omnibox_event.pb.h"
13#include "components/search/search.h"
14#include "components/variations/entropy_provider.h"
15#include "components/variations/variations_associated_data.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18using metrics::OmniboxEventProto;
19
20class OmniboxFieldTrialTest : public testing::Test {
21 public:
22  OmniboxFieldTrialTest() {
23    ResetFieldTrialList();
24  }
25
26  void ResetFieldTrialList() {
27    // Destroy the existing FieldTrialList before creating a new one to avoid
28    // a DCHECK.
29    field_trial_list_.reset();
30    field_trial_list_.reset(new base::FieldTrialList(
31        new metrics::SHA1EntropyProvider("foo")));
32    variations::testing::ClearAllVariationParams();
33    OmniboxFieldTrial::ActivateDynamicTrials();
34  }
35
36  // Creates and activates a field trial.
37  static base::FieldTrial* CreateTestTrial(const std::string& name,
38                                           const std::string& group_name) {
39    base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
40        name, group_name);
41    trial->group();
42    return trial;
43  }
44
45  // Add a field trial disabling ZeroSuggest.
46  static void CreateDisableZeroSuggestTrial() {
47    std::map<std::string, std::string> params;
48    params[std::string(OmniboxFieldTrial::kZeroSuggestRule)] = "false";
49    variations::AssociateVariationParams(
50        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params);
51    base::FieldTrialList::CreateFieldTrial(
52        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
53  }
54
55  // EXPECTS that demotions[match_type] exists with value expected_value.
56  static void VerifyDemotion(
57      const OmniboxFieldTrial::DemotionMultipliers& demotions,
58      AutocompleteMatchType::Type match_type,
59      float expected_value);
60
61  // EXPECT()s that OmniboxFieldTrial::GetValueForRuleInContext(|rule|,
62  // |page_classification|) returns |rule_value|.
63  static void ExpectRuleValue(
64      const std::string& rule_value,
65      const std::string& rule,
66      OmniboxEventProto::PageClassification page_classification);
67
68 private:
69  scoped_ptr<base::FieldTrialList> field_trial_list_;
70
71  DISALLOW_COPY_AND_ASSIGN(OmniboxFieldTrialTest);
72};
73
74// static
75void OmniboxFieldTrialTest::VerifyDemotion(
76    const OmniboxFieldTrial::DemotionMultipliers& demotions,
77    AutocompleteMatchType::Type match_type,
78    float expected_value) {
79  OmniboxFieldTrial::DemotionMultipliers::const_iterator demotion_it =
80      demotions.find(match_type);
81  ASSERT_TRUE(demotion_it != demotions.end());
82  EXPECT_FLOAT_EQ(expected_value, demotion_it->second);
83}
84
85// static
86void OmniboxFieldTrialTest::ExpectRuleValue(
87    const std::string& rule_value,
88    const std::string& rule,
89    OmniboxEventProto::PageClassification page_classification) {
90  EXPECT_EQ(rule_value,
91            OmniboxFieldTrial::GetValueForRuleInContext(
92                rule, page_classification));
93}
94
95// Test if GetDisabledProviderTypes() properly parses various field trial
96// group names.
97TEST_F(OmniboxFieldTrialTest, GetDisabledProviderTypes) {
98  EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
99
100  {
101    SCOPED_TRACE("Invalid groups");
102    CreateTestTrial("AutocompleteDynamicTrial_0", "DisabledProviders_");
103    EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
104    ResetFieldTrialList();
105    CreateTestTrial("AutocompleteDynamicTrial_1", "DisabledProviders_XXX");
106    EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
107    ResetFieldTrialList();
108    CreateTestTrial("AutocompleteDynamicTrial_1", "DisabledProviders_12abc");
109    EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
110  }
111
112  {
113    SCOPED_TRACE("Valid group name, unsupported trial name.");
114    ResetFieldTrialList();
115    CreateTestTrial("UnsupportedTrialName", "DisabledProviders_20");
116    EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes());
117  }
118
119  {
120    SCOPED_TRACE("Valid field and group name.");
121    ResetFieldTrialList();
122    CreateTestTrial("AutocompleteDynamicTrial_2", "DisabledProviders_3");
123    EXPECT_EQ(3, OmniboxFieldTrial::GetDisabledProviderTypes());
124    // Two groups should be OR-ed together.
125    CreateTestTrial("AutocompleteDynamicTrial_3", "DisabledProviders_6");
126    EXPECT_EQ(7, OmniboxFieldTrial::GetDisabledProviderTypes());
127  }
128}
129
130// Test if InZeroSuggestFieldTrial() properly parses various field trial
131// group names.
132TEST_F(OmniboxFieldTrialTest, ZeroSuggestFieldTrial) {
133  // Default ZeroSuggest setting depends on OS.
134#if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(OS_LINUX) || \
135    (defined(OS_MACOSX) && !defined(OS_IOS))
136  EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
137#else
138  EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
139#endif
140
141  {
142    SCOPED_TRACE("Disable ZeroSuggest.");
143    ResetFieldTrialList();
144    CreateDisableZeroSuggestTrial();
145    EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
146  }
147
148  {
149    SCOPED_TRACE("Bundled field trial parameters.");
150    ResetFieldTrialList();
151    std::map<std::string, std::string> params;
152    params[std::string(OmniboxFieldTrial::kZeroSuggestRule)] = "true";
153    ASSERT_TRUE(variations::AssociateVariationParams(
154        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
155    base::FieldTrialList::CreateFieldTrial(
156        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
157    EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
158    EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial());
159    EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial());
160
161    ResetFieldTrialList();
162    params[std::string(OmniboxFieldTrial::kZeroSuggestVariantRule)] =
163        "MostVisited";
164    ASSERT_TRUE(variations::AssociateVariationParams(
165        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
166    base::FieldTrialList::CreateFieldTrial(
167        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
168    EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
169    EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial());
170    EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial());
171
172    ResetFieldTrialList();
173    params[std::string(OmniboxFieldTrial::kZeroSuggestVariantRule)] =
174        "AfterTyping";
175    base::FieldTrialList::CreateFieldTrial(
176        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
177    ASSERT_TRUE(variations::AssociateVariationParams(
178        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
179    EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
180    EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial());
181    EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial());
182  }
183}
184
185TEST_F(OmniboxFieldTrialTest, GetDemotionsByTypeWithFallback) {
186  {
187    std::map<std::string, std::string> params;
188    params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":1:*"] =
189        "1:50,2:0";
190    params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":3:*"] =
191        "5:100";
192    params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":*:*"] = "1:25";
193    ASSERT_TRUE(variations::AssociateVariationParams(
194        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
195  }
196  base::FieldTrialList::CreateFieldTrial(
197      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
198  OmniboxFieldTrial::DemotionMultipliers demotions_by_type;
199  OmniboxFieldTrial::GetDemotionsByType(
200      OmniboxEventProto::NTP, &demotions_by_type);
201  ASSERT_EQ(2u, demotions_by_type.size());
202  VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_URL, 0.5);
203  VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_TITLE, 0.0);
204  OmniboxFieldTrial::GetDemotionsByType(
205      OmniboxEventProto::HOME_PAGE, &demotions_by_type);
206  ASSERT_EQ(1u, demotions_by_type.size());
207  VerifyDemotion(demotions_by_type, AutocompleteMatchType::NAVSUGGEST, 1.0);
208  OmniboxFieldTrial::GetDemotionsByType(
209      OmniboxEventProto::BLANK, &demotions_by_type);
210  ASSERT_EQ(1u, demotions_by_type.size());
211  VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_URL, 0.25);
212}
213
214TEST_F(OmniboxFieldTrialTest, GetValueForRuleInContext) {
215  {
216    std::map<std::string, std::string> params;
217    // Rule 1 has some exact matches and fallbacks at every level.
218    params["rule1:1:0"] = "rule1-1-0-value";  // NTP
219    params["rule1:3:0"] = "rule1-3-0-value";  // HOME_PAGE
220    params["rule1:4:1"] = "rule1-4-1-value";  // OTHER
221    params["rule1:4:*"] = "rule1-4-*-value";  // OTHER
222    params["rule1:*:1"] = "rule1-*-1-value";  // global
223    params["rule1:*:*"] = "rule1-*-*-value";  // global
224    // Rule 2 has no exact matches but has fallbacks.
225    params["rule2:*:0"] = "rule2-*-0-value";  // global
226    params["rule2:1:*"] = "rule2-1-*-value";  // NTP
227    params["rule2:*:*"] = "rule2-*-*-value";  // global
228    // Rule 3 has only a global fallback.
229    params["rule3:*:*"] = "rule3-*-*-value";  // global
230    // Rule 4 has an exact match but no fallbacks.
231    params["rule4:4:0"] = "rule4-4-0-value";  // OTHER
232    // Add a malformed rule to make sure it doesn't screw things up.
233    params["unrecognized"] = "unrecognized-value";
234    ASSERT_TRUE(variations::AssociateVariationParams(
235        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
236  }
237
238  base::FieldTrialList::CreateFieldTrial(
239      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
240
241  if (chrome::IsInstantExtendedAPIEnabled()) {
242    // Tests with Instant Extended enabled.
243    // Tests for rule 1.
244    ExpectRuleValue("rule1-4-1-value",
245                    "rule1", OmniboxEventProto::OTHER);    // exact match
246    ExpectRuleValue("rule1-*-1-value",
247                    "rule1", OmniboxEventProto::BLANK);    // partial fallback
248    ExpectRuleValue("rule1-*-1-value",
249                    "rule1",
250                    OmniboxEventProto::NTP);               // partial fallback
251
252    // Tests for rule 2.
253    ExpectRuleValue("rule2-1-*-value",
254                    "rule2",
255                    OmniboxEventProto::NTP);               // partial fallback
256    ExpectRuleValue("rule2-*-*-value",
257                    "rule2", OmniboxEventProto::OTHER);    // global fallback
258
259    // Tests for rule 3.
260    ExpectRuleValue("rule3-*-*-value",
261                    "rule3",
262                    OmniboxEventProto::HOME_PAGE);         // global fallback
263    ExpectRuleValue("rule3-*-*-value",
264                    "rule3",
265                    OmniboxEventProto::OTHER);             // global fallback
266
267    // Tests for rule 4.
268    ExpectRuleValue("",
269                    "rule4",
270                    OmniboxEventProto::BLANK);             // no global fallback
271    ExpectRuleValue("",
272                    "rule4",
273                    OmniboxEventProto::HOME_PAGE);         // no global fallback
274
275    // Tests for rule 5 (a missing rule).
276    ExpectRuleValue("",
277                    "rule5", OmniboxEventProto::OTHER);    // no rule at all
278  } else {
279    // Tests for rule 1.
280    ExpectRuleValue("rule1-1-0-value",
281                    "rule1", OmniboxEventProto::NTP);      // exact match
282    ExpectRuleValue("rule1-1-0-value",
283                    "rule1", OmniboxEventProto::NTP);      // exact match
284    ExpectRuleValue("rule1-*-*-value",
285                    "rule1", OmniboxEventProto::BLANK);    // fallback to global
286    ExpectRuleValue("rule1-3-0-value",
287                    "rule1",
288                    OmniboxEventProto::HOME_PAGE);         // exact match
289    ExpectRuleValue("rule1-4-*-value",
290                    "rule1", OmniboxEventProto::OTHER);    // partial fallback
291    ExpectRuleValue("rule1-*-*-value",
292                    "rule1",
293                    OmniboxEventProto::                    // fallback to global
294                    SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT);
295    // Tests for rule 2.
296    ExpectRuleValue("rule2-*-0-value",
297                    "rule2",
298                    OmniboxEventProto::HOME_PAGE);         // partial fallback
299    ExpectRuleValue("rule2-*-0-value",
300                    "rule2", OmniboxEventProto::OTHER);    // partial fallback
301
302    // Tests for rule 3.
303    ExpectRuleValue("rule3-*-*-value",
304                    "rule3",
305                    OmniboxEventProto::HOME_PAGE);         // fallback to global
306    ExpectRuleValue("rule3-*-*-value",
307                    "rule3", OmniboxEventProto::OTHER);    // fallback to global
308
309    // Tests for rule 4.
310    ExpectRuleValue("",
311                    "rule4", OmniboxEventProto::BLANK);    // no global fallback
312    ExpectRuleValue("",
313                    "rule4",
314                    OmniboxEventProto::HOME_PAGE);         // no global fallback
315    ExpectRuleValue("rule4-4-0-value",
316                    "rule4", OmniboxEventProto::OTHER);    // exact match
317
318    // Tests for rule 5 (a missing rule).
319    ExpectRuleValue("",
320                    "rule5", OmniboxEventProto::OTHER);    // no rule at all
321  }
322}
323
324TEST_F(OmniboxFieldTrialTest, HUPNewScoringFieldTrial) {
325  {
326    std::map<std::string, std::string> params;
327    params[std::string(OmniboxFieldTrial::kHUPNewScoringEnabledParam)] = "1";
328    params[std::string(
329        OmniboxFieldTrial::kHUPNewScoringTypedCountRelevanceCapParam)] = "56";
330    params[std::string(
331        OmniboxFieldTrial::kHUPNewScoringTypedCountHalfLifeTimeParam)] = "77";
332    params[std::string(
333        OmniboxFieldTrial::kHUPNewScoringTypedCountScoreBucketsParam)] =
334        "0.2:25,0.1:1001,2.3:777";
335    params[std::string(
336        OmniboxFieldTrial::kHUPNewScoringVisitedCountRelevanceCapParam)] = "11";
337    params[std::string(
338        OmniboxFieldTrial::kHUPNewScoringVisitedCountHalfLifeTimeParam)] = "31";
339    params[std::string(
340        OmniboxFieldTrial::kHUPNewScoringVisitedCountScoreBucketsParam)] =
341        "5:300,0:200";
342    ASSERT_TRUE(variations::AssociateVariationParams(
343        OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
344  }
345  base::FieldTrialList::CreateFieldTrial(
346      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
347
348  HUPScoringParams scoring_params;
349  OmniboxFieldTrial::GetExperimentalHUPScoringParams(&scoring_params);
350  EXPECT_TRUE(scoring_params.experimental_scoring_enabled);
351  EXPECT_EQ(56, scoring_params.typed_count_buckets.relevance_cap());
352  EXPECT_EQ(77, scoring_params.typed_count_buckets.half_life_days());
353  ASSERT_EQ(3u, scoring_params.typed_count_buckets.buckets().size());
354  EXPECT_EQ(std::make_pair(2.3, 777),
355            scoring_params.typed_count_buckets.buckets()[0]);
356  EXPECT_EQ(std::make_pair(0.2, 25),
357            scoring_params.typed_count_buckets.buckets()[1]);
358  EXPECT_EQ(std::make_pair(0.1, 1001),
359            scoring_params.typed_count_buckets.buckets()[2]);
360  EXPECT_EQ(11, scoring_params.visited_count_buckets.relevance_cap());
361  EXPECT_EQ(31, scoring_params.visited_count_buckets.half_life_days());
362  ASSERT_EQ(2u, scoring_params.visited_count_buckets.buckets().size());
363  EXPECT_EQ(std::make_pair(5.0, 300),
364            scoring_params.visited_count_buckets.buckets()[0]);
365  EXPECT_EQ(std::make_pair(0.0, 200),
366            scoring_params.visited_count_buckets.buckets()[1]);
367}
368
369TEST_F(OmniboxFieldTrialTest, HalfLifeTimeDecay) {
370  HUPScoringParams::ScoreBuckets buckets;
371
372  // No decay by default.
373  EXPECT_EQ(1.0, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(7)));
374
375  buckets.set_half_life_days(7);
376  EXPECT_EQ(0.5, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(7)));
377  EXPECT_EQ(0.25, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(14)));
378  EXPECT_EQ(1.0, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(0)));
379  EXPECT_EQ(1.0, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(-1)));
380}
381