123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797 |
- // Copyright 2015 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 "base/feature_list.h"
- #include <stddef.h>
- #include <utility>
- #include <vector>
- #include "base/feature_list_buildflags.h"
- #include "base/format_macros.h"
- #include "base/memory/read_only_shared_memory_region.h"
- #include "base/metrics/field_trial.h"
- #include "base/metrics/field_trial_param_associator.h"
- #include "base/metrics/persistent_memory_allocator.h"
- #include "base/ranges/algorithm.h"
- #include "base/strings/strcat.h"
- #include "base/strings/string_piece.h"
- #include "base/strings/string_util.h"
- #include "base/strings/stringprintf.h"
- #include "base/test/scoped_feature_list.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace base {
- namespace {
- constexpr char kFeatureOnByDefaultName[] = "OnByDefault";
- struct Feature kFeatureOnByDefault {
- kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT
- };
- constexpr char kFeatureOffByDefaultName[] = "OffByDefault";
- struct Feature kFeatureOffByDefault {
- kFeatureOffByDefaultName, FEATURE_DISABLED_BY_DEFAULT
- };
- std::string SortFeatureListString(const std::string& feature_list) {
- std::vector<base::StringPiece> features =
- FeatureList::SplitFeatureListString(feature_list);
- ranges::sort(features);
- return JoinString(features, ",");
- }
- } // namespace
- class FeatureListTest : public testing::Test {
- public:
- FeatureListTest() {
- // Provide an empty FeatureList to each test by default.
- scoped_feature_list_.InitWithFeatureList(std::make_unique<FeatureList>());
- }
- FeatureListTest(const FeatureListTest&) = delete;
- FeatureListTest& operator=(const FeatureListTest&) = delete;
- ~FeatureListTest() override = default;
- private:
- test::ScopedFeatureList scoped_feature_list_;
- };
- TEST_F(FeatureListTest, DefaultStates) {
- EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
- }
- TEST_F(FeatureListTest, InitializeFromCommandLine) {
- struct {
- const char* enable_features;
- const char* disable_features;
- bool expected_feature_on_state;
- bool expected_feature_off_state;
- } test_cases[] = {
- {"", "", true, false},
- {"OffByDefault", "", true, true},
- {"OffByDefault", "OnByDefault", false, true},
- {"OnByDefault,OffByDefault", "", true, true},
- {"", "OnByDefault,OffByDefault", false, false},
- // In the case an entry is both, disable takes precedence.
- {"OnByDefault", "OnByDefault,OffByDefault", false, false},
- };
- for (size_t i = 0; i < std::size(test_cases); ++i) {
- const auto& test_case = test_cases[i];
- SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: [%s] [%s]", i,
- test_case.enable_features,
- test_case.disable_features));
- auto feature_list = std::make_unique<FeatureList>();
- feature_list->InitializeFromCommandLine(test_case.enable_features,
- test_case.disable_features);
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_EQ(test_case.expected_feature_on_state,
- FeatureList::IsEnabled(kFeatureOnByDefault))
- << i;
- EXPECT_EQ(test_case.expected_feature_off_state,
- FeatureList::IsEnabled(kFeatureOffByDefault))
- << i;
- }
- }
- TEST_F(FeatureListTest, InitializeFromCommandLineWithFeatureParams) {
- struct {
- const std::string enable_features;
- const std::string expected_field_trial_created;
- const std::map<std::string, std::string> expected_feature_params;
- } test_cases[] = {
- {"Feature:x/100/y/test", "StudyFeature", {{"x", "100"}, {"y", "test"}}},
- {"Feature<Trial1:x/200/y/123", "Trial1", {{"x", "200"}, {"y", "123"}}},
- {"Feature<Trial2.Group2:x/test/y/uma/z/ukm",
- "Trial2",
- {{"x", "test"}, {"y", "uma"}, {"z", "ukm"}}},
- };
- // Clear global state so that repeated runs of this test don't flake.
- // When https://crrev.com/c/3694674 is submitted, we should be able to remove
- // this.
- base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
- const Feature kFeature = {"Feature", FEATURE_DISABLED_BY_DEFAULT};
- for (const auto& test_case : test_cases) {
- SCOPED_TRACE(test_case.enable_features);
- auto feature_list = std::make_unique<FeatureList>();
- feature_list->InitializeFromCommandLine(test_case.enable_features, "");
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_TRUE(FeatureList::IsEnabled(kFeature));
- EXPECT_TRUE(
- FieldTrialList::IsTrialActive(test_case.expected_field_trial_created));
- std::map<std::string, std::string> actual_params;
- EXPECT_TRUE(GetFieldTrialParamsByFeature(kFeature, &actual_params));
- EXPECT_EQ(test_case.expected_feature_params, actual_params);
- }
- }
- TEST_F(FeatureListTest, CheckFeatureIdentity) {
- // Tests that CheckFeatureIdentity() correctly detects when two different
- // structs with the same feature name are passed to it.
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::make_unique<FeatureList>());
- FeatureList* feature_list = FeatureList::GetInstance();
- // Call it twice for each feature at the top of the file, since the first call
- // makes it remember the entry and the second call will verify it.
- EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault));
- EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault));
- EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOffByDefault));
- EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOffByDefault));
- // Now, call it with a distinct struct for |kFeatureOnByDefaultName|, which
- // should return false.
- struct Feature kFeatureOnByDefault2 {
- kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT
- };
- EXPECT_FALSE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault2));
- }
- TEST_F(FeatureListTest, FieldTrialOverrides) {
- struct {
- FeatureList::OverrideState trial1_state;
- FeatureList::OverrideState trial2_state;
- } test_cases[] = {
- {FeatureList::OVERRIDE_DISABLE_FEATURE,
- FeatureList::OVERRIDE_DISABLE_FEATURE},
- {FeatureList::OVERRIDE_DISABLE_FEATURE,
- FeatureList::OVERRIDE_ENABLE_FEATURE},
- {FeatureList::OVERRIDE_ENABLE_FEATURE,
- FeatureList::OVERRIDE_DISABLE_FEATURE},
- {FeatureList::OVERRIDE_ENABLE_FEATURE,
- FeatureList::OVERRIDE_ENABLE_FEATURE},
- };
- FieldTrial::ActiveGroup active_group;
- for (size_t i = 0; i < std::size(test_cases); ++i) {
- const auto& test_case = test_cases[i];
- SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i));
- test::ScopedFeatureList outer_scope;
- outer_scope.InitWithEmptyFeatureAndFieldTrialLists();
- auto feature_list = std::make_unique<FeatureList>();
- FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A");
- FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B");
- feature_list->RegisterFieldTrialOverride(kFeatureOnByDefaultName,
- test_case.trial1_state, trial1);
- feature_list->RegisterFieldTrialOverride(kFeatureOffByDefaultName,
- test_case.trial2_state, trial2);
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- // Initially, neither trial should be active.
- EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name()));
- EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name()));
- const bool expected_enabled_1 =
- (test_case.trial1_state == FeatureList::OVERRIDE_ENABLE_FEATURE);
- EXPECT_EQ(expected_enabled_1, FeatureList::IsEnabled(kFeatureOnByDefault));
- // The above should have activated |trial1|.
- EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name()));
- EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name()));
- const bool expected_enabled_2 =
- (test_case.trial2_state == FeatureList::OVERRIDE_ENABLE_FEATURE);
- EXPECT_EQ(expected_enabled_2, FeatureList::IsEnabled(kFeatureOffByDefault));
- // The above should have activated |trial2|.
- EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name()));
- EXPECT_TRUE(FieldTrialList::IsTrialActive(trial2->trial_name()));
- }
- }
- TEST_F(FeatureListTest, FieldTrialAssociateUseDefault) {
- auto feature_list = std::make_unique<FeatureList>();
- FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A");
- FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B");
- feature_list->RegisterFieldTrialOverride(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial1);
- feature_list->RegisterFieldTrialOverride(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial2);
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- // Initially, neither trial should be active.
- EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name()));
- EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name()));
- // Check the feature enabled state is its default.
- EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
- // The above should have activated |trial1|.
- EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name()));
- EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name()));
- // Check the feature enabled state is its default.
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
- // The above should have activated |trial2|.
- EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name()));
- EXPECT_TRUE(FieldTrialList::IsTrialActive(trial2->trial_name()));
- }
- TEST_F(FeatureListTest, CommandLineEnableTakesPrecedenceOverFieldTrial) {
- auto feature_list = std::make_unique<FeatureList>();
- // The feature is explicitly enabled on the command-line.
- feature_list->InitializeFromCommandLine(kFeatureOffByDefaultName, "");
- // But the FieldTrial would set the feature to disabled.
- FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample2", "A");
- feature_list->RegisterFieldTrialOverride(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, trial);
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name()));
- // Command-line should take precedence.
- EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
- // Since the feature is on due to the command-line, and not as a result of the
- // field trial, the field trial should not be activated (since the Associate*
- // API wasn't used.)
- EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name()));
- }
- TEST_F(FeatureListTest, CommandLineDisableTakesPrecedenceOverFieldTrial) {
- auto feature_list = std::make_unique<FeatureList>();
- // The feature is explicitly disabled on the command-line.
- feature_list->InitializeFromCommandLine("", kFeatureOffByDefaultName);
- // But the FieldTrial would set the feature to enabled.
- FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample2", "A");
- feature_list->RegisterFieldTrialOverride(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE, trial);
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name()));
- // Command-line should take precedence.
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
- // Since the feature is on due to the command-line, and not as a result of the
- // field trial, the field trial should not be activated (since the Associate*
- // API wasn't used.)
- EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name()));
- }
- TEST_F(FeatureListTest, IsFeatureOverriddenFromFieldTrial) {
- auto feature_list = std::make_unique<FeatureList>();
- // No features are overridden from the field trails yet.
- EXPECT_FALSE(feature_list->IsFeatureOverridden(kFeatureOnByDefaultName));
- EXPECT_FALSE(feature_list->IsFeatureOverridden(kFeatureOffByDefaultName));
- // Now, register a field trial to override |kFeatureOnByDefaultName| state
- // and check that the function still returns false for that feature.
- feature_list->RegisterFieldTrialOverride(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT,
- FieldTrialList::CreateFieldTrial("Trial1", "A"));
- feature_list->RegisterFieldTrialOverride(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE,
- FieldTrialList::CreateFieldTrial("Trial2", "A"));
- EXPECT_TRUE(feature_list->IsFeatureOverridden(kFeatureOnByDefaultName));
- EXPECT_TRUE(feature_list->IsFeatureOverridden(kFeatureOffByDefaultName));
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- // Check the expected feature states for good measure.
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
- }
- TEST_F(FeatureListTest, IsFeatureOverriddenFromCommandLine) {
- auto feature_list = std::make_unique<FeatureList>();
- // No features are overridden from the command line yet
- EXPECT_FALSE(feature_list->IsFeatureOverridden(kFeatureOnByDefaultName));
- EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOnByDefaultName));
- EXPECT_FALSE(feature_list->IsFeatureOverridden(kFeatureOffByDefaultName));
- EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName));
- EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
- EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
- EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
- EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
- // Now, enable |kFeatureOffByDefaultName| via the command-line.
- feature_list->InitializeFromCommandLine(kFeatureOffByDefaultName, "");
- // It should now be overridden for the enabled group.
- EXPECT_TRUE(feature_list->IsFeatureOverridden(kFeatureOffByDefaultName));
- EXPECT_TRUE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName));
- EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
- EXPECT_TRUE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
- // Register a field trial to associate with the feature and ensure that the
- // results are still the same.
- feature_list->AssociateReportingFieldTrial(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE,
- FieldTrialList::CreateFieldTrial("Trial1", "A"));
- EXPECT_TRUE(feature_list->IsFeatureOverridden(kFeatureOffByDefaultName));
- EXPECT_TRUE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName));
- EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
- EXPECT_TRUE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
- // Now, register a field trial to override |kFeatureOnByDefaultName| state
- // and check that the function still returns false for that feature.
- feature_list->RegisterFieldTrialOverride(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE,
- FieldTrialList::CreateFieldTrial("Trial2", "A"));
- EXPECT_TRUE(feature_list->IsFeatureOverridden(kFeatureOnByDefaultName));
- EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOnByDefaultName));
- EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
- EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- // Check the expected feature states for good measure.
- EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
- }
- TEST_F(FeatureListTest, AssociateReportingFieldTrial) {
- struct {
- const char* enable_features;
- const char* disable_features;
- bool expected_enable_trial_created;
- bool expected_disable_trial_created;
- } test_cases[] = {
- // If no enable/disable flags are specified, no trials should be created.
- {"", "", false, false},
- // Enabling the feature should result in the enable trial created.
- {kFeatureOffByDefaultName, "", true, false},
- // Disabling the feature should result in the disable trial created.
- {"", kFeatureOffByDefaultName, false, true},
- };
- const char kTrialName[] = "ForcingTrial";
- const char kForcedOnGroupName[] = "ForcedOn";
- const char kForcedOffGroupName[] = "ForcedOff";
- for (size_t i = 0; i < std::size(test_cases); ++i) {
- const auto& test_case = test_cases[i];
- SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: [%s] [%s]", i,
- test_case.enable_features,
- test_case.disable_features));
- test::ScopedFeatureList outer_scope;
- outer_scope.InitWithEmptyFeatureAndFieldTrialLists();
- auto feature_list = std::make_unique<FeatureList>();
- feature_list->InitializeFromCommandLine(test_case.enable_features,
- test_case.disable_features);
- FieldTrial* enable_trial = nullptr;
- if (feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)) {
- enable_trial = base::FieldTrialList::CreateFieldTrial(kTrialName,
- kForcedOnGroupName);
- feature_list->AssociateReportingFieldTrial(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE,
- enable_trial);
- }
- FieldTrial* disable_trial = nullptr;
- if (feature_list->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)) {
- disable_trial = base::FieldTrialList::CreateFieldTrial(
- kTrialName, kForcedOffGroupName);
- feature_list->AssociateReportingFieldTrial(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE,
- disable_trial);
- }
- EXPECT_EQ(test_case.expected_enable_trial_created, enable_trial != nullptr);
- EXPECT_EQ(test_case.expected_disable_trial_created,
- disable_trial != nullptr);
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
- if (disable_trial) {
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
- EXPECT_TRUE(FieldTrialList::IsTrialActive(kTrialName));
- EXPECT_EQ(kForcedOffGroupName, disable_trial->group_name());
- } else if (enable_trial) {
- EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
- EXPECT_TRUE(FieldTrialList::IsTrialActive(kTrialName));
- EXPECT_EQ(kForcedOnGroupName, enable_trial->group_name());
- }
- }
- }
- TEST_F(FeatureListTest, RegisterExtraFeatureOverrides) {
- auto feature_list = std::make_unique<FeatureList>();
- std::vector<FeatureList::FeatureOverrideInfo> overrides;
- overrides.push_back({std::cref(kFeatureOnByDefault),
- FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE});
- overrides.push_back({std::cref(kFeatureOffByDefault),
- FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE});
- feature_list->RegisterExtraFeatureOverrides(std::move(overrides));
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
- EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
- }
- TEST_F(FeatureListTest, InitializeFromCommandLineThenRegisterExtraOverrides) {
- auto feature_list = std::make_unique<FeatureList>();
- feature_list->InitializeFromCommandLine(kFeatureOnByDefaultName,
- kFeatureOffByDefaultName);
- std::vector<FeatureList::FeatureOverrideInfo> overrides;
- overrides.push_back({std::cref(kFeatureOnByDefault),
- FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE});
- overrides.push_back({std::cref(kFeatureOffByDefault),
- FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE});
- feature_list->RegisterExtraFeatureOverrides(std::move(overrides));
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- // The InitializeFromCommandLine supersedes the RegisterExtraFeatureOverrides
- // because it was called first.
- EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
- std::string enable_features;
- std::string disable_features;
- FeatureList::GetInstance()->GetFeatureOverrides(&enable_features,
- &disable_features);
- EXPECT_EQ(kFeatureOnByDefaultName, SortFeatureListString(enable_features));
- EXPECT_EQ(kFeatureOffByDefaultName, SortFeatureListString(disable_features));
- }
- TEST_F(FeatureListTest, GetFeatureOverrides) {
- auto feature_list = std::make_unique<FeatureList>();
- feature_list->InitializeFromCommandLine("A,X", "D");
- Feature feature_b = {"B", FEATURE_ENABLED_BY_DEFAULT};
- Feature feature_c = {"C", FEATURE_DISABLED_BY_DEFAULT};
- std::vector<FeatureList::FeatureOverrideInfo> overrides;
- overrides.push_back({std::cref(feature_b),
- FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE});
- overrides.push_back({std::cref(feature_c),
- FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE});
- feature_list->RegisterExtraFeatureOverrides(std::move(overrides));
- FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group");
- feature_list->RegisterFieldTrialOverride(kFeatureOffByDefaultName,
- FeatureList::OVERRIDE_ENABLE_FEATURE,
- trial);
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- std::string enable_features;
- std::string disable_features;
- FeatureList::GetInstance()->GetFeatureOverrides(&enable_features,
- &disable_features);
- EXPECT_EQ("A,C,OffByDefault<Trial,X", SortFeatureListString(enable_features));
- EXPECT_EQ("B,D", SortFeatureListString(disable_features));
- FeatureList::GetInstance()->GetCommandLineFeatureOverrides(&enable_features,
- &disable_features);
- EXPECT_EQ("A,C,X", SortFeatureListString(enable_features));
- EXPECT_EQ("B,D", SortFeatureListString(disable_features));
- }
- TEST_F(FeatureListTest, GetFeatureOverrides_UseDefault) {
- auto feature_list = std::make_unique<FeatureList>();
- feature_list->InitializeFromCommandLine("A,X", "D");
- FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group");
- feature_list->RegisterFieldTrialOverride(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial);
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- std::string enable_features;
- std::string disable_features;
- FeatureList::GetInstance()->GetFeatureOverrides(&enable_features,
- &disable_features);
- EXPECT_EQ("*OffByDefault<Trial,A,X", SortFeatureListString(enable_features));
- EXPECT_EQ("D", SortFeatureListString(disable_features));
- }
- TEST_F(FeatureListTest, GetFieldTrial) {
- FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group");
- auto feature_list = std::make_unique<FeatureList>();
- feature_list->RegisterFieldTrialOverride(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial);
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_EQ(trial, FeatureList::GetFieldTrial(kFeatureOnByDefault));
- EXPECT_EQ(nullptr, FeatureList::GetFieldTrial(kFeatureOffByDefault));
- }
- TEST_F(FeatureListTest, InitializeFromCommandLine_WithFieldTrials) {
- FieldTrialList::CreateFieldTrial("Trial", "Group");
- auto feature_list = std::make_unique<FeatureList>();
- feature_list->InitializeFromCommandLine("A,OffByDefault<Trial,X", "D");
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_FALSE(FieldTrialList::IsTrialActive("Trial"));
- EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
- EXPECT_TRUE(FieldTrialList::IsTrialActive("Trial"));
- }
- TEST_F(FeatureListTest, InitializeFromCommandLine_UseDefault) {
- FieldTrialList::CreateFieldTrial("T1", "Group");
- FieldTrialList::CreateFieldTrial("T2", "Group");
- auto feature_list = std::make_unique<FeatureList>();
- feature_list->InitializeFromCommandLine(
- "A,*OffByDefault<T1,*OnByDefault<T2,X", "D");
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_FALSE(FieldTrialList::IsTrialActive("T1"));
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
- EXPECT_TRUE(FieldTrialList::IsTrialActive("T1"));
- EXPECT_FALSE(FieldTrialList::IsTrialActive("T2"));
- EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
- EXPECT_TRUE(FieldTrialList::IsTrialActive("T2"));
- }
- TEST_F(FeatureListTest, InitializeInstance) {
- std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
- // Initialize from command line if we haven't yet.
- FeatureList::InitializeInstance("", kFeatureOnByDefaultName);
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
- // Do not initialize from commandline if we have already.
- FeatureList::InitializeInstance(kFeatureOffByDefaultName, "");
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
- }
- TEST_F(FeatureListTest, UninitializedInstance_IsEnabledReturnsFalse) {
- std::unique_ptr<FeatureList> original_feature_list =
- FeatureList::ClearInstanceForTesting();
- // This test case simulates the calling pattern found in code which does not
- // explicitly initialize the features list.
- // All IsEnabled() calls should return the default value in this scenario.
- EXPECT_EQ(nullptr, FeatureList::GetInstance());
- EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
- EXPECT_EQ(nullptr, FeatureList::GetInstance());
- EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
- if (original_feature_list)
- FeatureList::RestoreInstanceForTesting(std::move(original_feature_list));
- }
- TEST_F(FeatureListTest, StoreAndRetrieveFeaturesFromSharedMemory) {
- std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
- // Create some overrides.
- feature_list->RegisterOverride(kFeatureOffByDefaultName,
- FeatureList::OVERRIDE_ENABLE_FEATURE, nullptr);
- feature_list->RegisterOverride(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, nullptr);
- feature_list->FinalizeInitialization();
- // Create an allocator and store the overrides.
- base::MappedReadOnlyRegion shm =
- base::ReadOnlySharedMemoryRegion::Create(4 << 10);
- WritableSharedPersistentMemoryAllocator allocator(std::move(shm.mapping), 1,
- "");
- feature_list->AddFeaturesToAllocator(&allocator);
- std::unique_ptr<base::FeatureList> feature_list2(new base::FeatureList);
- // Check that the new feature list is empty.
- EXPECT_FALSE(feature_list2->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
- EXPECT_FALSE(feature_list2->IsFeatureOverriddenFromCommandLine(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
- feature_list2->InitializeFromSharedMemory(&allocator);
- // Check that the new feature list now has 2 overrides.
- EXPECT_TRUE(feature_list2->IsFeatureOverriddenFromCommandLine(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
- EXPECT_TRUE(feature_list2->IsFeatureOverriddenFromCommandLine(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
- }
- TEST_F(FeatureListTest, StoreAndRetrieveAssociatedFeaturesFromSharedMemory) {
- std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
- // Create some overrides.
- FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A");
- FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B");
- feature_list->RegisterFieldTrialOverride(
- kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial1);
- feature_list->RegisterFieldTrialOverride(
- kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial2);
- feature_list->FinalizeInitialization();
- // Create an allocator and store the overrides.
- base::MappedReadOnlyRegion shm =
- base::ReadOnlySharedMemoryRegion::Create(4 << 10);
- WritableSharedPersistentMemoryAllocator allocator(std::move(shm.mapping), 1,
- "");
- feature_list->AddFeaturesToAllocator(&allocator);
- std::unique_ptr<base::FeatureList> feature_list2(new base::FeatureList);
- feature_list2->InitializeFromSharedMemory(&allocator);
- feature_list2->FinalizeInitialization();
- // Check that the field trials are still associated.
- FieldTrial* associated_trial1 =
- feature_list2->GetAssociatedFieldTrial(kFeatureOnByDefault);
- FieldTrial* associated_trial2 =
- feature_list2->GetAssociatedFieldTrial(kFeatureOffByDefault);
- EXPECT_EQ(associated_trial1, trial1);
- EXPECT_EQ(associated_trial2, trial2);
- }
- #if BUILDFLAG(ENABLE_BANNED_BASE_FEATURE_PREFIX) && \
- defined(GTEST_HAS_DEATH_TEST)
- using FeatureListDeathTest = FeatureListTest;
- TEST_F(FeatureListDeathTest, DiesWithBadFeatureName) {
- EXPECT_DEATH(
- Feature(
- StrCat({BUILDFLAG(BANNED_BASE_FEATURE_PREFIX), "MyFeature"}).c_str(),
- FEATURE_DISABLED_BY_DEFAULT),
- StrCat({"Invalid feature name ", BUILDFLAG(BANNED_BASE_FEATURE_PREFIX),
- "MyFeature"}));
- }
- #endif // BUILDFLAG(ENABLE_BANNED_BASE_FEATURE_PREFIX) &&
- // defined(GTEST_HAS_DEATH_TEST)
- TEST(FeatureListAccessorTest, DefaultStates) {
- auto feature_list = std::make_unique<FeatureList>();
- auto feature_list_accessor = feature_list->ConstructAccessor();
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_EQ(feature_list_accessor->GetOverrideStateByFeatureName(
- kFeatureOnByDefault.name),
- FeatureList::OVERRIDE_USE_DEFAULT);
- EXPECT_EQ(feature_list_accessor->GetOverrideStateByFeatureName(
- kFeatureOffByDefault.name),
- FeatureList::OVERRIDE_USE_DEFAULT);
- }
- TEST(FeatureListAccessorTest, InitializeFromCommandLine) {
- struct {
- const char* enable_features;
- const char* disable_features;
- FeatureList::OverrideState expected_feature_on_state;
- FeatureList::OverrideState expected_feature_off_state;
- } test_cases[] = {
- {"", "", FeatureList::OVERRIDE_USE_DEFAULT,
- FeatureList::OVERRIDE_USE_DEFAULT},
- {"OffByDefault", "", FeatureList::OVERRIDE_USE_DEFAULT,
- FeatureList::OVERRIDE_ENABLE_FEATURE},
- {"OffByDefault", "OnByDefault", FeatureList::OVERRIDE_DISABLE_FEATURE,
- FeatureList::OVERRIDE_ENABLE_FEATURE},
- {"OnByDefault,OffByDefault", "", FeatureList::OVERRIDE_ENABLE_FEATURE,
- FeatureList::OVERRIDE_ENABLE_FEATURE},
- {"", "OnByDefault,OffByDefault", FeatureList::OVERRIDE_DISABLE_FEATURE,
- FeatureList::OVERRIDE_DISABLE_FEATURE},
- // In the case an entry is both, disable takes precedence.
- {"OnByDefault", "OnByDefault,OffByDefault",
- FeatureList::OVERRIDE_DISABLE_FEATURE,
- FeatureList::OVERRIDE_DISABLE_FEATURE},
- };
- for (size_t i = 0; i < std::size(test_cases); ++i) {
- const auto& test_case = test_cases[i];
- SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: [%s] [%s]", i,
- test_case.enable_features,
- test_case.disable_features));
- auto feature_list = std::make_unique<FeatureList>();
- auto feature_list_accessor = feature_list->ConstructAccessor();
- feature_list->InitializeFromCommandLine(test_case.enable_features,
- test_case.disable_features);
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_EQ(test_case.expected_feature_on_state,
- feature_list_accessor->GetOverrideStateByFeatureName(
- kFeatureOnByDefault.name))
- << i;
- EXPECT_EQ(test_case.expected_feature_off_state,
- feature_list_accessor->GetOverrideStateByFeatureName(
- kFeatureOffByDefault.name))
- << i;
- }
- }
- TEST(FeatureListAccessorTest, InitializeFromCommandLineWithFeatureParams) {
- struct {
- const std::string enable_features;
- const std::map<std::string, std::string> expected_feature_params;
- } test_cases[] = {
- {"Feature:x/100/y/test", {{"x", "100"}, {"y", "test"}}},
- {"Feature<Trial:asdf/ghjkl/y/123", {{"asdf", "ghjkl"}, {"y", "123"}}},
- };
- // Clear global state so that repeated runs of this test don't flake.
- // When https://crrev.com/c/3694674 is submitted, we should be able to remove
- // this.
- base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
- for (size_t i = 0; i < std::size(test_cases); ++i) {
- const auto& test_case = test_cases[i];
- SCOPED_TRACE(test_case.enable_features);
- auto feature_list = std::make_unique<FeatureList>();
- auto feature_list_accessor = feature_list->ConstructAccessor();
- feature_list->InitializeFromCommandLine(test_case.enable_features, "");
- test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- EXPECT_EQ(FeatureList::OVERRIDE_ENABLE_FEATURE,
- feature_list_accessor->GetOverrideStateByFeatureName("Feature"))
- << i;
- std::map<std::string, std::string> actual_params;
- EXPECT_TRUE(feature_list_accessor->GetParamsByFeatureName("Feature",
- &actual_params))
- << i;
- EXPECT_EQ(test_case.expected_feature_params, actual_params) << i;
- }
- }
- } // namespace base
|