| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef BASE_TEST_SCOPED_FEATURE_LIST_H_ |
| #define BASE_TEST_SCOPED_FEATURE_LIST_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/feature_list.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/raw_ref.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/metrics/field_trial_params.h" |
| #include "base/types/pass_key.h" |
| #include "third_party/abseil-cpp/absl/base/attributes.h" |
| |
| namespace base::test { |
| |
| // A reference to a base::Feature and field trial params that should be force |
| // enabled and overwritten for test purposes. |
| struct FeatureRefAndParams { |
| FeatureRefAndParams(const Feature& feature ABSL_ATTRIBUTE_LIFETIME_BOUND, |
| const FieldTrialParams& params); |
| |
| FeatureRefAndParams(const FeatureRefAndParams& other); |
| |
| ~FeatureRefAndParams(); |
| |
| const raw_ref<const Feature> feature; |
| const FieldTrialParams params; |
| }; |
| |
| // A lightweight wrapper for a reference to a base::Feature. Allows lists of |
| // features to be enabled/disabled to be easily passed without actually copying |
| // the underlying base::Feature. Actual C++ references do not work well for this |
| // purpose, as vectors of references are disallowed. |
| class FeatureRef { |
| public: |
| // NOLINTNEXTLINE(google-explicit-constructor) |
| FeatureRef(const Feature& feature ABSL_ATTRIBUTE_LIFETIME_BOUND) |
| : feature_(feature) {} |
| |
| const Feature& operator*() const { return *feature_; } |
| const Feature* operator->() const { return &*feature_; } |
| |
| private: |
| friend bool operator==(const FeatureRef& lhs, const FeatureRef& rhs) { |
| return &*lhs == &*rhs; |
| } |
| friend bool operator<(const FeatureRef& lhs, const FeatureRef& rhs) { |
| return &*lhs < &*rhs; |
| } |
| |
| raw_ref<const Feature> feature_; |
| }; |
| |
| // ScopedFeatureList resets the global FeatureList instance to a new empty |
| // instance and restores the original instance upon destruction. When using the |
| // non-deprecated APIs, a corresponding FieldTrialList is also created. |
| // |
| // Note: Re-using the same object is allowed. To reset the feature list and |
| // initialize it anew, call `Reset` and then one of the `Init` methods. |
| // |
| // If multiple instances of this class are used in a nested fashion, they |
| // should be destroyed in the opposite order of their Init*() methods being |
| // called. |
| // |
| // ScopedFeatureList needs to be initialized on the main thread (via one of |
| // Init*() methods) before running code that inspects the state of features, |
| // such as in the constructor of the test harness. |
| // |
| // WARNING: To be clear, in multithreaded test environments (such as browser |
| // tests) there may background threads using FeatureList before the test body is |
| // even entered. In these cases it is imperative that ScopedFeatureList be |
| // initialized BEFORE those threads are started, hence the recommendation to do |
| // initialization in the test harness's constructor. |
| class ScopedFeatureList final { |
| public: |
| struct Features; |
| struct FeatureWithStudyGroup; |
| |
| // Constructs the instance in a non-initialized state. |
| ScopedFeatureList(); |
| |
| // Shorthand for immediately initializing with InitAndEnableFeature(). |
| explicit ScopedFeatureList(const Feature& enable_feature); |
| |
| ScopedFeatureList(const ScopedFeatureList&) = delete; |
| ScopedFeatureList& operator=(const ScopedFeatureList&) = delete; |
| |
| ~ScopedFeatureList(); |
| |
| // Resets the instance to a non-initialized state. |
| void Reset(); |
| |
| // Initializes and registers a FeatureList instance without any additional |
| // enabled or disabled features. Existing state, if any, will be kept. |
| // This is equivalent to calling InitWithFeatures({}, {}). |
| void Init(); |
| |
| // Initializes a FeatureList instance without any additional enabled or |
| // disabled features. Existing state, if any, will be discarded. |
| // Using this function is not generally recommended, as doing so in a test |
| // removes the ability to run the test while passing additional |
| // --enable-features flags from the command line. |
| void InitWithEmptyFeatureAndFieldTrialLists(); |
| |
| // Initializes a FeatureList instance and FieldTrialLists to be null and |
| // clear all field trial parameters. |
| // WARNING: This should not be generally used except for tests that require |
| // manually instantiating objects like FieldTrialList, for example when |
| // mocking an EntropyProvider. |
| void InitWithNullFeatureAndFieldTrialLists(); |
| |
| // WARNING: This method will reset any globally configured features to their |
| // default values, which can hide feature interaction bugs. Please use |
| // sparingly. https://crbug.com/713390 |
| // Initializes and registers the given FeatureList instance. |
| void InitWithFeatureList(std::unique_ptr<FeatureList> feature_list); |
| |
| // Initializes and registers a FeatureList instance based on the current |
| // FeatureList and overridden with the given enabled features and the |
| // specified field trial parameters, and the given disabled features |
| // with the given enabled and disabled features (comma-separated names). |
| // Note: This creates a scoped global field trial list if there is not |
| // currently one. |
| void InitFromCommandLine(const std::string& enable_features, |
| const std::string& disable_features); |
| |
| // Initializes and registers a FeatureList instance based on the current |
| // FeatureList and overridden with the given enabled and disabled features. |
| // Any feature overrides already present in the global FeatureList will |
| // continue to apply, unless they conflict with the overrides passed into this |
| // method. This is important for testing potentially unexpected feature |
| // interactions. |
| void InitWithFeatures(const std::vector<FeatureRef>& enabled_features, |
| const std::vector<FeatureRef>& disabled_features); |
| |
| // Initializes and registers a FeatureList instance based on the current |
| // FeatureList and overridden with single enabled feature. |
| void InitAndEnableFeature(const Feature& feature); |
| |
| // Initializes and registers a FeatureList instance based on the current |
| // FeatureList and overridden with single enabled feature and associated field |
| // trial parameters. |
| // Note: this creates a scoped global field trial list if there is not |
| // currently one. |
| void InitAndEnableFeatureWithParameters( |
| const Feature& feature, |
| const FieldTrialParams& feature_parameters); |
| |
| // Initializes and registers a FeatureList instance based on the current |
| // FeatureList and overridden with the given enabled features and the |
| // specified field trial parameters, and the given disabled features. |
| // Note: This creates a scoped global field trial list if there is not |
| // currently one. |
| void InitWithFeaturesAndParameters( |
| const std::vector<FeatureRefAndParams>& enabled_features, |
| const std::vector<FeatureRef>& disabled_features); |
| |
| // Initializes and registers a FeatureList instance based on the current |
| // FeatureList and overridden with single disabled feature. |
| void InitAndDisableFeature(const Feature& feature); |
| |
| // Initializes and registers a FeatureList instance based on the current |
| // FeatureList and overridden with a single feature either enabled or |
| // disabled depending on |enabled|. |
| void InitWithFeatureState(const Feature& feature, bool enabled); |
| |
| private: |
| using PassKey = base::PassKey<ScopedFeatureList>; |
| |
| // Initializes and registers a FeatureList instance based on the current |
| // FeatureList and overridden with the given enabled and disabled features. |
| // Any feature overrides already present in the global FeatureList will |
| // continue to apply, unless they conflict with the overrides passed into this |
| // method. |
| // Features to enable may be specified through either |enabled_features| or |
| // |enabled_feature_and_params|, but not both (i.e. one of these must be |
| // empty). |
| void InitWithFeaturesImpl( |
| const std::vector<FeatureRef>& enabled_features, |
| const std::vector<FeatureRefAndParams>& enabled_features_and_params, |
| const std::vector<FeatureRef>& disabled_features, |
| bool keep_existing_states = true); |
| |
| // Initializes and registers a FeatureList instance based on the current |
| // FeatureList and overridden with the given enabled and disabled features. |
| // Any feature overrides already present in the global FeatureList will |
| // continue to apply, unless they conflict with the overrides passed into this |
| // method. |
| // If |create_associated_field_trials| is true, associated field trials are |
| // always created independent of feature parameters. If false, field trials |
| // for features whose parameters are specified will be created. |
| // If |keep_existing_states| is true, keep all states and override them |
| // according to the |merged_features|. Otherwise, clear all states and |
| // newly initialize all states with |merged_features|. |
| void InitWithMergedFeatures(Features&& merged_features, |
| bool create_associated_field_trials, |
| bool keep_existing_states); |
| |
| bool init_called_ = false; |
| std::unique_ptr<FeatureList> original_feature_list_; |
| raw_ptr<base::FieldTrialList> original_field_trial_list_ = nullptr; |
| std::string original_params_; |
| std::unique_ptr<base::FieldTrialList> field_trial_list_; |
| }; |
| |
| } // namespace base::test |
| |
| #endif // BASE_TEST_SCOPED_FEATURE_LIST_H_ |