1 // Copyright 2015 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 #include "base/feature_list.h"
6
7 #include <stddef.h>
8
9 #include <utility>
10 #include <vector>
11
12 #include "base/feature_list_buildflags.h"
13 #include "base/format_macros.h"
14 #include "base/memory/read_only_shared_memory_region.h"
15 #include "base/metrics/field_trial.h"
16 #include "base/metrics/field_trial_param_associator.h"
17 #include "base/metrics/persistent_memory_allocator.h"
18 #include "base/ranges/algorithm.h"
19 #include "base/strings/strcat.h"
20 #include "base/strings/string_piece.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/test/scoped_feature_list.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 namespace base {
27
28 namespace {
29
30 constexpr char kFeatureOnByDefaultName[] = "OnByDefault";
31 CONSTINIT Feature kFeatureOnByDefault(kFeatureOnByDefaultName,
32 FEATURE_ENABLED_BY_DEFAULT);
33
34 constexpr char kFeatureOffByDefaultName[] = "OffByDefault";
35 CONSTINIT Feature kFeatureOffByDefault(kFeatureOffByDefaultName,
36 FEATURE_DISABLED_BY_DEFAULT);
37
SortFeatureListString(const std::string & feature_list)38 std::string SortFeatureListString(const std::string& feature_list) {
39 std::vector<base::StringPiece> features =
40 FeatureList::SplitFeatureListString(feature_list);
41 ranges::sort(features);
42 return JoinString(features, ",");
43 }
44
45 } // namespace
46
47 class FeatureListTest : public testing::Test {
48 public:
FeatureListTest()49 FeatureListTest() {
50 // Provide an empty FeatureList to each test by default.
51 scoped_feature_list_.InitWithFeatureList(std::make_unique<FeatureList>());
52 }
53 FeatureListTest(const FeatureListTest&) = delete;
54 FeatureListTest& operator=(const FeatureListTest&) = delete;
55 ~FeatureListTest() override = default;
56
57 private:
58 test::ScopedFeatureList scoped_feature_list_;
59 };
60
TEST_F(FeatureListTest,DefaultStates)61 TEST_F(FeatureListTest, DefaultStates) {
62 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
63 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
64 }
65
TEST_F(FeatureListTest,InitFromCommandLine)66 TEST_F(FeatureListTest, InitFromCommandLine) {
67 struct {
68 const char* enable_features;
69 const char* disable_features;
70 bool expected_feature_on_state;
71 bool expected_feature_off_state;
72 } test_cases[] = {
73 {"", "", true, false},
74 {"OffByDefault", "", true, true},
75 {"OffByDefault", "OnByDefault", false, true},
76 {"OnByDefault,OffByDefault", "", true, true},
77 {"", "OnByDefault,OffByDefault", false, false},
78 // In the case an entry is both, disable takes precedence.
79 {"OnByDefault", "OnByDefault,OffByDefault", false, false},
80 };
81
82 for (size_t i = 0; i < std::size(test_cases); ++i) {
83 const auto& test_case = test_cases[i];
84 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: [%s] [%s]", i,
85 test_case.enable_features,
86 test_case.disable_features));
87
88 auto feature_list = std::make_unique<FeatureList>();
89 feature_list->InitFromCommandLine(test_case.enable_features,
90 test_case.disable_features);
91 test::ScopedFeatureList scoped_feature_list;
92 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
93
94 EXPECT_EQ(test_case.expected_feature_on_state,
95 FeatureList::IsEnabled(kFeatureOnByDefault))
96 << i;
97 EXPECT_EQ(test_case.expected_feature_off_state,
98 FeatureList::IsEnabled(kFeatureOffByDefault))
99 << i;
100
101 // Reading the state of each feature again will pull it from their
102 // respective caches instead of performing the full lookup, which should
103 // yield the same result.
104 EXPECT_EQ(test_case.expected_feature_on_state,
105 FeatureList::IsEnabled(kFeatureOnByDefault))
106 << i;
107 EXPECT_EQ(test_case.expected_feature_off_state,
108 FeatureList::IsEnabled(kFeatureOffByDefault))
109 << i;
110 }
111 }
112
TEST_F(FeatureListTest,InitFromCommandLineWithFeatureParams)113 TEST_F(FeatureListTest, InitFromCommandLineWithFeatureParams) {
114 struct {
115 const std::string enable_features;
116 const std::string expected_field_trial_created;
117 const std::map<std::string, std::string> expected_feature_params;
118 } test_cases[] = {
119 {"Feature:x/100/y/test", "StudyFeature", {{"x", "100"}, {"y", "test"}}},
120 {"Feature<Trial1:x/200/y/123", "Trial1", {{"x", "200"}, {"y", "123"}}},
121 {"Feature<Trial2.Group2:x/test/y/uma/z/ukm",
122 "Trial2",
123 {{"x", "test"}, {"y", "uma"}, {"z", "ukm"}}},
124 };
125
126 // Clear global state so that repeated runs of this test don't flake.
127 // When https://crrev.com/c/3694674 is submitted, we should be able to remove
128 // this.
129 base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
130
131 static BASE_FEATURE(kFeature, "Feature", FEATURE_DISABLED_BY_DEFAULT);
132 for (const auto& test_case : test_cases) {
133 SCOPED_TRACE(test_case.enable_features);
134
135 auto feature_list = std::make_unique<FeatureList>();
136 feature_list->InitFromCommandLine(test_case.enable_features, "");
137 test::ScopedFeatureList scoped_feature_list;
138 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
139
140 EXPECT_TRUE(FeatureList::IsEnabled(kFeature));
141 EXPECT_TRUE(
142 FieldTrialList::IsTrialActive(test_case.expected_field_trial_created));
143 std::map<std::string, std::string> actual_params;
144 EXPECT_TRUE(GetFieldTrialParamsByFeature(kFeature, &actual_params));
145 EXPECT_EQ(test_case.expected_feature_params, actual_params);
146 }
147 }
148
TEST_F(FeatureListTest,CheckFeatureIdentity)149 TEST_F(FeatureListTest, CheckFeatureIdentity) {
150 // Tests that CheckFeatureIdentity() correctly detects when two different
151 // structs with the same feature name are passed to it.
152
153 test::ScopedFeatureList scoped_feature_list;
154 scoped_feature_list.InitWithFeatureList(std::make_unique<FeatureList>());
155 FeatureList* feature_list = FeatureList::GetInstance();
156
157 // Call it twice for each feature at the top of the file, since the first call
158 // makes it remember the entry and the second call will verify it.
159 EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault));
160 EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault));
161 EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOffByDefault));
162 EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOffByDefault));
163
164 // Now, call it with a distinct struct for |kFeatureOnByDefaultName|, which
165 // should return false.
166 struct Feature kFeatureOnByDefault2 {
167 kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT
168 };
169 EXPECT_FALSE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault2));
170 }
171
TEST_F(FeatureListTest,FieldTrialOverrides)172 TEST_F(FeatureListTest, FieldTrialOverrides) {
173 struct {
174 FeatureList::OverrideState trial1_state;
175 FeatureList::OverrideState trial2_state;
176 } test_cases[] = {
177 {FeatureList::OVERRIDE_DISABLE_FEATURE,
178 FeatureList::OVERRIDE_DISABLE_FEATURE},
179 {FeatureList::OVERRIDE_DISABLE_FEATURE,
180 FeatureList::OVERRIDE_ENABLE_FEATURE},
181 {FeatureList::OVERRIDE_ENABLE_FEATURE,
182 FeatureList::OVERRIDE_DISABLE_FEATURE},
183 {FeatureList::OVERRIDE_ENABLE_FEATURE,
184 FeatureList::OVERRIDE_ENABLE_FEATURE},
185 };
186
187 FieldTrial::ActiveGroup active_group;
188 for (size_t i = 0; i < std::size(test_cases); ++i) {
189 const auto& test_case = test_cases[i];
190 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i));
191
192 test::ScopedFeatureList outer_scope;
193 outer_scope.InitWithEmptyFeatureAndFieldTrialLists();
194
195 auto feature_list = std::make_unique<FeatureList>();
196
197 FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A");
198 FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B");
199 feature_list->RegisterFieldTrialOverride(kFeatureOnByDefaultName,
200 test_case.trial1_state, trial1);
201 feature_list->RegisterFieldTrialOverride(kFeatureOffByDefaultName,
202 test_case.trial2_state, trial2);
203 test::ScopedFeatureList scoped_feature_list;
204 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
205
206 // Initially, neither trial should be active.
207 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name()));
208 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name()));
209
210 const bool expected_enabled_1 =
211 (test_case.trial1_state == FeatureList::OVERRIDE_ENABLE_FEATURE);
212 EXPECT_EQ(expected_enabled_1, FeatureList::IsEnabled(kFeatureOnByDefault));
213 // The above should have activated |trial1|.
214 EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name()));
215 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name()));
216
217 const bool expected_enabled_2 =
218 (test_case.trial2_state == FeatureList::OVERRIDE_ENABLE_FEATURE);
219 EXPECT_EQ(expected_enabled_2, FeatureList::IsEnabled(kFeatureOffByDefault));
220 // The above should have activated |trial2|.
221 EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name()));
222 EXPECT_TRUE(FieldTrialList::IsTrialActive(trial2->trial_name()));
223 }
224 }
225
TEST_F(FeatureListTest,FieldTrialAssociateUseDefault)226 TEST_F(FeatureListTest, FieldTrialAssociateUseDefault) {
227 auto feature_list = std::make_unique<FeatureList>();
228
229 FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A");
230 FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B");
231 feature_list->RegisterFieldTrialOverride(
232 kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial1);
233 feature_list->RegisterFieldTrialOverride(
234 kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial2);
235 test::ScopedFeatureList scoped_feature_list;
236 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
237
238 // Initially, neither trial should be active.
239 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name()));
240 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name()));
241
242 // Check the feature enabled state is its default.
243 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
244 // The above should have activated |trial1|.
245 EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name()));
246 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name()));
247
248 // Check the feature enabled state is its default.
249 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
250 // The above should have activated |trial2|.
251 EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name()));
252 EXPECT_TRUE(FieldTrialList::IsTrialActive(trial2->trial_name()));
253 }
254
TEST_F(FeatureListTest,CommandLineEnableTakesPrecedenceOverFieldTrial)255 TEST_F(FeatureListTest, CommandLineEnableTakesPrecedenceOverFieldTrial) {
256 auto feature_list = std::make_unique<FeatureList>();
257
258 // The feature is explicitly enabled on the command-line.
259 feature_list->InitFromCommandLine(kFeatureOffByDefaultName, "");
260
261 // But the FieldTrial would set the feature to disabled.
262 FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample2", "A");
263 feature_list->RegisterFieldTrialOverride(
264 kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, trial);
265 test::ScopedFeatureList scoped_feature_list;
266 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
267
268 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name()));
269 // Command-line should take precedence.
270 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
271 // Since the feature is on due to the command-line, and not as a result of the
272 // field trial, the field trial should not be activated (since the Associate*
273 // API wasn't used.)
274 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name()));
275 }
276
TEST_F(FeatureListTest,CommandLineDisableTakesPrecedenceOverFieldTrial)277 TEST_F(FeatureListTest, CommandLineDisableTakesPrecedenceOverFieldTrial) {
278 auto feature_list = std::make_unique<FeatureList>();
279
280 // The feature is explicitly disabled on the command-line.
281 feature_list->InitFromCommandLine("", kFeatureOffByDefaultName);
282
283 // But the FieldTrial would set the feature to enabled.
284 FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample2", "A");
285 feature_list->RegisterFieldTrialOverride(
286 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE, trial);
287 test::ScopedFeatureList scoped_feature_list;
288 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
289
290 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name()));
291 // Command-line should take precedence.
292 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
293 // Since the feature is on due to the command-line, and not as a result of the
294 // field trial, the field trial should not be activated (since the Associate*
295 // API wasn't used.)
296 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name()));
297 }
298
TEST_F(FeatureListTest,IsFeatureOverriddenFromFieldTrial)299 TEST_F(FeatureListTest, IsFeatureOverriddenFromFieldTrial) {
300 auto feature_list = std::make_unique<FeatureList>();
301
302 // No features are overridden from the field trails yet.
303 EXPECT_FALSE(feature_list->IsFeatureOverridden(kFeatureOnByDefaultName));
304 EXPECT_FALSE(feature_list->IsFeatureOverridden(kFeatureOffByDefaultName));
305
306 // Now, register a field trial to override |kFeatureOnByDefaultName| state
307 // and check that the function still returns false for that feature.
308 feature_list->RegisterFieldTrialOverride(
309 kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT,
310 FieldTrialList::CreateFieldTrial("Trial1", "A"));
311 feature_list->RegisterFieldTrialOverride(
312 kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE,
313 FieldTrialList::CreateFieldTrial("Trial2", "A"));
314 EXPECT_TRUE(feature_list->IsFeatureOverridden(kFeatureOnByDefaultName));
315 EXPECT_TRUE(feature_list->IsFeatureOverridden(kFeatureOffByDefaultName));
316
317 test::ScopedFeatureList scoped_feature_list;
318 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
319 // Check the expected feature states for good measure.
320 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
321 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
322 }
323
TEST_F(FeatureListTest,IsFeatureOverriddenFromCommandLine)324 TEST_F(FeatureListTest, IsFeatureOverriddenFromCommandLine) {
325 auto feature_list = std::make_unique<FeatureList>();
326
327 // No features are overridden from the command line yet
328 EXPECT_FALSE(feature_list->IsFeatureOverridden(kFeatureOnByDefaultName));
329 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
330 kFeatureOnByDefaultName));
331 EXPECT_FALSE(feature_list->IsFeatureOverridden(kFeatureOffByDefaultName));
332 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
333 kFeatureOffByDefaultName));
334 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
335 kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
336 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
337 kFeatureOnByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
338 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
339 kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
340 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
341 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
342
343 // Now, enable |kFeatureOffByDefaultName| via the command-line.
344 feature_list->InitFromCommandLine(kFeatureOffByDefaultName, "");
345
346 // It should now be overridden for the enabled group.
347 EXPECT_TRUE(feature_list->IsFeatureOverridden(kFeatureOffByDefaultName));
348 EXPECT_TRUE(feature_list->IsFeatureOverriddenFromCommandLine(
349 kFeatureOffByDefaultName));
350 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
351 kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
352 EXPECT_TRUE(feature_list->IsFeatureOverriddenFromCommandLine(
353 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
354
355 // Register a field trial to associate with the feature and ensure that the
356 // results are still the same.
357 feature_list->AssociateReportingFieldTrial(
358 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE,
359 FieldTrialList::CreateFieldTrial("Trial1", "A"));
360 EXPECT_TRUE(feature_list->IsFeatureOverridden(kFeatureOffByDefaultName));
361 EXPECT_TRUE(feature_list->IsFeatureOverriddenFromCommandLine(
362 kFeatureOffByDefaultName));
363 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
364 kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
365 EXPECT_TRUE(feature_list->IsFeatureOverriddenFromCommandLine(
366 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
367
368 // Now, register a field trial to override |kFeatureOnByDefaultName| state
369 // and check that the function still returns false for that feature.
370 feature_list->RegisterFieldTrialOverride(
371 kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE,
372 FieldTrialList::CreateFieldTrial("Trial2", "A"));
373 EXPECT_TRUE(feature_list->IsFeatureOverridden(kFeatureOnByDefaultName));
374 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
375 kFeatureOnByDefaultName));
376 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
377 kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
378 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
379 kFeatureOnByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
380 test::ScopedFeatureList scoped_feature_list;
381 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
382
383 // Check the expected feature states for good measure.
384 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
385 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
386 }
387
TEST_F(FeatureListTest,AssociateReportingFieldTrial)388 TEST_F(FeatureListTest, AssociateReportingFieldTrial) {
389 struct {
390 const char* enable_features;
391 const char* disable_features;
392 bool expected_enable_trial_created;
393 bool expected_disable_trial_created;
394 } test_cases[] = {
395 // If no enable/disable flags are specified, no trials should be created.
396 {"", "", false, false},
397 // Enabling the feature should result in the enable trial created.
398 {kFeatureOffByDefaultName, "", true, false},
399 // Disabling the feature should result in the disable trial created.
400 {"", kFeatureOffByDefaultName, false, true},
401 };
402
403 const char kTrialName[] = "ForcingTrial";
404 const char kForcedOnGroupName[] = "ForcedOn";
405 const char kForcedOffGroupName[] = "ForcedOff";
406
407 for (size_t i = 0; i < std::size(test_cases); ++i) {
408 const auto& test_case = test_cases[i];
409 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: [%s] [%s]", i,
410 test_case.enable_features,
411 test_case.disable_features));
412
413 test::ScopedFeatureList outer_scope;
414 outer_scope.InitWithEmptyFeatureAndFieldTrialLists();
415
416 auto feature_list = std::make_unique<FeatureList>();
417 feature_list->InitFromCommandLine(test_case.enable_features,
418 test_case.disable_features);
419
420 FieldTrial* enable_trial = nullptr;
421 if (feature_list->IsFeatureOverriddenFromCommandLine(
422 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)) {
423 enable_trial = base::FieldTrialList::CreateFieldTrial(kTrialName,
424 kForcedOnGroupName);
425 feature_list->AssociateReportingFieldTrial(
426 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE,
427 enable_trial);
428 }
429 FieldTrial* disable_trial = nullptr;
430 if (feature_list->IsFeatureOverriddenFromCommandLine(
431 kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)) {
432 disable_trial = base::FieldTrialList::CreateFieldTrial(
433 kTrialName, kForcedOffGroupName);
434 feature_list->AssociateReportingFieldTrial(
435 kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE,
436 disable_trial);
437 }
438 EXPECT_EQ(test_case.expected_enable_trial_created, enable_trial != nullptr);
439 EXPECT_EQ(test_case.expected_disable_trial_created,
440 disable_trial != nullptr);
441 test::ScopedFeatureList scoped_feature_list;
442 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
443
444 EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
445 if (disable_trial) {
446 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
447 EXPECT_TRUE(FieldTrialList::IsTrialActive(kTrialName));
448 EXPECT_EQ(kForcedOffGroupName, disable_trial->group_name());
449 } else if (enable_trial) {
450 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
451 EXPECT_TRUE(FieldTrialList::IsTrialActive(kTrialName));
452 EXPECT_EQ(kForcedOnGroupName, enable_trial->group_name());
453 }
454 }
455 }
456
TEST_F(FeatureListTest,RegisterExtraFeatureOverrides)457 TEST_F(FeatureListTest, RegisterExtraFeatureOverrides) {
458 auto feature_list = std::make_unique<FeatureList>();
459 std::vector<FeatureList::FeatureOverrideInfo> overrides;
460 overrides.push_back({std::cref(kFeatureOnByDefault),
461 FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE});
462 overrides.push_back({std::cref(kFeatureOffByDefault),
463 FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE});
464 feature_list->RegisterExtraFeatureOverrides(std::move(overrides));
465 test::ScopedFeatureList scoped_feature_list;
466 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
467
468 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
469 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
470 }
471
TEST_F(FeatureListTest,InitFromCommandLineThenRegisterExtraOverrides)472 TEST_F(FeatureListTest, InitFromCommandLineThenRegisterExtraOverrides) {
473 auto feature_list = std::make_unique<FeatureList>();
474 feature_list->InitFromCommandLine(kFeatureOnByDefaultName,
475 kFeatureOffByDefaultName);
476 std::vector<FeatureList::FeatureOverrideInfo> overrides;
477 overrides.push_back({std::cref(kFeatureOnByDefault),
478 FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE});
479 overrides.push_back({std::cref(kFeatureOffByDefault),
480 FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE});
481 feature_list->RegisterExtraFeatureOverrides(std::move(overrides));
482 test::ScopedFeatureList scoped_feature_list;
483 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
484
485 // The InitFromCommandLine supersedes the RegisterExtraFeatureOverrides
486 // because it was called first.
487 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
488 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
489
490 std::string enable_features;
491 std::string disable_features;
492 FeatureList::GetInstance()->GetFeatureOverrides(&enable_features,
493 &disable_features);
494 EXPECT_EQ(kFeatureOnByDefaultName, SortFeatureListString(enable_features));
495 EXPECT_EQ(kFeatureOffByDefaultName, SortFeatureListString(disable_features));
496 }
497
TEST_F(FeatureListTest,GetFeatureOverrides)498 TEST_F(FeatureListTest, GetFeatureOverrides) {
499 auto feature_list = std::make_unique<FeatureList>();
500 feature_list->InitFromCommandLine("A,X", "D");
501
502 Feature feature_b = {"B", FEATURE_ENABLED_BY_DEFAULT};
503 Feature feature_c = {"C", FEATURE_DISABLED_BY_DEFAULT};
504 std::vector<FeatureList::FeatureOverrideInfo> overrides;
505 overrides.push_back({std::cref(feature_b),
506 FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE});
507 overrides.push_back({std::cref(feature_c),
508 FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE});
509 feature_list->RegisterExtraFeatureOverrides(std::move(overrides));
510
511 FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group");
512 feature_list->RegisterFieldTrialOverride(kFeatureOffByDefaultName,
513 FeatureList::OVERRIDE_ENABLE_FEATURE,
514 trial);
515
516 test::ScopedFeatureList scoped_feature_list;
517 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
518
519 std::string enable_features;
520 std::string disable_features;
521 FeatureList::GetInstance()->GetFeatureOverrides(&enable_features,
522 &disable_features);
523 EXPECT_EQ("A,C,OffByDefault<Trial,X", SortFeatureListString(enable_features));
524 EXPECT_EQ("B,D", SortFeatureListString(disable_features));
525
526 FeatureList::GetInstance()->GetCommandLineFeatureOverrides(&enable_features,
527 &disable_features);
528 EXPECT_EQ("A,C,X", SortFeatureListString(enable_features));
529 EXPECT_EQ("B,D", SortFeatureListString(disable_features));
530 }
531
TEST_F(FeatureListTest,GetFeatureOverrides_UseDefault)532 TEST_F(FeatureListTest, GetFeatureOverrides_UseDefault) {
533 auto feature_list = std::make_unique<FeatureList>();
534 feature_list->InitFromCommandLine("A,X", "D");
535
536 FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group");
537 feature_list->RegisterFieldTrialOverride(
538 kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial);
539
540 test::ScopedFeatureList scoped_feature_list;
541 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
542
543 std::string enable_features;
544 std::string disable_features;
545 FeatureList::GetInstance()->GetFeatureOverrides(&enable_features,
546 &disable_features);
547 EXPECT_EQ("*OffByDefault<Trial,A,X", SortFeatureListString(enable_features));
548 EXPECT_EQ("D", SortFeatureListString(disable_features));
549 }
550
TEST_F(FeatureListTest,GetFieldTrial)551 TEST_F(FeatureListTest, GetFieldTrial) {
552 FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group");
553 auto feature_list = std::make_unique<FeatureList>();
554 feature_list->RegisterFieldTrialOverride(
555 kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial);
556 test::ScopedFeatureList scoped_feature_list;
557 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
558
559 EXPECT_EQ(trial, FeatureList::GetFieldTrial(kFeatureOnByDefault));
560 EXPECT_EQ(nullptr, FeatureList::GetFieldTrial(kFeatureOffByDefault));
561 }
562
TEST_F(FeatureListTest,InitFromCommandLine_WithFieldTrials)563 TEST_F(FeatureListTest, InitFromCommandLine_WithFieldTrials) {
564 FieldTrialList::CreateFieldTrial("Trial", "Group");
565 auto feature_list = std::make_unique<FeatureList>();
566 feature_list->InitFromCommandLine("A,OffByDefault<Trial,X", "D");
567 test::ScopedFeatureList scoped_feature_list;
568 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
569
570 EXPECT_FALSE(FieldTrialList::IsTrialActive("Trial"));
571 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
572 EXPECT_TRUE(FieldTrialList::IsTrialActive("Trial"));
573 }
574
TEST_F(FeatureListTest,InitFromCommandLine_UseDefault)575 TEST_F(FeatureListTest, InitFromCommandLine_UseDefault) {
576 FieldTrialList::CreateFieldTrial("T1", "Group");
577 FieldTrialList::CreateFieldTrial("T2", "Group");
578 auto feature_list = std::make_unique<FeatureList>();
579 feature_list->InitFromCommandLine("A,*OffByDefault<T1,*OnByDefault<T2,X",
580 "D");
581 test::ScopedFeatureList scoped_feature_list;
582 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
583
584 EXPECT_FALSE(FieldTrialList::IsTrialActive("T1"));
585 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
586 EXPECT_TRUE(FieldTrialList::IsTrialActive("T1"));
587
588 EXPECT_FALSE(FieldTrialList::IsTrialActive("T2"));
589 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
590 EXPECT_TRUE(FieldTrialList::IsTrialActive("T2"));
591 }
592
TEST_F(FeatureListTest,InitInstance)593 TEST_F(FeatureListTest, InitInstance) {
594 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
595 test::ScopedFeatureList scoped_feature_list;
596 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
597
598 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
599 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
600
601 // Initialize from command line if we haven't yet.
602 FeatureList::InitInstance("", kFeatureOnByDefaultName);
603 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
604 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
605
606 // Do not initialize from commandline if we have already.
607 FeatureList::InitInstance(kFeatureOffByDefaultName, "");
608 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
609 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
610 }
611
TEST_F(FeatureListTest,UninitializedInstance_IsEnabledReturnsFalse)612 TEST_F(FeatureListTest, UninitializedInstance_IsEnabledReturnsFalse) {
613 std::unique_ptr<FeatureList> original_feature_list =
614 FeatureList::ClearInstanceForTesting();
615
616 // This test case simulates the calling pattern found in code which does not
617 // explicitly initialize the features list.
618 // All IsEnabled() calls should return the default value in this scenario.
619 EXPECT_EQ(nullptr, FeatureList::GetInstance());
620 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
621 EXPECT_EQ(nullptr, FeatureList::GetInstance());
622 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
623
624 if (original_feature_list)
625 FeatureList::RestoreInstanceForTesting(std::move(original_feature_list));
626 }
627
TEST_F(FeatureListTest,StoreAndRetrieveFeaturesFromSharedMemory)628 TEST_F(FeatureListTest, StoreAndRetrieveFeaturesFromSharedMemory) {
629 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
630
631 // Create some overrides.
632 feature_list->RegisterOverride(kFeatureOffByDefaultName,
633 FeatureList::OVERRIDE_ENABLE_FEATURE, nullptr);
634 feature_list->RegisterOverride(
635 kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, nullptr);
636 feature_list->FinalizeInitialization();
637
638 // Create an allocator and store the overrides.
639 base::MappedReadOnlyRegion shm =
640 base::ReadOnlySharedMemoryRegion::Create(4 << 10);
641 WritableSharedPersistentMemoryAllocator allocator(std::move(shm.mapping), 1,
642 "");
643 feature_list->AddFeaturesToAllocator(&allocator);
644
645 std::unique_ptr<base::FeatureList> feature_list2(new base::FeatureList);
646
647 // Check that the new feature list is empty.
648 EXPECT_FALSE(feature_list2->IsFeatureOverriddenFromCommandLine(
649 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
650 EXPECT_FALSE(feature_list2->IsFeatureOverriddenFromCommandLine(
651 kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
652
653 feature_list2->InitFromSharedMemory(&allocator);
654 // Check that the new feature list now has 2 overrides.
655 EXPECT_TRUE(feature_list2->IsFeatureOverriddenFromCommandLine(
656 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
657 EXPECT_TRUE(feature_list2->IsFeatureOverriddenFromCommandLine(
658 kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
659 }
660
TEST_F(FeatureListTest,StoreAndRetrieveAssociatedFeaturesFromSharedMemory)661 TEST_F(FeatureListTest, StoreAndRetrieveAssociatedFeaturesFromSharedMemory) {
662 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
663
664 // Create some overrides.
665 FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A");
666 FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B");
667 feature_list->RegisterFieldTrialOverride(
668 kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial1);
669 feature_list->RegisterFieldTrialOverride(
670 kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial2);
671 feature_list->FinalizeInitialization();
672
673 // Create an allocator and store the overrides.
674 base::MappedReadOnlyRegion shm =
675 base::ReadOnlySharedMemoryRegion::Create(4 << 10);
676 WritableSharedPersistentMemoryAllocator allocator(std::move(shm.mapping), 1,
677 "");
678 feature_list->AddFeaturesToAllocator(&allocator);
679
680 std::unique_ptr<base::FeatureList> feature_list2(new base::FeatureList);
681 feature_list2->InitFromSharedMemory(&allocator);
682 feature_list2->FinalizeInitialization();
683
684 // Check that the field trials are still associated.
685 FieldTrial* associated_trial1 =
686 feature_list2->GetAssociatedFieldTrial(kFeatureOnByDefault);
687 FieldTrial* associated_trial2 =
688 feature_list2->GetAssociatedFieldTrial(kFeatureOffByDefault);
689 EXPECT_EQ(associated_trial1, trial1);
690 EXPECT_EQ(associated_trial2, trial2);
691 }
692
TEST_F(FeatureListTest,SetEarlyAccessInstance_AllowList)693 TEST_F(FeatureListTest, SetEarlyAccessInstance_AllowList) {
694 test::ScopedFeatureList clear_feature_list;
695 clear_feature_list.InitWithNullFeatureAndFieldTrialLists();
696
697 auto early_access_feature_list = std::make_unique<FeatureList>();
698 early_access_feature_list->InitFromCommandLine("OffByDefault", "OnByDefault");
699 FeatureList::SetEarlyAccessInstance(std::move(early_access_feature_list),
700 {"OnByDefault"});
701 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
702 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
703 EXPECT_EQ(&kFeatureOffByDefault,
704 FeatureList::GetEarlyAccessedFeatureForTesting());
705 FeatureList::ResetEarlyFeatureAccessTrackerForTesting();
706 }
707
TEST_F(FeatureListTest,SetEarlyAccessInstance_ReplaceByRealList)708 TEST_F(FeatureListTest, SetEarlyAccessInstance_ReplaceByRealList) {
709 test::ScopedFeatureList clear_feature_list;
710 clear_feature_list.InitWithNullFeatureAndFieldTrialLists();
711
712 auto early_access_feature_list = std::make_unique<FeatureList>();
713 early_access_feature_list->InitFromCommandLine("OffByDefault", "OnByDefault");
714 FeatureList::SetEarlyAccessInstance(std::move(early_access_feature_list),
715 {"OffByDefault", "OnByDefault"});
716 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
717 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
718
719 auto feature_list = std::make_unique<FeatureList>();
720 feature_list->InitFromCommandLine("", "");
721 FeatureList::SetInstance(std::move(feature_list));
722 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
723 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
724 }
725
726 #if BUILDFLAG(ENABLE_BANNED_BASE_FEATURE_PREFIX) && \
727 defined(GTEST_HAS_DEATH_TEST)
728 using FeatureListDeathTest = FeatureListTest;
TEST_F(FeatureListDeathTest,DiesWithBadFeatureName)729 TEST_F(FeatureListDeathTest, DiesWithBadFeatureName) {
730 EXPECT_DEATH(
731 Feature(
732 StrCat({BUILDFLAG(BANNED_BASE_FEATURE_PREFIX), "MyFeature"}).c_str(),
733 FEATURE_DISABLED_BY_DEFAULT),
734 StrCat({"Invalid feature name ", BUILDFLAG(BANNED_BASE_FEATURE_PREFIX),
735 "MyFeature"}));
736 }
737 #endif // BUILDFLAG(ENABLE_BANNED_BASE_FEATURE_PREFIX) &&
738 // defined(GTEST_HAS_DEATH_TEST)
739
TEST(FeatureListAccessorTest,DefaultStates)740 TEST(FeatureListAccessorTest, DefaultStates) {
741 test::ScopedFeatureList scoped_feature_list;
742 auto feature_list = std::make_unique<FeatureList>();
743 auto feature_list_accessor = feature_list->ConstructAccessor();
744 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
745
746 EXPECT_EQ(feature_list_accessor->GetOverrideStateByFeatureName(
747 kFeatureOnByDefault.name),
748 FeatureList::OVERRIDE_USE_DEFAULT);
749 EXPECT_EQ(feature_list_accessor->GetOverrideStateByFeatureName(
750 kFeatureOffByDefault.name),
751 FeatureList::OVERRIDE_USE_DEFAULT);
752 }
753
TEST(FeatureListAccessorTest,InitFromCommandLine)754 TEST(FeatureListAccessorTest, InitFromCommandLine) {
755 struct {
756 const char* enable_features;
757 const char* disable_features;
758 FeatureList::OverrideState expected_feature_on_state;
759 FeatureList::OverrideState expected_feature_off_state;
760 } test_cases[] = {
761 {"", "", FeatureList::OVERRIDE_USE_DEFAULT,
762 FeatureList::OVERRIDE_USE_DEFAULT},
763 {"OffByDefault", "", FeatureList::OVERRIDE_USE_DEFAULT,
764 FeatureList::OVERRIDE_ENABLE_FEATURE},
765 {"OffByDefault", "OnByDefault", FeatureList::OVERRIDE_DISABLE_FEATURE,
766 FeatureList::OVERRIDE_ENABLE_FEATURE},
767 {"OnByDefault,OffByDefault", "", FeatureList::OVERRIDE_ENABLE_FEATURE,
768 FeatureList::OVERRIDE_ENABLE_FEATURE},
769 {"", "OnByDefault,OffByDefault", FeatureList::OVERRIDE_DISABLE_FEATURE,
770 FeatureList::OVERRIDE_DISABLE_FEATURE},
771 // In the case an entry is both, disable takes precedence.
772 {"OnByDefault", "OnByDefault,OffByDefault",
773 FeatureList::OVERRIDE_DISABLE_FEATURE,
774 FeatureList::OVERRIDE_DISABLE_FEATURE},
775 };
776
777 for (size_t i = 0; i < std::size(test_cases); ++i) {
778 const auto& test_case = test_cases[i];
779 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: [%s] [%s]", i,
780 test_case.enable_features,
781 test_case.disable_features));
782
783 test::ScopedFeatureList scoped_feature_list;
784 auto feature_list = std::make_unique<FeatureList>();
785 auto feature_list_accessor = feature_list->ConstructAccessor();
786 feature_list->InitFromCommandLine(test_case.enable_features,
787 test_case.disable_features);
788 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
789
790 EXPECT_EQ(test_case.expected_feature_on_state,
791 feature_list_accessor->GetOverrideStateByFeatureName(
792 kFeatureOnByDefault.name))
793 << i;
794 EXPECT_EQ(test_case.expected_feature_off_state,
795 feature_list_accessor->GetOverrideStateByFeatureName(
796 kFeatureOffByDefault.name))
797 << i;
798 }
799 }
800
TEST(FeatureListAccessorTest,InitFromCommandLineWithFeatureParams)801 TEST(FeatureListAccessorTest, InitFromCommandLineWithFeatureParams) {
802 struct {
803 const std::string enable_features;
804 const std::map<std::string, std::string> expected_feature_params;
805 } test_cases[] = {
806 {"Feature:x/100/y/test", {{"x", "100"}, {"y", "test"}}},
807 {"Feature<Trial:asdf/ghjkl/y/123", {{"asdf", "ghjkl"}, {"y", "123"}}},
808 };
809
810 // Clear global state so that repeated runs of this test don't flake.
811 // When https://crrev.com/c/3694674 is submitted, we should be able to remove
812 // this.
813 base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
814
815 for (size_t i = 0; i < std::size(test_cases); ++i) {
816 const auto& test_case = test_cases[i];
817 SCOPED_TRACE(test_case.enable_features);
818
819 test::ScopedFeatureList scoped_feature_list;
820 auto feature_list = std::make_unique<FeatureList>();
821 auto feature_list_accessor = feature_list->ConstructAccessor();
822 feature_list->InitFromCommandLine(test_case.enable_features, "");
823 scoped_feature_list.InitWithFeatureList(std::move(feature_list));
824
825 EXPECT_EQ(FeatureList::OVERRIDE_ENABLE_FEATURE,
826 feature_list_accessor->GetOverrideStateByFeatureName("Feature"))
827 << i;
828 std::map<std::string, std::string> actual_params;
829 EXPECT_TRUE(feature_list_accessor->GetParamsByFeatureName("Feature",
830 &actual_params))
831 << i;
832 EXPECT_EQ(test_case.expected_feature_params, actual_params) << i;
833 }
834 }
835
836 } // namespace base
837