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
|
// 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_
|