1 // Copyright 2016 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_TEST_SCOPED_FEATURE_LIST_H_ 6 #define BASE_TEST_SCOPED_FEATURE_LIST_H_ 7 8 #include <memory> 9 #include <string> 10 #include <vector> 11 12 #include "base/containers/flat_map.h" 13 #include "base/feature_list.h" 14 #include "base/memory/raw_ptr.h" 15 #include "base/memory/raw_ref.h" 16 #include "base/metrics/field_trial.h" 17 #include "base/metrics/field_trial_params.h" 18 #include "base/types/pass_key.h" 19 #include "third_party/abseil-cpp/absl/base/attributes.h" 20 21 namespace base::test { 22 23 // A reference to a base::Feature and field trial params that should be force 24 // enabled and overwritten for test purposes. 25 struct FeatureRefAndParams { 26 FeatureRefAndParams(const Feature& feature ABSL_ATTRIBUTE_LIFETIME_BOUND, 27 const FieldTrialParams& params); 28 29 FeatureRefAndParams(const FeatureRefAndParams& other); 30 31 ~FeatureRefAndParams(); 32 33 const raw_ref<const Feature> feature; 34 const FieldTrialParams params; 35 }; 36 37 // A lightweight wrapper for a reference to a base::Feature. Allows lists of 38 // features to be enabled/disabled to be easily passed without actually copying 39 // the underlying base::Feature. Actual C++ references do not work well for this 40 // purpose, as vectors of references are disallowed. 41 class FeatureRef { 42 public: 43 // NOLINTNEXTLINE(google-explicit-constructor) FeatureRef(const Feature & feature ABSL_ATTRIBUTE_LIFETIME_BOUND)44 FeatureRef(const Feature& feature ABSL_ATTRIBUTE_LIFETIME_BOUND) 45 : feature_(feature) {} 46 47 const Feature& operator*() const { return *feature_; } 48 const Feature* operator->() const { return &*feature_; } 49 50 private: 51 friend bool operator==(const FeatureRef& lhs, const FeatureRef& rhs) { 52 return &*lhs == &*rhs; 53 } 54 friend bool operator<(const FeatureRef& lhs, const FeatureRef& rhs) { 55 return &*lhs < &*rhs; 56 } 57 58 raw_ref<const Feature> feature_; 59 }; 60 61 // ScopedFeatureList resets the global FeatureList instance to a new instance 62 // and restores the original instance upon destruction. Whether the existing 63 // FeatureList state is kept or discarded depends on the `Init` method called. 64 // When using the non-deprecated APIs, a corresponding FieldTrialList is also 65 // created. 66 // 67 // Note: Re-using the same object is allowed. To reset the feature list and 68 // initialize it anew, call `Reset` and then one of the `Init` methods. 69 // 70 // If multiple instances of this class are used in a nested fashion, they 71 // should be destroyed in the opposite order of their Init*() methods being 72 // called. 73 // 74 // ScopedFeatureList needs to be initialized on the main thread (via one of 75 // Init*() methods) before running code that inspects the state of features, 76 // such as in the constructor of the test harness. 77 // 78 // WARNING: To be clear, in multithreaded test environments (such as browser 79 // tests) there may background threads using FeatureList before the test body is 80 // even entered. In these cases it is imperative that ScopedFeatureList be 81 // initialized BEFORE those threads are started, hence the recommendation to do 82 // initialization in the test harness's constructor. 83 class ScopedFeatureList final { 84 public: 85 struct Features; 86 struct FeatureWithStudyGroup; 87 88 // Constructs the instance in a non-initialized state. 89 ScopedFeatureList(); 90 91 // Shorthand for immediately initializing with InitAndEnableFeature(). 92 explicit ScopedFeatureList(const Feature& enable_feature); 93 94 ScopedFeatureList(const ScopedFeatureList&) = delete; 95 ScopedFeatureList& operator=(const ScopedFeatureList&) = delete; 96 97 ~ScopedFeatureList(); 98 99 // Resets the instance to a non-initialized state. 100 void Reset(); 101 102 // Initializes and registers a FeatureList instance without any additional 103 // enabled or disabled features. Existing state, if any, will be kept. 104 // This is equivalent to calling InitWithFeatures({}, {}). 105 void Init(); 106 107 // Initializes a FeatureList instance without any additional enabled or 108 // disabled features. Existing state, if any, will be discarded. 109 // Using this function is not generally recommended, as doing so in a test 110 // removes the ability to run the test while passing additional 111 // --enable-features flags from the command line. 112 void InitWithEmptyFeatureAndFieldTrialLists(); 113 114 // Initializes a FeatureList instance and FieldTrialLists to be null and 115 // clear all field trial parameters. 116 // WARNING: This should not be generally used except for tests that require 117 // manually instantiating objects like FieldTrialList, for example when 118 // mocking an EntropyProvider. 119 void InitWithNullFeatureAndFieldTrialLists(); 120 121 // WARNING: This method will reset any globally configured features to their 122 // default values, which can hide feature interaction bugs. Please use 123 // sparingly. https://crbug.com/713390 124 // Initializes and registers the given FeatureList instance. 125 void InitWithFeatureList(std::unique_ptr<FeatureList> feature_list); 126 127 // Initializes and registers a FeatureList instance based on the current 128 // FeatureList and overridden with the given enabled features and the 129 // specified field trial parameters, and the given disabled features 130 // with the given enabled and disabled features (comma-separated names). 131 // Note: This creates a scoped global field trial list if there is not 132 // currently one. 133 void InitFromCommandLine(const std::string& enable_features, 134 const std::string& disable_features); 135 136 // Initializes and registers a FeatureList instance based on the current 137 // FeatureList and overridden with the given enabled and disabled features. 138 // Any feature overrides already present in the global FeatureList will 139 // continue to apply, unless they conflict with the overrides passed into this 140 // method. This is important for testing potentially unexpected feature 141 // interactions. 142 void InitWithFeatures(const std::vector<FeatureRef>& enabled_features, 143 const std::vector<FeatureRef>& disabled_features); 144 145 // Initializes and registers a FeatureList instance based on the current 146 // FeatureList and overridden with single enabled feature. 147 void InitAndEnableFeature(const Feature& feature); 148 149 // Initializes and registers a FeatureList instance based on the current 150 // FeatureList and overridden with single enabled feature and associated field 151 // trial parameters. 152 // Note: this creates a scoped global field trial list if there is not 153 // currently one. 154 void InitAndEnableFeatureWithParameters( 155 const Feature& feature, 156 const FieldTrialParams& feature_parameters); 157 158 // Initializes and registers a FeatureList instance based on the current 159 // FeatureList and overridden with the given enabled features and the 160 // specified field trial parameters, and the given disabled features. 161 // Note: This creates a scoped global field trial list if there is not 162 // currently one. 163 void InitWithFeaturesAndParameters( 164 const std::vector<FeatureRefAndParams>& enabled_features, 165 const std::vector<FeatureRef>& disabled_features); 166 167 // Initializes and registers a FeatureList instance based on the current 168 // FeatureList and overridden with single disabled feature. 169 void InitAndDisableFeature(const Feature& feature); 170 171 // Initializes and registers a FeatureList instance based on the current 172 // FeatureList and overridden with a single feature either enabled or 173 // disabled depending on |enabled|. 174 void InitWithFeatureState(const Feature& feature, bool enabled); 175 176 // Same as `InitWithFeatureState()`, but supports multiple features at a time. 177 // `feature_states` - a map where the keys are features and the values are 178 // their overridden states (`false` for force-disabled, 179 // `true` for force-enabled). 180 void InitWithFeatureStates(const flat_map<FeatureRef, bool>& feature_states); 181 182 private: 183 using PassKey = base::PassKey<ScopedFeatureList>; 184 185 // Initializes and registers a FeatureList instance based on the current 186 // FeatureList and overridden with the given enabled and disabled features. 187 // Any feature overrides already present in the global FeatureList will 188 // continue to apply, unless they conflict with the overrides passed into this 189 // method. 190 // Features to enable may be specified through either |enabled_features| or 191 // |enabled_feature_and_params|, but not both (i.e. one of these must be 192 // empty). 193 void InitWithFeaturesImpl( 194 const std::vector<FeatureRef>& enabled_features, 195 const std::vector<FeatureRefAndParams>& enabled_features_and_params, 196 const std::vector<FeatureRef>& disabled_features, 197 bool keep_existing_states = true); 198 199 // Initializes and registers a FeatureList instance based on the current 200 // FeatureList and overridden with the given enabled and disabled features. 201 // Any feature overrides already present in the global FeatureList will 202 // continue to apply, unless they conflict with the overrides passed into this 203 // method. 204 // If |create_associated_field_trials| is true, associated field trials are 205 // always created independent of feature parameters. If false, field trials 206 // for features whose parameters are specified will be created. 207 // If |keep_existing_states| is true, keep all states and override them 208 // according to the |merged_features|. Otherwise, clear all states and 209 // newly initialize all states with |merged_features|. 210 void InitWithMergedFeatures(Features&& merged_features, 211 bool create_associated_field_trials, 212 bool keep_existing_states); 213 214 bool init_called_ = false; 215 std::unique_ptr<FeatureList> original_feature_list_; 216 raw_ptr<base::FieldTrialList> original_field_trial_list_ = nullptr; 217 std::string original_params_; 218 std::unique_ptr<base::FieldTrialList> field_trial_list_; 219 }; 220 221 } // namespace base::test 222 223 #endif // BASE_TEST_SCOPED_FEATURE_LIST_H_ 224