Chromium Code Reviews| Index: components/variations/variations_seed_processor_unittest.cc |
| diff --git a/components/variations/variations_seed_processor_unittest.cc b/components/variations/variations_seed_processor_unittest.cc |
| index 437c611fb39878a917a3879dea2189df7b38e69a..dad2406da307bb74a607d344882ba9562fbec035 100644 |
| --- a/components/variations/variations_seed_processor_unittest.cc |
| +++ b/components/variations/variations_seed_processor_unittest.cc |
| @@ -9,6 +9,7 @@ |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| +#include "base/feature_list.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "components/variations/processed_study.h" |
| @@ -119,16 +120,22 @@ class VariationsSeedProcessorTest : public ::testing::Test { |
| } |
| bool CreateTrialFromStudy(const Study* study) { |
| + return CreateTrialFromStudyWithFeatureList(study, &feature_list_); |
| + } |
| + |
| + bool CreateTrialFromStudyWithFeatureList(const Study* study, |
| + base::FeatureList* feature_list) { |
| ProcessedStudy processed_study; |
| if (processed_study.Init(study, false)) { |
| VariationsSeedProcessor().CreateTrialFromStudy( |
| - processed_study, override_callback_.callback()); |
| + processed_study, override_callback_.callback(), feature_list); |
| return true; |
| } |
| return false; |
| } |
| protected: |
| + base::FeatureList feature_list_; |
| TestOverrideStringCallback override_callback_; |
| private: |
| @@ -226,23 +233,27 @@ TEST_F(VariationsSeedProcessorTest, |
| // Check that adding [expired, non-expired] activates the non-expired one. |
| ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName)); |
| { |
| + base::FeatureList feature_list; |
| base::FieldTrialList field_trial_list(NULL); |
| study1->set_expiry_date(TimeToProtoTime(year_ago)); |
| seed_processor.CreateTrialsFromSeed( |
| seed, "en-CA", base::Time::Now(), version, Study_Channel_STABLE, |
| - Study_FormFactor_DESKTOP, "", "", "", override_callback_.callback()); |
| + Study_FormFactor_DESKTOP, "", "", "", override_callback_.callback(), |
| + &feature_list); |
| EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName)); |
| } |
| // Check that adding [non-expired, expired] activates the non-expired one. |
| ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName)); |
| { |
| + base::FeatureList feature_list; |
| base::FieldTrialList field_trial_list(NULL); |
| study1->clear_expiry_date(); |
| study2->set_expiry_date(TimeToProtoTime(year_ago)); |
| seed_processor.CreateTrialsFromSeed( |
| seed, "en-CA", base::Time::Now(), version, Study_Channel_STABLE, |
| - Study_FormFactor_DESKTOP, "", "", "", override_callback_.callback()); |
| + Study_FormFactor_DESKTOP, "", "", "", override_callback_.callback(), |
| + &feature_list); |
| EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName)); |
| } |
| } |
| @@ -447,7 +458,7 @@ TEST_F(VariationsSeedProcessorTest, StartsActive) { |
| seed_processor.CreateTrialsFromSeed( |
| seed, "en-CA", base::Time::Now(), base::Version("20.0.0.0"), |
| Study_Channel_STABLE, Study_FormFactor_DESKTOP, "", "", "", |
| - override_callback_.callback()); |
| + override_callback_.callback(), &feature_list_); |
| // Non-specified and ACTIVATION_EXPLICIT should not start active, but |
| // ACTIVATION_AUTO should. |
| @@ -504,4 +515,151 @@ TEST_F(VariationsSeedProcessorTest, ForcingFlagAlreadyForced) { |
| EXPECT_EQ(kExperimentId, id); |
| } |
| +TEST_F(VariationsSeedProcessorTest, FeatureEnabledOrDisableByTrial) { |
| + struct base::Feature kFeatureOffByDefault { |
| + "kOff", base::FEATURE_DISABLED_BY_DEFAULT |
| + }; |
| + struct base::Feature kFeatureOnByDefault { |
| + "kOn", base::FEATURE_ENABLED_BY_DEFAULT |
| + }; |
| + struct base::Feature kUnrelatedFeature { |
| + "kUnrelated", base::FEATURE_DISABLED_BY_DEFAULT |
| + }; |
| + |
| + struct { |
| + const char* enable_feature; |
| + const char* disable_feature; |
| + bool expected_feature_off_state; |
| + bool expected_feature_on_state; |
| + } test_cases[] = { |
| + {nullptr, nullptr, false, true}, |
| + {kFeatureOnByDefault.name, nullptr, false, true}, |
| + {kFeatureOffByDefault.name, nullptr, true, true}, |
| + {nullptr, kFeatureOnByDefault.name, false, false}, |
| + {nullptr, kFeatureOffByDefault.name, false, true}, |
| + }; |
| + |
| + for (size_t i = 0; i < arraysize(test_cases); i++) { |
| + const auto& test_case = test_cases[i]; |
| + |
| + base::FieldTrialList field_trial_list(NULL); |
| + base::FeatureList::ClearInstanceForTesting(); |
|
Ilya Sherman
2015/09/16 00:50:44
Should this also be called at the end of each test
Alexei Svitkine (slow)
2015/09/22 21:19:59
Good point. Put in the dtor of the harness.
|
| + scoped_ptr<base::FeatureList> feature_list(new base::FeatureList); |
| + |
| + Study study; |
| + study.set_name("Study1"); |
| + study.set_default_experiment_name("B"); |
| + AddExperiment("B", 0, &study); |
| + |
| + Study_Experiment* experiment = AddExperiment("A", 1, &study); |
| + Study_Experiment_FeatureAssociation* association = |
| + experiment->mutable_feature_association(); |
| + if (test_case.enable_feature) |
| + association->add_enable_feature(test_case.enable_feature); |
| + else if (test_case.disable_feature) |
| + association->add_disable_feature(test_case.disable_feature); |
| + |
| + EXPECT_TRUE( |
| + CreateTrialFromStudyWithFeatureList(&study, feature_list.get())); |
| + base::FeatureList::SetInstance(feature_list.Pass()); |
| + |
| + // |kUnrelatedFeature| should not be affected. |
| + EXPECT_FALSE(base::FeatureList::IsEnabled(kUnrelatedFeature)); |
| + |
| + // Before the associated feature is queried, the trial shouldn't be active. |
| + EXPECT_FALSE(IsFieldTrialActive(study.name())); |
| + |
| + EXPECT_EQ(test_case.expected_feature_off_state, |
| + base::FeatureList::IsEnabled(kFeatureOffByDefault)); |
| + EXPECT_EQ(test_case.expected_feature_on_state, |
| + base::FeatureList::IsEnabled(kFeatureOnByDefault)); |
| + |
| + // The field trial should get activated if it had a feature association. |
| + const bool expected_field_trial_active = |
| + test_case.enable_feature || test_case.disable_feature; |
| + EXPECT_EQ(expected_field_trial_active, IsFieldTrialActive(study.name())); |
| + } |
| +} |
| + |
| +TEST_F(VariationsSeedProcessorTest, FeatureForcingTrialReportingGroup) { |
| + struct base::Feature kFeatureOffByDefault { |
| + "kOff", base::FEATURE_DISABLED_BY_DEFAULT |
| + }; |
| + struct base::Feature kFeatureOnByDefault { |
| + "kOn", base::FEATURE_ENABLED_BY_DEFAULT |
| + }; |
| + |
| + struct { |
| + const base::Feature& feature; |
| + bool set_forcing_feature_on; |
| + bool set_forcing_feature_off; |
| + const char* enable_features_command_line; |
| + const char* disable_features_command_line; |
| + bool expected_feature_state; |
| + bool expected_trial_activated; |
| + } test_cases[] = { |
| + {kFeatureOffByDefault, false, false, "", "", false, false}, |
| + {kFeatureOffByDefault, true, false, kFeatureOffByDefault.name, "", true, |
| + true}, |
| + {kFeatureOffByDefault, false, true, kFeatureOffByDefault.name, "", true, |
| + false}, |
| + {kFeatureOffByDefault, false, true, "", kFeatureOffByDefault.name, false, |
| + true}, |
| + {kFeatureOffByDefault, true, false, "", kFeatureOffByDefault.name, false, |
| + false}, |
| + {kFeatureOnByDefault, false, false, "", "", true, false}, |
| + {kFeatureOnByDefault, true, false, kFeatureOnByDefault.name, "", true, |
| + true}, |
| + {kFeatureOnByDefault, false, true, kFeatureOnByDefault.name, "", true, |
| + false}, |
| + {kFeatureOnByDefault, false, true, "", kFeatureOnByDefault.name, false, |
| + true}, |
| + {kFeatureOnByDefault, true, false, "", kFeatureOnByDefault.name, false, |
| + false}, |
| + }; |
|
Ilya Sherman
2015/09/16 00:50:44
This test is pretty dense, making it hard to read.
Alexei Svitkine (slow)
2015/09/22 21:19:59
Let me think about. I started it as individual tes
Alexei Svitkine (slow)
2015/09/23 22:01:27
Thinking about this more, I agree that this test i
Ilya Sherman
2015/09/24 01:11:32
The comments help a bunch -- thanks!
I think my m
Alexei Svitkine (slow)
2015/09/24 20:49:21
I agree that complicated table-driven tests are ha
|
| + |
| + for (size_t i = 0; i < arraysize(test_cases); i++) { |
| + const auto& test_case = test_cases[i]; |
| + |
| + base::FieldTrialList field_trial_list(NULL); |
| + base::FeatureList::ClearInstanceForTesting(); |
| + scoped_ptr<base::FeatureList> feature_list(new base::FeatureList); |
| + feature_list->InitializeFromCommandLine( |
| + test_case.enable_features_command_line, |
| + test_case.disable_features_command_line); |
| + |
| + Study study; |
| + study.set_name("Study1"); |
| + study.set_default_experiment_name("B"); |
| + Study_Experiment* experiment_with_probability = |
| + AddExperiment("B", 1, &study); |
| + |
| + Study_Experiment* experiment = AddExperiment("A", 0, &study); |
| + Study_Experiment_FeatureAssociation* association = |
| + experiment->mutable_feature_association(); |
| + if (test_case.set_forcing_feature_on) |
| + association->set_forcing_feature_on(test_case.feature.name); |
| + else if (test_case.set_forcing_feature_off) |
| + association->set_forcing_feature_off(test_case.feature.name); |
| + |
| + EXPECT_TRUE( |
| + CreateTrialFromStudyWithFeatureList(&study, feature_list.get())); |
| + base::FeatureList::SetInstance(feature_list.Pass()); |
| + |
| + // Trial should not be activated initially. |
| + EXPECT_FALSE(IsFieldTrialActive(study.name())); |
| + EXPECT_EQ(test_case.expected_feature_state, |
| + base::FeatureList::IsEnabled(test_case.feature)); |
| + EXPECT_EQ(test_case.expected_trial_activated, |
| + IsFieldTrialActive(study.name())); |
| + if (test_case.expected_trial_activated) { |
| + EXPECT_EQ(experiment->name(), |
| + base::FieldTrialList::FindFullName(study.name())); |
| + } else { |
| + EXPECT_EQ(experiment_with_probability->name(), |
| + base::FieldTrialList::FindFullName(study.name())); |
| + } |
| + } |
| +} |
|
Ilya Sherman
2015/09/16 00:50:44
It would be nice to add tests for what happens in
Alexei Svitkine (slow)
2015/09/16 20:54:08
This is meant to be enforced server-side. I guess
Ilya Sherman
2015/09/17 01:02:46
I guess for things that are strictly enforced serv
|
| + |
| } // namespace variations |