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