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 18 using metrics::OmniboxEventProto; 19 20 class 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 75 void 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 86 void 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. 97 TEST_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. 132 TEST_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 185 TEST_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 214 TEST_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 324 TEST_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 369 TEST_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