1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
base / test / scoped_feature_list.h [blame]
// 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 <memory>
#include <string>
#include <vector>
#include "base/compiler_specific.h"
#include "base/containers/flat_map.h"
#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"
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 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 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 instance
// and restores the original instance upon destruction. Whether the existing
// FeatureList state is kept or discarded depends on the `Init` method called.
// 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);
// Same as `InitWithFeatureState()`, but supports multiple features at a time.
// `feature_states` - a map where the keys are features and the values are
// their overridden states (`false` for force-disabled,
// `true` for force-enabled).
void InitWithFeatureStates(const flat_map<FeatureRef, bool>& feature_states);
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_