// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/omnibox/omnibox_field_trial.h" #include "base/basictypes.h" #include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "base/metrics/field_trial.h" #include "base/strings/string16.h" #include "chrome/browser/autocomplete/autocomplete_input.h" #include "chrome/browser/search/search.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/metrics/variations/variations_util.h" #include "components/variations/entropy_provider.h" #include "testing/gtest/include/gtest/gtest.h" class OmniboxFieldTrialTest : public testing::Test { public: OmniboxFieldTrialTest() { ResetFieldTrialList(); } void ResetFieldTrialList() { // Destroy the existing FieldTrialList before creating a new one to avoid // a DCHECK. field_trial_list_.reset(); field_trial_list_.reset(new base::FieldTrialList( new metrics::SHA1EntropyProvider("foo"))); chrome_variations::testing::ClearAllVariationParams(); OmniboxFieldTrial::ActivateDynamicTrials(); } // Creates and activates a field trial. static base::FieldTrial* CreateTestTrial(const std::string& name, const std::string& group_name) { base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial( name, group_name); trial->group(); return trial; } // EXPECTS that demotions[match_type] exists with value expected_value. static void VerifyDemotion( const OmniboxFieldTrial::DemotionMultipliers& demotions, AutocompleteMatchType::Type match_type, float expected_value); // EXPECT()s that OmniboxFieldTrial::GetValueForRuleInContext(|rule|, // |page_classification|) returns |rule_value|. static void ExpectRuleValue( const std::string& rule_value, const std::string& rule, AutocompleteInput::PageClassification page_classification); private: scoped_ptr field_trial_list_; DISALLOW_COPY_AND_ASSIGN(OmniboxFieldTrialTest); }; // static void OmniboxFieldTrialTest::VerifyDemotion( const OmniboxFieldTrial::DemotionMultipliers& demotions, AutocompleteMatchType::Type match_type, float expected_value) { OmniboxFieldTrial::DemotionMultipliers::const_iterator demotion_it = demotions.find(match_type); ASSERT_TRUE(demotion_it != demotions.end()); EXPECT_FLOAT_EQ(expected_value, demotion_it->second); } // static void OmniboxFieldTrialTest::ExpectRuleValue( const std::string& rule_value, const std::string& rule, AutocompleteInput::PageClassification page_classification) { EXPECT_EQ(rule_value, OmniboxFieldTrial::GetValueForRuleInContext( rule, page_classification)); } // Test if GetDisabledProviderTypes() properly parses various field trial // group names. TEST_F(OmniboxFieldTrialTest, GetDisabledProviderTypes) { EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes()); { SCOPED_TRACE("Invalid groups"); CreateTestTrial("AutocompleteDynamicTrial_0", "DisabledProviders_"); EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes()); ResetFieldTrialList(); CreateTestTrial("AutocompleteDynamicTrial_1", "DisabledProviders_XXX"); EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes()); ResetFieldTrialList(); CreateTestTrial("AutocompleteDynamicTrial_1", "DisabledProviders_12abc"); EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes()); } { SCOPED_TRACE("Valid group name, unsupported trial name."); ResetFieldTrialList(); CreateTestTrial("UnsupportedTrialName", "DisabledProviders_20"); EXPECT_EQ(0, OmniboxFieldTrial::GetDisabledProviderTypes()); } { SCOPED_TRACE("Valid field and group name."); ResetFieldTrialList(); CreateTestTrial("AutocompleteDynamicTrial_2", "DisabledProviders_3"); EXPECT_EQ(3, OmniboxFieldTrial::GetDisabledProviderTypes()); // Two groups should be OR-ed together. CreateTestTrial("AutocompleteDynamicTrial_3", "DisabledProviders_6"); EXPECT_EQ(7, OmniboxFieldTrial::GetDisabledProviderTypes()); } } // Test if InZeroSuggestFieldTrial() properly parses various field trial // group names. TEST_F(OmniboxFieldTrialTest, ZeroSuggestFieldTrial) { EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial()); { SCOPED_TRACE("Valid group name, unsupported trial name."); ResetFieldTrialList(); CreateTestTrial("UnsupportedTrialName", "EnableZeroSuggest"); EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial()); ResetFieldTrialList(); CreateTestTrial("UnsupportedTrialName", "EnableZeroSuggest_Queries"); EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial()); ResetFieldTrialList(); CreateTestTrial("UnsupportedTrialName", "EnableZeroSuggest_URLS"); EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial()); } { SCOPED_TRACE("Valid trial name, unsupported group name."); ResetFieldTrialList(); CreateTestTrial("AutocompleteDynamicTrial_2", "UnrelatedGroup"); EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestFieldTrial()); } { SCOPED_TRACE("Valid field and group name."); ResetFieldTrialList(); CreateTestTrial("AutocompleteDynamicTrial_2", "EnableZeroSuggest"); EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial()); ResetFieldTrialList(); CreateTestTrial("AutocompleteDynamicTrial_2", "EnableZeroSuggest_Queries"); EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial()); ResetFieldTrialList(); CreateTestTrial("AutocompleteDynamicTrial_3", "EnableZeroSuggest_URLs"); EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial()); } { SCOPED_TRACE("Bundled field trial parameters."); ResetFieldTrialList(); std::map params; params[std::string(OmniboxFieldTrial::kZeroSuggestRule)] = "true"; ASSERT_TRUE(chrome_variations::AssociateVariationParams( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); base::FieldTrialList::CreateFieldTrial( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial()); EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial()); EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial()); ResetFieldTrialList(); params[std::string(OmniboxFieldTrial::kZeroSuggestVariantRule)] = "MostVisited"; ASSERT_TRUE(chrome_variations::AssociateVariationParams( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); base::FieldTrialList::CreateFieldTrial( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial()); EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial()); EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial()); ResetFieldTrialList(); params[std::string(OmniboxFieldTrial::kZeroSuggestVariantRule)] = "AfterTyping"; base::FieldTrialList::CreateFieldTrial( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); ASSERT_TRUE(chrome_variations::AssociateVariationParams( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial()); EXPECT_FALSE(OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial()); EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial()); } } TEST_F(OmniboxFieldTrialTest, GetDemotionsByTypeWithFallback) { { std::map params; params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":1:*"] = "1:50,2:0"; params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":3:*"] = "5:100"; params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":*:*"] = "1:25"; ASSERT_TRUE(chrome_variations::AssociateVariationParams( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); } base::FieldTrialList::CreateFieldTrial( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); OmniboxFieldTrial::DemotionMultipliers demotions_by_type; OmniboxFieldTrial::GetDemotionsByType( AutocompleteInput::NTP, &demotions_by_type); ASSERT_EQ(2u, demotions_by_type.size()); VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_URL, 0.5); VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_TITLE, 0.0); OmniboxFieldTrial::GetDemotionsByType( AutocompleteInput::HOME_PAGE, &demotions_by_type); ASSERT_EQ(1u, demotions_by_type.size()); VerifyDemotion(demotions_by_type, AutocompleteMatchType::NAVSUGGEST, 1.0); OmniboxFieldTrial::GetDemotionsByType( AutocompleteInput::BLANK, &demotions_by_type); ASSERT_EQ(1u, demotions_by_type.size()); VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_URL, 0.25); } TEST_F(OmniboxFieldTrialTest, GetUndemotableTopTypes) { { std::map params; const std::string rule(OmniboxFieldTrial::kUndemotableTopTypeRule); params[rule + ":1:*"] = "1,3"; params[rule + ":3:*"] = "5"; params[rule + ":*:*"] = "2"; ASSERT_TRUE(chrome_variations::AssociateVariationParams( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); } base::FieldTrialList::CreateFieldTrial( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); OmniboxFieldTrial::UndemotableTopMatchTypes undemotable_types; undemotable_types = OmniboxFieldTrial::GetUndemotableTopTypes( AutocompleteInput::NTP); ASSERT_EQ(2u, undemotable_types.size()); ASSERT_EQ(1u, undemotable_types.count(AutocompleteMatchType::HISTORY_URL)); ASSERT_EQ(1u, undemotable_types.count(AutocompleteMatchType::HISTORY_BODY)); undemotable_types = OmniboxFieldTrial::GetUndemotableTopTypes( AutocompleteInput::HOME_PAGE); ASSERT_EQ(1u, undemotable_types.size()); ASSERT_EQ(1u, undemotable_types.count(AutocompleteMatchType::NAVSUGGEST)); undemotable_types = OmniboxFieldTrial::GetUndemotableTopTypes( AutocompleteInput::BLANK); ASSERT_EQ(1u, undemotable_types.size()); ASSERT_EQ(1u, undemotable_types.count(AutocompleteMatchType::HISTORY_TITLE)); } TEST_F(OmniboxFieldTrialTest, GetValueForRuleInContext) { { std::map params; // Rule 1 has some exact matches and fallbacks at every level. params["rule1:1:0"] = "rule1-1-0-value"; // NTP params["rule1:3:0"] = "rule1-3-0-value"; // HOME_PAGE params["rule1:4:1"] = "rule1-4-1-value"; // OTHER params["rule1:4:*"] = "rule1-4-*-value"; // OTHER params["rule1:*:1"] = "rule1-*-1-value"; // global params["rule1:*:*"] = "rule1-*-*-value"; // global // Rule 2 has no exact matches but has fallbacks. params["rule2:*:0"] = "rule2-*-0-value"; // global params["rule2:1:*"] = "rule2-1-*-value"; // NTP params["rule2:*:*"] = "rule2-*-*-value"; // global // Rule 3 has only a global fallback. params["rule3:*:*"] = "rule3-*-*-value"; // global // Rule 4 has an exact match but no fallbacks. params["rule4:4:0"] = "rule4-4-0-value"; // OTHER // Add a malformed rule to make sure it doesn't screw things up. params["unrecognized"] = "unrecognized-value"; ASSERT_TRUE(chrome_variations::AssociateVariationParams( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); } base::FieldTrialList::CreateFieldTrial( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); if (chrome::IsInstantExtendedAPIEnabled()) { // Tests with Instant Extended enabled. // Tests for rule 1. ExpectRuleValue("rule1-4-1-value", "rule1", AutocompleteInput::OTHER); // exact match ExpectRuleValue("rule1-*-1-value", "rule1", AutocompleteInput::BLANK); // partial fallback ExpectRuleValue("rule1-*-1-value", "rule1", AutocompleteInput::NTP); // partial fallback // Tests for rule 2. ExpectRuleValue("rule2-1-*-value", "rule2", AutocompleteInput::NTP); // partial fallback ExpectRuleValue("rule2-*-*-value", "rule2", AutocompleteInput::OTHER); // global fallback // Tests for rule 3. ExpectRuleValue("rule3-*-*-value", "rule3", AutocompleteInput::HOME_PAGE); // global fallback ExpectRuleValue("rule3-*-*-value", "rule3", AutocompleteInput::OTHER); // global fallback // Tests for rule 4. ExpectRuleValue("", "rule4", AutocompleteInput::BLANK); // no global fallback ExpectRuleValue("", "rule4", AutocompleteInput::HOME_PAGE); // no global fallback // Tests for rule 5 (a missing rule). ExpectRuleValue("", "rule5", AutocompleteInput::OTHER); // no rule at all } else { // Tests for rule 1. ExpectRuleValue("rule1-1-0-value", "rule1", AutocompleteInput::NTP); // exact match ExpectRuleValue("rule1-1-0-value", "rule1", AutocompleteInput::NTP); // exact match ExpectRuleValue("rule1-*-*-value", "rule1", AutocompleteInput::BLANK); // fallback to global ExpectRuleValue("rule1-3-0-value", "rule1", AutocompleteInput::HOME_PAGE); // exact match ExpectRuleValue("rule1-4-*-value", "rule1", AutocompleteInput::OTHER); // partial fallback ExpectRuleValue("rule1-*-*-value", "rule1", AutocompleteInput:: // fallback to global SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT); // Tests for rule 2. ExpectRuleValue("rule2-*-0-value", "rule2", AutocompleteInput::HOME_PAGE); // partial fallback ExpectRuleValue("rule2-*-0-value", "rule2", AutocompleteInput::OTHER); // partial fallback // Tests for rule 3. ExpectRuleValue("rule3-*-*-value", "rule3", AutocompleteInput::HOME_PAGE); // fallback to global ExpectRuleValue("rule3-*-*-value", "rule3", AutocompleteInput::OTHER); // fallback to global // Tests for rule 4. ExpectRuleValue("", "rule4", AutocompleteInput::BLANK); // no global fallback ExpectRuleValue("", "rule4", AutocompleteInput::HOME_PAGE); // no global fallback ExpectRuleValue("rule4-4-0-value", "rule4", AutocompleteInput::OTHER); // exact match // Tests for rule 5 (a missing rule). ExpectRuleValue("", "rule5", AutocompleteInput::OTHER); // no rule at all } } TEST_F(OmniboxFieldTrialTest, HUPNewScoringFieldTrial) { { std::map params; params[std::string(OmniboxFieldTrial::kHUPNewScoringEnabledParam)] = "1"; params[std::string( OmniboxFieldTrial::kHUPNewScoringTypedCountRelevanceCapParam)] = "56"; params[std::string( OmniboxFieldTrial::kHUPNewScoringTypedCountHalfLifeTimeParam)] = "77"; params[std::string( OmniboxFieldTrial::kHUPNewScoringTypedCountScoreBucketsParam)] = "0.2:25,0.1:1001,2.3:777"; params[std::string( OmniboxFieldTrial::kHUPNewScoringVisitedCountRelevanceCapParam)] = "11"; params[std::string( OmniboxFieldTrial::kHUPNewScoringVisitedCountHalfLifeTimeParam)] = "31"; params[std::string( OmniboxFieldTrial::kHUPNewScoringVisitedCountScoreBucketsParam)] = "5:300,0:200"; ASSERT_TRUE(chrome_variations::AssociateVariationParams( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); } base::FieldTrialList::CreateFieldTrial( OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); HUPScoringParams scoring_params; OmniboxFieldTrial::GetExperimentalHUPScoringParams(&scoring_params); EXPECT_TRUE(scoring_params.experimental_scoring_enabled); EXPECT_EQ(56, scoring_params.typed_count_buckets.relevance_cap()); EXPECT_EQ(77, scoring_params.typed_count_buckets.half_life_days()); ASSERT_EQ(3u, scoring_params.typed_count_buckets.buckets().size()); EXPECT_EQ(std::make_pair(2.3, 777), scoring_params.typed_count_buckets.buckets()[0]); EXPECT_EQ(std::make_pair(0.2, 25), scoring_params.typed_count_buckets.buckets()[1]); EXPECT_EQ(std::make_pair(0.1, 1001), scoring_params.typed_count_buckets.buckets()[2]); EXPECT_EQ(11, scoring_params.visited_count_buckets.relevance_cap()); EXPECT_EQ(31, scoring_params.visited_count_buckets.half_life_days()); ASSERT_EQ(2u, scoring_params.visited_count_buckets.buckets().size()); EXPECT_EQ(std::make_pair(5.0, 300), scoring_params.visited_count_buckets.buckets()[0]); EXPECT_EQ(std::make_pair(0.0, 200), scoring_params.visited_count_buckets.buckets()[1]); } TEST_F(OmniboxFieldTrialTest, HalfLifeTimeDecay) { HUPScoringParams::ScoreBuckets buckets; // No decay by default. EXPECT_EQ(1.0, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(7))); buckets.set_half_life_days(7); EXPECT_EQ(0.5, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(7))); EXPECT_EQ(0.25, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(14))); EXPECT_EQ(1.0, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(0))); EXPECT_EQ(1.0, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(-1))); }