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