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