1 // Copyright 2012 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/metrics/field_trial.h"
6
7 #include <stddef.h>
8
9 #include <string_view>
10 #include <utility>
11
12 #include "base/base_switches.h"
13 #include "base/build_time.h"
14 #include "base/command_line.h"
15 #include "base/feature_list.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/metrics/field_trial_list_including_low_anonymity.h"
18 #include "base/metrics/field_trial_param_associator.h"
19 #include "base/rand_util.h"
20 #include "base/run_loop.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/test/gtest_util.h"
24 #include "base/test/mock_entropy_provider.h"
25 #include "base/test/multiprocess_test.h"
26 #include "base/test/scoped_feature_list.h"
27 #include "base/test/task_environment.h"
28 #include "base/test/test_shared_memory_util.h"
29 #include "base/test/test_timeouts.h"
30 #include "build/build_config.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33 #include "testing/multiprocess_func_list.h"
34
35 #if BUILDFLAG(USE_BLINK)
36 #include "base/process/launch.h"
37 #endif
38
39 #if BUILDFLAG(IS_POSIX)
40 #include "base/files/platform_file.h"
41 #include "base/posix/global_descriptors.h"
42 #endif
43
44 #if BUILDFLAG(IS_MAC)
45 #include "base/mac/mach_port_rendezvous.h"
46 #endif
47
48 namespace base {
49
50 namespace {
51
52 // Default group name used by several tests.
53 const char kDefaultGroupName[] = "DefaultGroup";
54
55 // Call FieldTrialList::FactoryGetFieldTrial().
CreateFieldTrial(const std::string & trial_name,int total_probability,const std::string & default_group_name,bool is_low_anonymity=false)56 scoped_refptr<FieldTrial> CreateFieldTrial(
57 const std::string& trial_name,
58 int total_probability,
59 const std::string& default_group_name,
60 bool is_low_anonymity = false) {
61 MockEntropyProvider entropy_provider(0.9);
62 return FieldTrialList::FactoryGetFieldTrial(
63 trial_name, total_probability, default_group_name, entropy_provider, 0,
64 is_low_anonymity);
65 }
66
67 // A FieldTrialList::Observer implementation which stores the trial name and
68 // group name received via OnFieldTrialGroupFinalized() for later inspection.
69 class TestFieldTrialObserver : public FieldTrialList::Observer {
70 public:
TestFieldTrialObserver()71 TestFieldTrialObserver() { FieldTrialList::AddObserver(this); }
72 TestFieldTrialObserver(const TestFieldTrialObserver&) = delete;
73 TestFieldTrialObserver& operator=(const TestFieldTrialObserver&) = delete;
74
~TestFieldTrialObserver()75 ~TestFieldTrialObserver() override { FieldTrialList::RemoveObserver(this); }
76
OnFieldTrialGroupFinalized(const FieldTrial & trial,const std::string & group)77 void OnFieldTrialGroupFinalized(const FieldTrial& trial,
78 const std::string& group) override {
79 trial_name_ = trial.trial_name();
80 group_name_ = group;
81 }
82
trial_name() const83 const std::string& trial_name() const { return trial_name_; }
group_name() const84 const std::string& group_name() const { return group_name_; }
85
86 private:
87 std::string trial_name_;
88 std::string group_name_;
89 };
90
91 // A FieldTrialList::Observer implementation which accesses the group of a
92 // FieldTrial from OnFieldTrialGroupFinalized(). Used to test reentrancy.
93 class FieldTrialObserverAccessingGroup : public FieldTrialList::Observer {
94 public:
95 // |trial_to_access| is the FieldTrial on which to invoke Activate() when
96 // receiving an OnFieldTrialGroupFinalized() notification.
FieldTrialObserverAccessingGroup(scoped_refptr<FieldTrial> trial_to_access)97 explicit FieldTrialObserverAccessingGroup(
98 scoped_refptr<FieldTrial> trial_to_access)
99 : trial_to_access_(trial_to_access) {
100 FieldTrialList::AddObserver(this);
101 }
102 FieldTrialObserverAccessingGroup(const FieldTrialObserverAccessingGroup&) =
103 delete;
104 FieldTrialObserverAccessingGroup& operator=(
105 const FieldTrialObserverAccessingGroup&) = delete;
106
~FieldTrialObserverAccessingGroup()107 ~FieldTrialObserverAccessingGroup() override {
108 FieldTrialList::RemoveObserver(this);
109 }
110
OnFieldTrialGroupFinalized(const base::FieldTrial & trial,const std::string & group)111 void OnFieldTrialGroupFinalized(const base::FieldTrial& trial,
112 const std::string& group) override {
113 trial_to_access_->Activate();
114 }
115
116 private:
117 scoped_refptr<FieldTrial> trial_to_access_;
118 };
119
MockEscapeQueryParamValue(const std::string & input)120 std::string MockEscapeQueryParamValue(const std::string& input) {
121 return input;
122 }
123
124 } // namespace
125
126 // Same as |TestFieldTrialObserver|, but registers for low anonymity field
127 // trials too.
128 class TestFieldTrialObserverIncludingLowAnonymity
129 : public FieldTrialList::Observer {
130 public:
TestFieldTrialObserverIncludingLowAnonymity()131 TestFieldTrialObserverIncludingLowAnonymity() {
132 FieldTrialListIncludingLowAnonymity::AddObserver(this);
133 }
134 TestFieldTrialObserverIncludingLowAnonymity(
135 const TestFieldTrialObserverIncludingLowAnonymity&) = delete;
136 TestFieldTrialObserverIncludingLowAnonymity& operator=(
137 const TestFieldTrialObserverIncludingLowAnonymity&) = delete;
138
~TestFieldTrialObserverIncludingLowAnonymity()139 ~TestFieldTrialObserverIncludingLowAnonymity() override {
140 FieldTrialListIncludingLowAnonymity::RemoveObserver(this);
141 }
142
OnFieldTrialGroupFinalized(const base::FieldTrial & trial,const std::string & group)143 void OnFieldTrialGroupFinalized(const base::FieldTrial& trial,
144 const std::string& group) override {
145 trial_name_ = trial.trial_name();
146 group_name_ = group;
147 }
148
trial_name() const149 const std::string& trial_name() const { return trial_name_; }
group_name() const150 const std::string& group_name() const { return group_name_; }
151
152 private:
153 std::string trial_name_;
154 std::string group_name_;
155 };
156
157 class FieldTrialTest : public ::testing::Test {
158 public:
FieldTrialTest()159 FieldTrialTest() {
160 // The test suite instantiates a FieldTrialList but for the purpose of these
161 // tests it's cleaner to start from scratch.
162 scoped_feature_list_.InitWithEmptyFeatureAndFieldTrialLists();
163 }
164 FieldTrialTest(const FieldTrialTest&) = delete;
165 FieldTrialTest& operator=(const FieldTrialTest&) = delete;
166
167 private:
168 test::TaskEnvironment task_environment_;
169 test::ScopedFeatureList scoped_feature_list_;
170 };
171
172 MATCHER(CompareActiveGroupToFieldTrial, "") {
173 const base::FieldTrial::ActiveGroup& lhs = ::testing::get<0>(arg);
174 const base::FieldTrial* rhs = ::testing::get<1>(arg).get();
175 return lhs.trial_name == rhs->trial_name() &&
176 lhs.group_name == rhs->group_name_internal();
177 }
178
179 // Test registration, and also check that destructors are called for trials.
TEST_F(FieldTrialTest,Registration)180 TEST_F(FieldTrialTest, Registration) {
181 const char name1[] = "name 1 test";
182 const char name2[] = "name 2 test";
183 EXPECT_FALSE(FieldTrialList::Find(name1));
184 EXPECT_FALSE(FieldTrialList::Find(name2));
185
186 scoped_refptr<FieldTrial> trial1 =
187 CreateFieldTrial(name1, 10, "default name 1 test");
188 EXPECT_EQ(FieldTrial::kNotFinalized, trial1->group_);
189 EXPECT_EQ(name1, trial1->trial_name());
190 EXPECT_EQ("", trial1->group_name_internal());
191
192 trial1->AppendGroup(std::string(), 7);
193
194 EXPECT_EQ(trial1.get(), FieldTrialList::Find(name1));
195 EXPECT_FALSE(FieldTrialList::Find(name2));
196
197 scoped_refptr<FieldTrial> trial2 =
198 CreateFieldTrial(name2, 10, "default name 2 test");
199 EXPECT_EQ(FieldTrial::kNotFinalized, trial2->group_);
200 EXPECT_EQ(name2, trial2->trial_name());
201 EXPECT_EQ("", trial2->group_name_internal());
202
203 trial2->AppendGroup("a first group", 7);
204
205 EXPECT_EQ(trial1.get(), FieldTrialList::Find(name1));
206 EXPECT_EQ(trial2.get(), FieldTrialList::Find(name2));
207 // Note: FieldTrialList should delete the objects at shutdown.
208 }
209
TEST_F(FieldTrialTest,AbsoluteProbabilities)210 TEST_F(FieldTrialTest, AbsoluteProbabilities) {
211 MockEntropyProvider entropy_provider(0.51);
212 scoped_refptr<FieldTrial> trial = FieldTrialList::FactoryGetFieldTrial(
213 "trial name", 100, "Default", entropy_provider);
214 trial->AppendGroup("LoserA", 0);
215 trial->AppendGroup("Winner", 100);
216 trial->AppendGroup("LoserB", 0);
217 EXPECT_EQ(trial->group_name(), "Winner");
218 }
219
TEST_F(FieldTrialTest,SmallProbabilities_49)220 TEST_F(FieldTrialTest, SmallProbabilities_49) {
221 MockEntropyProvider entropy_provider(0.49);
222 scoped_refptr<FieldTrial> trial = FieldTrialList::FactoryGetFieldTrial(
223 "trial name", 2, "Default", entropy_provider);
224 trial->AppendGroup("first", 1);
225 trial->AppendGroup("second", 1);
226 EXPECT_EQ(trial->group_name(), "first");
227 }
228
TEST_F(FieldTrialTest,SmallProbabilities_51)229 TEST_F(FieldTrialTest, SmallProbabilities_51) {
230 MockEntropyProvider entropy_provider(0.51);
231 scoped_refptr<FieldTrial> trial = FieldTrialList::FactoryGetFieldTrial(
232 "trial name", 2, "Default", entropy_provider);
233 trial->AppendGroup("first", 1);
234 trial->AppendGroup("second", 1);
235 EXPECT_EQ(trial->group_name(), "second");
236 }
237
TEST_F(FieldTrialTest,MiddleProbabilities_49)238 TEST_F(FieldTrialTest, MiddleProbabilities_49) {
239 MockEntropyProvider entropy_provider(0.49);
240 scoped_refptr<FieldTrial> trial = FieldTrialList::FactoryGetFieldTrial(
241 "trial name", 10, "Default", entropy_provider);
242 trial->AppendGroup("NotDefault", 5);
243 EXPECT_EQ(trial->group_name(), "NotDefault");
244 }
245
TEST_F(FieldTrialTest,MiddleProbabilities_51)246 TEST_F(FieldTrialTest, MiddleProbabilities_51) {
247 MockEntropyProvider entropy_provider(0.51);
248 scoped_refptr<FieldTrial> trial = FieldTrialList::FactoryGetFieldTrial(
249 "trial name", 10, "Default", entropy_provider);
250 trial->AppendGroup("NotDefault", 5);
251 EXPECT_EQ(trial->group_name(), "Default");
252 }
253
254 // AppendGroup after finalization should not change the winner.
TEST_F(FieldTrialTest,OneWinner)255 TEST_F(FieldTrialTest, OneWinner) {
256 MockEntropyProvider entropy_provider(0.51);
257 scoped_refptr<FieldTrial> trial = FieldTrialList::FactoryGetFieldTrial(
258 "trial name", 10, "Default", entropy_provider);
259
260 for (int i = 0; i < 5; ++i) {
261 trial->AppendGroup(StringPrintf("%d", i), 1);
262 }
263
264 // Entropy 0.51 should assign to the 6th group.
265 // It should be declared the winner and stay that way.
266 trial->AppendGroup("Winner", 1);
267 EXPECT_EQ("Winner", trial->group_name());
268
269 // Note: appending groups after calling group_name() is probably not really
270 // valid usage, since it will DCHECK if the default group won.
271 for (int i = 7; i < 10; ++i) {
272 trial->AppendGroup(StringPrintf("%d", i), 1);
273 EXPECT_EQ("Winner", trial->group_name());
274 }
275 }
276
TEST_F(FieldTrialTest,ActiveGroups)277 TEST_F(FieldTrialTest, ActiveGroups) {
278 std::string no_group("No Group");
279 scoped_refptr<FieldTrial> trial = CreateFieldTrial(no_group, 10, "Default");
280
281 // There is no winner yet, so no NameGroupId should be returned.
282 FieldTrial::ActiveGroup active_group;
283 EXPECT_FALSE(trial->GetActiveGroup(&active_group));
284
285 // Create a single winning group.
286 std::string one_winner("One Winner");
287 trial = CreateFieldTrial(one_winner, 10, "Default");
288 std::string winner("Winner");
289 trial->AppendGroup(winner, 10);
290 EXPECT_FALSE(trial->GetActiveGroup(&active_group));
291 trial->Activate();
292 EXPECT_TRUE(trial->GetActiveGroup(&active_group));
293 EXPECT_EQ(one_winner, active_group.trial_name);
294 EXPECT_EQ(winner, active_group.group_name);
295
296 std::string multi_group("MultiGroup");
297 scoped_refptr<FieldTrial> multi_group_trial =
298 CreateFieldTrial(multi_group, 9, "Default");
299
300 multi_group_trial->AppendGroup("Me", 3);
301 multi_group_trial->AppendGroup("You", 3);
302 multi_group_trial->AppendGroup("Them", 3);
303 EXPECT_FALSE(multi_group_trial->GetActiveGroup(&active_group));
304 multi_group_trial->Activate();
305 EXPECT_TRUE(multi_group_trial->GetActiveGroup(&active_group));
306 EXPECT_EQ(multi_group, active_group.trial_name);
307 EXPECT_EQ(multi_group_trial->group_name(), active_group.group_name);
308
309 // Now check if the list is built properly...
310 FieldTrial::ActiveGroups active_groups;
311 FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
312 EXPECT_EQ(2U, active_groups.size());
313 for (size_t i = 0; i < active_groups.size(); ++i) {
314 // Order is not guaranteed, so check all values.
315 EXPECT_NE(no_group, active_groups[i].trial_name);
316 EXPECT_TRUE(one_winner != active_groups[i].trial_name ||
317 winner == active_groups[i].group_name);
318 EXPECT_TRUE(multi_group != active_groups[i].trial_name ||
319 multi_group_trial->group_name() == active_groups[i].group_name);
320 }
321 }
322
TEST_F(FieldTrialTest,ActiveGroupsNotFinalized)323 TEST_F(FieldTrialTest, ActiveGroupsNotFinalized) {
324 const char kTrialName[] = "TestTrial";
325 const char kSecondaryGroupName[] = "SecondaryGroup";
326
327 scoped_refptr<FieldTrial> trial =
328 CreateFieldTrial(kTrialName, 100, kDefaultGroupName);
329 trial->AppendGroup(kSecondaryGroupName, 50);
330
331 // Before |Activate()| is called, |GetActiveGroup()| should return false.
332 FieldTrial::ActiveGroup active_group;
333 EXPECT_FALSE(trial->GetActiveGroup(&active_group));
334
335 // |GetActiveFieldTrialGroups()| should also not include the trial.
336 FieldTrial::ActiveGroups active_groups;
337 FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
338 EXPECT_TRUE(active_groups.empty());
339
340 // After |Activate()| has been called, both APIs should succeed.
341 trial->Activate();
342
343 EXPECT_TRUE(trial->GetActiveGroup(&active_group));
344 EXPECT_EQ(kTrialName, active_group.trial_name);
345 EXPECT_TRUE(kDefaultGroupName == active_group.group_name ||
346 kSecondaryGroupName == active_group.group_name);
347
348 FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
349 ASSERT_EQ(1U, active_groups.size());
350 EXPECT_EQ(kTrialName, active_groups[0].trial_name);
351 EXPECT_EQ(active_group.group_name, active_groups[0].group_name);
352 }
353
TEST_F(FieldTrialTest,GetGroupNameWithoutActivation)354 TEST_F(FieldTrialTest, GetGroupNameWithoutActivation) {
355 const char kTrialName[] = "TestTrial";
356 const char kSecondaryGroupName[] = "SecondaryGroup";
357
358 scoped_refptr<FieldTrial> trial =
359 CreateFieldTrial(kTrialName, 100, kDefaultGroupName);
360 trial->AppendGroup(kSecondaryGroupName, 50);
361
362 // The trial should start inactive.
363 EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
364
365 // Calling |GetGroupNameWithoutActivation()| should not activate the trial.
366 std::string group_name = trial->GetGroupNameWithoutActivation();
367 EXPECT_FALSE(group_name.empty());
368 EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
369
370 // Calling |group_name()| should activate it and return the same group name.
371 EXPECT_EQ(group_name, trial->group_name());
372 EXPECT_TRUE(FieldTrialList::IsTrialActive(kTrialName));
373 }
374
TEST_F(FieldTrialTest,SaveAll)375 TEST_F(FieldTrialTest, SaveAll) {
376 std::string save_string;
377
378 scoped_refptr<FieldTrial> trial =
379 CreateFieldTrial("Some name", 10, "Default some name");
380 EXPECT_EQ("", trial->group_name_internal());
381 FieldTrialList::AllStatesToString(&save_string);
382 EXPECT_EQ("Some name/Default some name", save_string);
383 // Getting all states should have finalized the trial.
384 EXPECT_EQ("Default some name", trial->group_name_internal());
385 save_string.clear();
386
387 // Create a winning group.
388 trial = CreateFieldTrial("trial2", 10, "Default some name");
389 trial->AppendGroup("Winner", 10);
390 trial->Activate();
391 FieldTrialList::AllStatesToString(&save_string);
392 EXPECT_EQ("Some name/Default some name/*trial2/Winner", save_string);
393 save_string.clear();
394
395 // Create a second trial and winning group.
396 scoped_refptr<FieldTrial> trial2 = CreateFieldTrial("xxx", 10, "Default xxx");
397 trial2->AppendGroup("yyyy", 10);
398 trial2->Activate();
399
400 FieldTrialList::AllStatesToString(&save_string);
401 // We assume names are alphabetized... though this is not critical.
402 EXPECT_EQ("Some name/Default some name/*trial2/Winner/*xxx/yyyy",
403 save_string);
404 save_string.clear();
405
406 // Create a third trial with only the default group.
407 scoped_refptr<FieldTrial> trial3 = CreateFieldTrial("zzz", 10, "default");
408
409 FieldTrialList::AllStatesToString(&save_string);
410 EXPECT_EQ("Some name/Default some name/*trial2/Winner/*xxx/yyyy/zzz/default",
411 save_string);
412
413 save_string.clear();
414 FieldTrialList::AllStatesToString(&save_string);
415 EXPECT_EQ("Some name/Default some name/*trial2/Winner/*xxx/yyyy/zzz/default",
416 save_string);
417 }
418
TEST_F(FieldTrialTest,Restore)419 TEST_F(FieldTrialTest, Restore) {
420 ASSERT_FALSE(FieldTrialList::TrialExists("Some_name"));
421 ASSERT_FALSE(FieldTrialList::TrialExists("xxx"));
422
423 FieldTrialList::CreateTrialsFromString("Some_name/Winner/xxx/yyyy/");
424
425 FieldTrial* trial = FieldTrialList::Find("Some_name");
426 ASSERT_NE(static_cast<FieldTrial*>(nullptr), trial);
427 EXPECT_EQ("Winner", trial->group_name());
428 EXPECT_EQ("Some_name", trial->trial_name());
429 EXPECT_FALSE(trial->IsOverridden());
430
431 trial = FieldTrialList::Find("xxx");
432 ASSERT_NE(static_cast<FieldTrial*>(nullptr), trial);
433 EXPECT_EQ("yyyy", trial->group_name());
434 EXPECT_EQ("xxx", trial->trial_name());
435 EXPECT_FALSE(trial->IsOverridden());
436 }
437
TEST_F(FieldTrialTest,RestoreNotEndingWithSlash)438 TEST_F(FieldTrialTest, RestoreNotEndingWithSlash) {
439 EXPECT_TRUE(FieldTrialList::CreateTrialsFromString("tname/gname"));
440
441 FieldTrial* trial = FieldTrialList::Find("tname");
442 ASSERT_NE(static_cast<FieldTrial*>(nullptr), trial);
443 EXPECT_EQ("gname", trial->group_name());
444 EXPECT_EQ("tname", trial->trial_name());
445 }
446
TEST_F(FieldTrialTest,BogusRestore)447 TEST_F(FieldTrialTest, BogusRestore) {
448 EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("MissingSlash"));
449 EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("MissingGroupName/"));
450 EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("noname, only group/"));
451 EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("/emptyname"));
452 EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("*/emptyname"));
453 }
454
TEST_F(FieldTrialTest,DuplicateRestore)455 TEST_F(FieldTrialTest, DuplicateRestore) {
456 scoped_refptr<FieldTrial> trial =
457 CreateFieldTrial("Some name", 10, "Default");
458 trial->AppendGroup("Winner", 10);
459 trial->Activate();
460 std::string save_string;
461 FieldTrialList::AllStatesToString(&save_string);
462 // * prefix since it is activated.
463 EXPECT_EQ("*Some name/Winner", save_string);
464
465 // It is OK if we redundantly specify a winner.
466 EXPECT_TRUE(FieldTrialList::CreateTrialsFromString(save_string));
467
468 // But it is an error to try to change to a different winner.
469 EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("Some name/Loser/"));
470 }
471
TEST_F(FieldTrialTest,CreateTrialsFromStringNotActive)472 TEST_F(FieldTrialTest, CreateTrialsFromStringNotActive) {
473 ASSERT_FALSE(FieldTrialList::TrialExists("Abc"));
474 ASSERT_FALSE(FieldTrialList::TrialExists("Xyz"));
475 ASSERT_TRUE(FieldTrialList::CreateTrialsFromString("Abc/def/Xyz/zyx/"));
476
477 FieldTrial::ActiveGroups active_groups;
478 FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
479 ASSERT_TRUE(active_groups.empty());
480
481 // Check that the values still get returned and querying them activates them.
482 EXPECT_EQ("def", FieldTrialList::FindFullName("Abc"));
483 EXPECT_EQ("zyx", FieldTrialList::FindFullName("Xyz"));
484
485 FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
486 ASSERT_EQ(2U, active_groups.size());
487 EXPECT_EQ("Abc", active_groups[0].trial_name);
488 EXPECT_EQ("def", active_groups[0].group_name);
489 EXPECT_EQ("Xyz", active_groups[1].trial_name);
490 EXPECT_EQ("zyx", active_groups[1].group_name);
491 }
492
TEST_F(FieldTrialTest,CreateTrialsFromStringForceActivation)493 TEST_F(FieldTrialTest, CreateTrialsFromStringForceActivation) {
494 ASSERT_FALSE(FieldTrialList::TrialExists("Abc"));
495 ASSERT_FALSE(FieldTrialList::TrialExists("def"));
496 ASSERT_FALSE(FieldTrialList::TrialExists("Xyz"));
497 ASSERT_TRUE(
498 FieldTrialList::CreateTrialsFromString("*Abc/cba/def/fed/*Xyz/zyx/"));
499
500 FieldTrial::ActiveGroups active_groups;
501 FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
502 ASSERT_EQ(2U, active_groups.size());
503 EXPECT_EQ("Abc", active_groups[0].trial_name);
504 EXPECT_EQ("cba", active_groups[0].group_name);
505 EXPECT_EQ("Xyz", active_groups[1].trial_name);
506 EXPECT_EQ("zyx", active_groups[1].group_name);
507 }
508
TEST_F(FieldTrialTest,CreateTrialsFromStringNotActiveObserver)509 TEST_F(FieldTrialTest, CreateTrialsFromStringNotActiveObserver) {
510 ASSERT_FALSE(FieldTrialList::TrialExists("Abc"));
511
512 TestFieldTrialObserver observer;
513 ASSERT_TRUE(FieldTrialList::CreateTrialsFromString("Abc/def/"));
514 RunLoop().RunUntilIdle();
515 // Observer shouldn't be notified.
516 EXPECT_TRUE(observer.trial_name().empty());
517
518 // Check that the values still get returned and querying them activates them.
519 EXPECT_EQ("def", FieldTrialList::FindFullName("Abc"));
520
521 EXPECT_EQ("Abc", observer.trial_name());
522 EXPECT_EQ("def", observer.group_name());
523 }
524
TEST_F(FieldTrialTest,CreateFieldTrial)525 TEST_F(FieldTrialTest, CreateFieldTrial) {
526 ASSERT_FALSE(FieldTrialList::TrialExists("Some_name"));
527
528 FieldTrialList::CreateFieldTrial("Some_name", "Winner");
529
530 FieldTrial* trial = FieldTrialList::Find("Some_name");
531 ASSERT_NE(static_cast<FieldTrial*>(nullptr), trial);
532 EXPECT_EQ("Winner", trial->group_name());
533 EXPECT_EQ("Some_name", trial->trial_name());
534 }
535
TEST_F(FieldTrialTest,CreateFieldTrialIsNotActive)536 TEST_F(FieldTrialTest, CreateFieldTrialIsNotActive) {
537 const char kTrialName[] = "CreateFieldTrialIsActiveTrial";
538 const char kWinnerGroup[] = "Winner";
539 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
540 FieldTrialList::CreateFieldTrial(kTrialName, kWinnerGroup);
541
542 FieldTrial::ActiveGroups active_groups;
543 FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
544 EXPECT_TRUE(active_groups.empty());
545 }
546
TEST_F(FieldTrialTest,DuplicateFieldTrial)547 TEST_F(FieldTrialTest, DuplicateFieldTrial) {
548 scoped_refptr<FieldTrial> trial =
549 CreateFieldTrial("Some_name", 10, "Default");
550 trial->AppendGroup("Winner", 10);
551
552 // It is OK if we redundantly specify a winner.
553 FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("Some_name", "Winner");
554 EXPECT_TRUE(trial1 != nullptr);
555
556 // But it is an error to try to change to a different winner.
557 FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("Some_name", "Loser");
558 EXPECT_TRUE(trial2 == nullptr);
559 }
560
TEST_F(FieldTrialTest,ForcedFieldTrials)561 TEST_F(FieldTrialTest, ForcedFieldTrials) {
562 // Validate we keep the forced choice.
563 FieldTrial* forced_trial = FieldTrialList::CreateFieldTrial("Use the",
564 "Force");
565 EXPECT_STREQ("Force", forced_trial->group_name().c_str());
566
567 scoped_refptr<FieldTrial> factory_trial =
568 CreateFieldTrial("Use the", 1000, "default");
569 EXPECT_EQ(factory_trial.get(), forced_trial);
570
571 factory_trial->AppendGroup("Force", 100);
572 EXPECT_EQ("Force", factory_trial->group_name());
573 factory_trial->AppendGroup("Dark Side", 100);
574 EXPECT_EQ("Force", factory_trial->group_name());
575 factory_trial->AppendGroup("Duck Tape", 800);
576 EXPECT_EQ("Force", factory_trial->group_name());
577 }
578
TEST_F(FieldTrialTest,ForcedFieldTrialsDefaultGroup)579 TEST_F(FieldTrialTest, ForcedFieldTrialsDefaultGroup) {
580 // Forcing the default should use the proper group ID.
581 FieldTrial* forced_trial =
582 FieldTrialList::CreateFieldTrial("Trial Name", "Default");
583 scoped_refptr<FieldTrial> factory_trial =
584 CreateFieldTrial("Trial Name", 1000, "Default");
585 EXPECT_EQ(forced_trial, factory_trial.get());
586
587 factory_trial->AppendGroup("Not Default", 100);
588 EXPECT_STREQ("Default", factory_trial->group_name().c_str());
589
590 factory_trial->AppendGroup("Not Default Either", 800);
591 EXPECT_STREQ("Default", factory_trial->group_name().c_str());
592 }
593
TEST_F(FieldTrialTest,SetForced)594 TEST_F(FieldTrialTest, SetForced) {
595 // Start by setting a trial for which we ensure a winner...
596 scoped_refptr<FieldTrial> forced_trial =
597 CreateFieldTrial("Use the", 1, "default");
598 EXPECT_EQ(forced_trial, forced_trial);
599
600 forced_trial->AppendGroup("Force", 1);
601 EXPECT_EQ("Force", forced_trial->group_name());
602
603 // Now force it.
604 forced_trial->SetForced();
605
606 // Now try to set it up differently as a hard coded registration would.
607 scoped_refptr<FieldTrial> hard_coded_trial =
608 CreateFieldTrial("Use the", 1, "default");
609 EXPECT_EQ(hard_coded_trial, forced_trial);
610
611 hard_coded_trial->AppendGroup("Force", 0);
612 EXPECT_EQ("Force", hard_coded_trial->group_name());
613
614 // Same thing if we would have done it to win again.
615 scoped_refptr<FieldTrial> other_hard_coded_trial =
616 CreateFieldTrial("Use the", 1, "default");
617 EXPECT_EQ(other_hard_coded_trial, forced_trial);
618
619 other_hard_coded_trial->AppendGroup("Force", 1);
620 EXPECT_EQ("Force", other_hard_coded_trial->group_name());
621 }
622
TEST_F(FieldTrialTest,SetForcedDefaultOnly)623 TEST_F(FieldTrialTest, SetForcedDefaultOnly) {
624 const char kTrialName[] = "SetForcedDefaultOnly";
625 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
626
627 scoped_refptr<FieldTrial> trial =
628 CreateFieldTrial(kTrialName, 100, kDefaultGroupName);
629 trial->SetForced();
630
631 trial = CreateFieldTrial(kTrialName, 100, kDefaultGroupName);
632 EXPECT_EQ(kDefaultGroupName, trial->group_name());
633 }
634
TEST_F(FieldTrialTest,SetForcedDefaultWithExtraGroup)635 TEST_F(FieldTrialTest, SetForcedDefaultWithExtraGroup) {
636 const char kTrialName[] = "SetForcedDefaultWithExtraGroup";
637 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
638
639 scoped_refptr<FieldTrial> trial =
640 CreateFieldTrial(kTrialName, 100, kDefaultGroupName);
641 trial->SetForced();
642
643 trial = CreateFieldTrial(kTrialName, 100, kDefaultGroupName);
644 trial->AppendGroup("Extra", 100);
645 EXPECT_EQ(kDefaultGroupName, trial->group_name());
646 }
647
TEST_F(FieldTrialTest,SetForcedTurnFeatureOn)648 TEST_F(FieldTrialTest, SetForcedTurnFeatureOn) {
649 const char kTrialName[] = "SetForcedTurnFeatureOn";
650 const char kExtraGroupName[] = "Extra";
651 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
652
653 // Simulate a server-side (forced) config that turns the feature on when the
654 // original hard-coded config had it disabled.
655 scoped_refptr<FieldTrial> forced_trial =
656 CreateFieldTrial(kTrialName, 100, kDefaultGroupName);
657 forced_trial->AppendGroup(kExtraGroupName, 100);
658 forced_trial->SetForced();
659
660 scoped_refptr<FieldTrial> client_trial =
661 CreateFieldTrial(kTrialName, 100, kDefaultGroupName);
662 client_trial->AppendGroup(kExtraGroupName, 0);
663
664 EXPECT_FALSE(client_trial->group_reported_);
665 EXPECT_EQ(kExtraGroupName, client_trial->group_name());
666 EXPECT_TRUE(client_trial->group_reported_);
667 EXPECT_EQ(kExtraGroupName, client_trial->group_name());
668 }
669
TEST_F(FieldTrialTest,SetForcedTurnFeatureOff)670 TEST_F(FieldTrialTest, SetForcedTurnFeatureOff) {
671 const char kTrialName[] = "SetForcedTurnFeatureOff";
672 const char kExtraGroupName[] = "Extra";
673 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
674
675 // Simulate a server-side (forced) config that turns the feature off when the
676 // original hard-coded config had it enabled.
677 scoped_refptr<FieldTrial> forced_trial =
678 CreateFieldTrial(kTrialName, 100, kDefaultGroupName);
679 forced_trial->AppendGroup(kExtraGroupName, 0);
680 forced_trial->SetForced();
681
682 scoped_refptr<FieldTrial> client_trial =
683 CreateFieldTrial(kTrialName, 100, kDefaultGroupName);
684 client_trial->AppendGroup(kExtraGroupName, 100);
685
686 EXPECT_FALSE(client_trial->group_reported_);
687 EXPECT_EQ(kDefaultGroupName, client_trial->group_name());
688 EXPECT_TRUE(client_trial->group_reported_);
689 EXPECT_EQ(kDefaultGroupName, client_trial->group_name());
690 }
691
TEST_F(FieldTrialTest,SetForcedChangeDefault_Default)692 TEST_F(FieldTrialTest, SetForcedChangeDefault_Default) {
693 const char kTrialName[] = "SetForcedDefaultGroupChange";
694 const char kGroupAName[] = "A";
695 const char kGroupBName[] = "B";
696 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
697
698 // Simulate a server-side (forced) config that switches which group is default
699 // and ensures that the non-forced code receives the correct group numbers.
700 scoped_refptr<FieldTrial> forced_trial =
701 CreateFieldTrial(kTrialName, 100, kGroupAName);
702 forced_trial->AppendGroup(kGroupBName, 100);
703 forced_trial->SetForced();
704
705 scoped_refptr<FieldTrial> client_trial =
706 CreateFieldTrial(kTrialName, 100, kGroupBName);
707 client_trial->AppendGroup(kGroupAName, 50);
708
709 EXPECT_FALSE(client_trial->group_reported_);
710 EXPECT_NE(kGroupAName, client_trial->group_name());
711 EXPECT_TRUE(client_trial->group_reported_);
712 EXPECT_EQ(kGroupBName, client_trial->group_name());
713 }
714
TEST_F(FieldTrialTest,SetForcedChangeDefault_NonDefault)715 TEST_F(FieldTrialTest, SetForcedChangeDefault_NonDefault) {
716 const char kTrialName[] = "SetForcedDefaultGroupChange";
717 const char kGroupAName[] = "A";
718 const char kGroupBName[] = "B";
719 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
720
721 // Simulate a server-side (forced) config that switches which group is default
722 // and ensures that the non-forced code receives the correct group numbers.
723 scoped_refptr<FieldTrial> forced_trial =
724 CreateFieldTrial(kTrialName, 100, kGroupAName);
725 forced_trial->AppendGroup(kGroupBName, 0);
726 forced_trial->SetForced();
727
728 scoped_refptr<FieldTrial> client_trial =
729 CreateFieldTrial(kTrialName, 100, kGroupBName);
730 client_trial->AppendGroup(kGroupAName, 50);
731
732 EXPECT_FALSE(client_trial->group_reported_);
733 EXPECT_EQ(kGroupAName, client_trial->group_name());
734 EXPECT_TRUE(client_trial->group_reported_);
735 EXPECT_EQ(kGroupAName, client_trial->group_name());
736 }
737
TEST_F(FieldTrialTest,Observe)738 TEST_F(FieldTrialTest, Observe) {
739 const char kTrialName[] = "TrialToObserve1";
740 const char kSecondaryGroupName[] = "SecondaryGroup";
741
742 TestFieldTrialObserver observer;
743 scoped_refptr<FieldTrial> trial =
744 CreateFieldTrial(kTrialName, 100, kDefaultGroupName);
745 trial->AppendGroup(kSecondaryGroupName, 50);
746 const std::string chosen_group_name = trial->group_name();
747 EXPECT_TRUE(chosen_group_name == kDefaultGroupName ||
748 chosen_group_name == kSecondaryGroupName);
749
750 // The observer should be notified synchronously by the group_name() call.
751 EXPECT_EQ(kTrialName, observer.trial_name());
752 EXPECT_EQ(chosen_group_name, observer.group_name());
753 }
754
755 // Verify that no hang occurs when a FieldTrial group is selected from a
756 // FieldTrialList::Observer::OnFieldTrialGroupFinalized() notification. If the
757 // FieldTrialList's lock is held when observers are notified, this test will
758 // hang due to reentrant lock acquisition when selecting the FieldTrial group.
TEST_F(FieldTrialTest,ObserveReentrancy)759 TEST_F(FieldTrialTest, ObserveReentrancy) {
760 const char kTrialName1[] = "TrialToObserve1";
761 const char kTrialName2[] = "TrialToObserve2";
762
763 scoped_refptr<FieldTrial> trial_1 =
764 CreateFieldTrial(kTrialName1, 100, kDefaultGroupName);
765
766 FieldTrialObserverAccessingGroup observer(trial_1);
767
768 scoped_refptr<FieldTrial> trial_2 =
769 CreateFieldTrial(kTrialName2, 100, kDefaultGroupName);
770
771 // No group should be selected for |trial_1| yet.
772 EXPECT_EQ(FieldTrial::kNotFinalized, trial_1->group_);
773
774 // Force selection of a group for |trial_2|. This will notify |observer| which
775 // will force the selection of a group for |trial_1|. This should not hang.
776 trial_2->Activate();
777
778 // The above call should have selected a group for |trial_1|.
779 EXPECT_NE(FieldTrial::kNotFinalized, trial_1->group_);
780 }
781
TEST_F(FieldTrialTest,NotDisabled)782 TEST_F(FieldTrialTest, NotDisabled) {
783 const char kTrialName[] = "NotDisabled";
784 const char kGroupName[] = "Group2";
785 const int kProbability = 100;
786 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
787
788 scoped_refptr<FieldTrial> trial =
789 CreateFieldTrial(kTrialName, kProbability, kDefaultGroupName);
790 trial->AppendGroup(kGroupName, kProbability);
791 EXPECT_EQ(kGroupName, trial->group_name());
792 }
793
TEST_F(FieldTrialTest,FloatBoundariesGiveEqualGroupSizes)794 TEST_F(FieldTrialTest, FloatBoundariesGiveEqualGroupSizes) {
795 const int kBucketCount = 100;
796
797 // Try each boundary value |i / 100.0| as the entropy value.
798 for (int i = 0; i < kBucketCount; ++i) {
799 const double entropy = i / static_cast<double>(kBucketCount);
800
801 scoped_refptr<FieldTrial> trial(
802 new FieldTrial("test", kBucketCount, "default", entropy,
803 /*is_low_anonymity=*/false, /*is_overridden=*/false));
804 for (int j = 0; j < kBucketCount; ++j)
805 trial->AppendGroup(NumberToString(j), 1);
806
807 EXPECT_EQ(NumberToString(i), trial->group_name());
808 }
809 }
810
TEST_F(FieldTrialTest,DoesNotSurpassTotalProbability)811 TEST_F(FieldTrialTest, DoesNotSurpassTotalProbability) {
812 const double kEntropyValue = 1.0 - 1e-9;
813 ASSERT_LT(kEntropyValue, 1.0);
814
815 scoped_refptr<FieldTrial> trial(
816 new FieldTrial("test", 2, "default", kEntropyValue,
817 /*is_low_anonymity=*/false, /*is_overridden=*/false));
818 trial->AppendGroup("1", 1);
819 trial->AppendGroup("2", 1);
820
821 EXPECT_EQ("2", trial->group_name());
822 }
823
TEST_F(FieldTrialTest,CreateSimulatedFieldTrial)824 TEST_F(FieldTrialTest, CreateSimulatedFieldTrial) {
825 const char kTrialName[] = "CreateSimulatedFieldTrial";
826 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
827
828 // Different cases to test, e.g. default vs. non default group being chosen.
829 struct {
830 double entropy_value;
831 const char* expected_group;
832 } test_cases[] = {
833 { 0.4, "A" },
834 { 0.85, "B" },
835 { 0.95, kDefaultGroupName },
836 };
837
838 for (auto& test_case : test_cases) {
839 TestFieldTrialObserver observer;
840 scoped_refptr<FieldTrial> trial(FieldTrial::CreateSimulatedFieldTrial(
841 kTrialName, 100, kDefaultGroupName, test_case.entropy_value));
842 trial->AppendGroup("A", 80);
843 trial->AppendGroup("B", 10);
844 EXPECT_EQ(test_case.expected_group, trial->group_name());
845
846 // Field trial shouldn't have been registered with the list.
847 EXPECT_FALSE(FieldTrialList::TrialExists(kTrialName));
848 EXPECT_EQ(0u, FieldTrialList::GetFieldTrialCount());
849
850 // Observer shouldn't have been notified.
851 RunLoop().RunUntilIdle();
852 EXPECT_TRUE(observer.trial_name().empty());
853
854 // The trial shouldn't be in the active set of trials.
855 FieldTrial::ActiveGroups active_groups;
856 FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
857 EXPECT_TRUE(active_groups.empty());
858
859 // The trial shouldn't be listed in the |AllStatesToString()| result.
860 std::string states;
861 FieldTrialList::AllStatesToString(&states);
862 EXPECT_TRUE(states.empty());
863 }
864 }
865
TEST(FieldTrialTestWithoutList,StatesStringFormat)866 TEST(FieldTrialTestWithoutList, StatesStringFormat) {
867 std::string save_string;
868
869 test::ScopedFeatureList scoped_feature_list;
870 // The test suite instantiates a FieldTrialList but for the purpose of these
871 // tests it's cleaner to start from scratch.
872 scoped_feature_list.InitWithEmptyFeatureAndFieldTrialLists();
873
874 // Scoping the first FieldTrialList, as we need another one to test the
875 // importing function.
876 {
877 test::ScopedFeatureList scoped_feature_list1;
878 scoped_feature_list1.InitWithNullFeatureAndFieldTrialLists();
879 FieldTrialList field_trial_list;
880
881 scoped_refptr<FieldTrial> trial =
882 CreateFieldTrial("Abc", 10, "Default some name");
883 trial->AppendGroup("cba", 10);
884 trial->Activate();
885 scoped_refptr<FieldTrial> trial2 =
886 CreateFieldTrial("Xyz", 10, "Default xxx");
887 trial2->AppendGroup("zyx", 10);
888 trial2->Activate();
889 scoped_refptr<FieldTrial> trial3 = CreateFieldTrial("zzz", 10, "default");
890
891 FieldTrialList::AllStatesToString(&save_string);
892 }
893
894 // Starting with a new blank FieldTrialList.
895 test::ScopedFeatureList scoped_feature_list2;
896 scoped_feature_list2.InitWithNullFeatureAndFieldTrialLists();
897 FieldTrialList field_trial_list;
898 ASSERT_TRUE(field_trial_list.CreateTrialsFromString(save_string));
899
900 FieldTrial::ActiveGroups active_groups;
901 field_trial_list.GetActiveFieldTrialGroups(&active_groups);
902 ASSERT_EQ(2U, active_groups.size());
903 EXPECT_EQ("Abc", active_groups[0].trial_name);
904 EXPECT_EQ("cba", active_groups[0].group_name);
905 EXPECT_EQ("Xyz", active_groups[1].trial_name);
906 EXPECT_EQ("zyx", active_groups[1].group_name);
907 EXPECT_TRUE(field_trial_list.TrialExists("zzz"));
908 }
909
910 class FieldTrialListTest : public ::testing::Test {
911 public:
FieldTrialListTest()912 FieldTrialListTest() {
913 // The test suite instantiates a FieldTrialList but for the purpose of these
914 // tests it's cleaner to start from scratch.
915 scoped_feature_list_.InitWithEmptyFeatureAndFieldTrialLists();
916 }
917
918 private:
919 test::ScopedFeatureList scoped_feature_list_;
920 };
921
TEST_F(FieldTrialListTest,InstantiateAllocator)922 TEST_F(FieldTrialListTest, InstantiateAllocator) {
923 test::ScopedFeatureList scoped_feature_list;
924 scoped_feature_list.InitWithEmptyFeatureAndFieldTrialLists();
925
926 FieldTrialList* field_trial_list = FieldTrialList::GetInstance();
927
928 FieldTrialList::CreateFieldTrial("Trial1", "Group1");
929
930 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
931 const void* memory = field_trial_list->field_trial_allocator_->data();
932 size_t used = field_trial_list->field_trial_allocator_->used();
933
934 // Ensure that the function is idempotent.
935 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
936 const void* new_memory = field_trial_list->field_trial_allocator_->data();
937 size_t new_used = field_trial_list->field_trial_allocator_->used();
938 EXPECT_EQ(memory, new_memory);
939 EXPECT_EQ(used, new_used);
940 }
941
TEST_F(FieldTrialListTest,AddTrialsToAllocator)942 TEST_F(FieldTrialListTest, AddTrialsToAllocator) {
943 std::string save_string;
944 base::ReadOnlySharedMemoryRegion shm_region;
945
946 // Scoping the first FieldTrialList, as we need another one to test that it
947 // matches.
948 {
949 test::ScopedFeatureList scoped_feature_list1;
950 scoped_feature_list1.InitWithEmptyFeatureAndFieldTrialLists();
951
952 FieldTrialList::CreateFieldTrial("Trial1", "Group1");
953 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
954 FieldTrialList::AllStatesToString(&save_string);
955 shm_region = FieldTrialList::DuplicateFieldTrialSharedMemoryForTesting();
956 ASSERT_TRUE(shm_region.IsValid());
957 }
958
959 test::ScopedFeatureList scoped_feature_list2;
960 scoped_feature_list2.InitWithEmptyFeatureAndFieldTrialLists();
961
962 // 4 KiB is enough to hold the trials only created for this test.
963 base::ReadOnlySharedMemoryMapping shm_mapping = shm_region.MapAt(0, 4 << 10);
964 ASSERT_TRUE(shm_mapping.IsValid());
965 FieldTrialList::CreateTrialsFromSharedMemoryMapping(std::move(shm_mapping));
966 std::string check_string;
967 FieldTrialList::AllStatesToString(&check_string);
968 EXPECT_EQ(save_string, check_string);
969 }
970
TEST_F(FieldTrialListTest,DoNotAddSimulatedFieldTrialsToAllocator)971 TEST_F(FieldTrialListTest, DoNotAddSimulatedFieldTrialsToAllocator) {
972 constexpr char kTrialName[] = "trial";
973 base::ReadOnlySharedMemoryRegion shm_region;
974 {
975 test::ScopedFeatureList scoped_feature_list1;
976 scoped_feature_list1.InitWithEmptyFeatureAndFieldTrialLists();
977
978 // Create a simulated trial and a real trial and call Activate() on them,
979 // which should only add the real trial to the field trial allocator.
980 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
981
982 // This shouldn't add to the allocator.
983 scoped_refptr<FieldTrial> simulated_trial =
984 FieldTrial::CreateSimulatedFieldTrial(kTrialName, 100, "Simulated",
985 0.95);
986 simulated_trial->Activate();
987
988 // This should add to the allocator.
989 FieldTrial* real_trial =
990 FieldTrialList::CreateFieldTrial(kTrialName, "Real");
991 real_trial->Activate();
992
993 shm_region = FieldTrialList::DuplicateFieldTrialSharedMemoryForTesting();
994 ASSERT_TRUE(shm_region.IsValid());
995 }
996
997 // Check that there's only one entry in the allocator.
998 test::ScopedFeatureList scoped_feature_list2;
999 scoped_feature_list2.InitWithEmptyFeatureAndFieldTrialLists();
1000 // 4 KiB is enough to hold the trials only created for this test.
1001 base::ReadOnlySharedMemoryMapping shm_mapping = shm_region.MapAt(0, 4 << 10);
1002 ASSERT_TRUE(shm_mapping.IsValid());
1003 FieldTrialList::CreateTrialsFromSharedMemoryMapping(std::move(shm_mapping));
1004 std::string check_string;
1005 FieldTrialList::AllStatesToString(&check_string);
1006 ASSERT_EQ(check_string.find("Simulated"), std::string::npos);
1007 }
1008
TEST_F(FieldTrialListTest,AssociateFieldTrialParams)1009 TEST_F(FieldTrialListTest, AssociateFieldTrialParams) {
1010 test::ScopedFeatureList scoped_feature_list;
1011 scoped_feature_list.InitWithEmptyFeatureAndFieldTrialLists();
1012
1013 std::string trial_name("Trial1");
1014 std::string group_name("Group1");
1015
1016 // Create a field trial with some params.
1017 FieldTrialList::CreateFieldTrial(trial_name, group_name);
1018 std::map<std::string, std::string> params;
1019 params["key1"] = "value1";
1020 params["key2"] = "value2";
1021 FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
1022 trial_name, group_name, params);
1023 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
1024
1025 // Clear all cached params from the associator.
1026 FieldTrialParamAssociator::GetInstance()->ClearAllCachedParamsForTesting();
1027 // Check that the params have been cleared from the cache.
1028 std::map<std::string, std::string> cached_params;
1029 FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback(
1030 trial_name, group_name, &cached_params);
1031 EXPECT_EQ(0U, cached_params.size());
1032
1033 // Check that we fetch the param from shared memory properly.
1034 std::map<std::string, std::string> new_params;
1035 GetFieldTrialParams(trial_name, &new_params);
1036 EXPECT_EQ("value1", new_params["key1"]);
1037 EXPECT_EQ("value2", new_params["key2"]);
1038 EXPECT_EQ(2U, new_params.size());
1039 }
1040
TEST_F(FieldTrialListTest,ClearParamsFromSharedMemory)1041 TEST_F(FieldTrialListTest, ClearParamsFromSharedMemory) {
1042 std::string trial_name("Trial1");
1043 std::string group_name("Group1");
1044
1045 base::ReadOnlySharedMemoryRegion shm_region;
1046 {
1047 test::ScopedFeatureList scoped_feature_list1;
1048 scoped_feature_list1.InitWithEmptyFeatureAndFieldTrialLists();
1049
1050 // Create a field trial with some params.
1051 FieldTrial* trial =
1052 FieldTrialList::CreateFieldTrial(trial_name, group_name);
1053 std::map<std::string, std::string> params;
1054 params["key1"] = "value1";
1055 params["key2"] = "value2";
1056 FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
1057 trial_name, group_name, params);
1058 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
1059
1060 // Clear all params from the associator AND shared memory. The allocated
1061 // segments should be different.
1062 FieldTrial::FieldTrialRef old_ref = trial->ref_;
1063 FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
1064 FieldTrial::FieldTrialRef new_ref = trial->ref_;
1065 EXPECT_NE(old_ref, new_ref);
1066
1067 // Check that there are no params associated with the field trial anymore.
1068 std::map<std::string, std::string> new_params;
1069 GetFieldTrialParams(trial_name, &new_params);
1070 EXPECT_EQ(0U, new_params.size());
1071
1072 // Now duplicate the handle so we can easily check that the trial is still
1073 // in shared memory via AllStatesToString.
1074 shm_region = FieldTrialList::DuplicateFieldTrialSharedMemoryForTesting();
1075 ASSERT_TRUE(shm_region.IsValid());
1076 }
1077
1078 // Check that we have the trial.
1079 test::ScopedFeatureList scoped_feature_list2;
1080 scoped_feature_list2.InitWithEmptyFeatureAndFieldTrialLists();
1081 // 4 KiB is enough to hold the trials only created for this test.
1082 base::ReadOnlySharedMemoryMapping shm_mapping = shm_region.MapAt(0, 4 << 10);
1083 ASSERT_TRUE(shm_mapping.IsValid());
1084 FieldTrialList::CreateTrialsFromSharedMemoryMapping(std::move(shm_mapping));
1085 std::string check_string;
1086 FieldTrialList::AllStatesToString(&check_string);
1087 EXPECT_EQ("*Trial1/Group1", check_string);
1088 }
1089
TEST_F(FieldTrialListTest,DumpAndFetchFromSharedMemory)1090 TEST_F(FieldTrialListTest, DumpAndFetchFromSharedMemory) {
1091 std::string trial_name("Trial1");
1092 std::string group_name("Group1");
1093
1094 // Create a field trial with some params.
1095 test::ScopedFeatureList scoped_feature_list;
1096 scoped_feature_list.InitWithEmptyFeatureAndFieldTrialLists();
1097
1098 FieldTrialList::CreateFieldTrial(trial_name, group_name);
1099 FieldTrialList::CreateFieldTrial("Trial2", "Group2", false,
1100 /*is_overridden=*/true);
1101 std::map<std::string, std::string> params;
1102 params["key1"] = "value1";
1103 params["key2"] = "value2";
1104 FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
1105 trial_name, group_name, params);
1106
1107 // 4 KiB is enough to hold the trials only created for this test.
1108 base::MappedReadOnlyRegion shm =
1109 base::ReadOnlySharedMemoryRegion::Create(4 << 10);
1110 ASSERT_TRUE(shm.IsValid());
1111 // We _could_ use PersistentMemoryAllocator, this just has less params.
1112 WritableSharedPersistentMemoryAllocator allocator(std::move(shm.mapping), 1,
1113 "");
1114
1115 // Dump and subsequently retrieve the field trial to |allocator|.
1116 FieldTrialList::DumpAllFieldTrialsToPersistentAllocator(&allocator);
1117 std::vector<const FieldTrial::FieldTrialEntry*> entries =
1118 FieldTrialList::GetAllFieldTrialsFromPersistentAllocator(allocator);
1119
1120 // Check that we have the entry we put in.
1121 EXPECT_EQ(2u, entries.size());
1122 const FieldTrial::FieldTrialEntry* entry1 = entries[0];
1123 const FieldTrial::FieldTrialEntry* entry2 = entries[1];
1124
1125 // Check that the trial information matches.
1126 std::string_view shm_trial_name;
1127 std::string_view shm_group_name;
1128 bool overridden;
1129 ASSERT_TRUE(entry1->GetState(shm_trial_name, shm_group_name, overridden));
1130 EXPECT_EQ(trial_name, shm_trial_name);
1131 EXPECT_EQ(group_name, shm_group_name);
1132 EXPECT_FALSE(overridden);
1133
1134 // Check that the params match.
1135 std::map<std::string, std::string> shm_params;
1136 entry1->GetParams(&shm_params);
1137 EXPECT_EQ(2u, shm_params.size());
1138 EXPECT_EQ("value1", shm_params["key1"]);
1139 EXPECT_EQ("value2", shm_params["key2"]);
1140
1141 ASSERT_TRUE(entry2->GetState(shm_trial_name, shm_group_name, overridden));
1142 EXPECT_EQ("Trial2", shm_trial_name);
1143 EXPECT_EQ("Group2", shm_group_name);
1144 EXPECT_TRUE(overridden);
1145 }
1146
1147 #if BUILDFLAG(USE_BLINK)
1148
1149 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
1150 constexpr GlobalDescriptors::Key kFDKey = 42;
1151 #endif
1152
1153 BASE_FEATURE(kTestFeatureA, "TestFeatureA", base::FEATURE_DISABLED_BY_DEFAULT);
1154 BASE_FEATURE(kTestFeatureB, "TestFeatureB", base::FEATURE_ENABLED_BY_DEFAULT);
1155 BASE_FEATURE(kTestFeatureC, "TestFeatureC", base::FEATURE_ENABLED_BY_DEFAULT);
1156
MULTIPROCESS_TEST_MAIN(CreateTrialsInChildProcess)1157 MULTIPROCESS_TEST_MAIN(CreateTrialsInChildProcess) {
1158 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
1159 // Since the fd value will be mapped from the global descriptors singleton,
1160 // set it there. We use the same value both for the key and the actual fd for
1161 // simplicity.
1162 // Note: On Android, the launch service already sets up the mapping.
1163 base::GlobalDescriptors::GetInstance()->Set(kFDKey, kFDKey);
1164 #endif
1165
1166 // Create and populate the field trial list singleton.
1167 FieldTrialList field_trial_list;
1168 FieldTrialList::CreateTrialsInChildProcess(*CommandLine::ForCurrentProcess());
1169
1170 // Create and populate the feature list singleton.
1171 auto feature_list = std::make_unique<FeatureList>();
1172 base::FieldTrialList::ApplyFeatureOverridesInChildProcess(feature_list.get());
1173 FeatureList::SetInstance(std::move(feature_list));
1174
1175 // Validate the expected field trial and feaure state
1176 CHECK_EQ("Group1", FieldTrialList::FindFullName("Trial1"));
1177 CHECK(FeatureList::IsEnabled(kTestFeatureA));
1178 CHECK(!FeatureList::IsEnabled(kTestFeatureB));
1179 CHECK(!FeatureList::IsEnabled(kTestFeatureC));
1180 return 0;
1181 }
1182
1183 #if !BUILDFLAG(IS_IOS)
TEST_F(FieldTrialListTest,PassFieldTrialSharedMemoryOnCommandLine)1184 TEST_F(FieldTrialListTest, PassFieldTrialSharedMemoryOnCommandLine) {
1185 // Setup some field trial state.
1186 test::ScopedFeatureList scoped_feature_list1;
1187 scoped_feature_list1.InitWithEmptyFeatureAndFieldTrialLists();
1188 std::unique_ptr<FeatureList> feature_list(new FeatureList);
1189 feature_list->InitFromCommandLine(kTestFeatureA.name, kTestFeatureB.name);
1190 FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial1", "Group1");
1191 feature_list->RegisterFieldTrialOverride(
1192 kTestFeatureC.name, FeatureList::OVERRIDE_DISABLE_FEATURE, trial);
1193 test::ScopedFeatureList scoped_feature_list2;
1194 scoped_feature_list2.InitWithFeatureList(std::move(feature_list));
1195
1196 // Prepare to launch a child process.
1197 CommandLine command_line = GetMultiProcessTestChildBaseCommandLine();
1198 LaunchOptions launch_options;
1199 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
1200 ScopedFD fd_to_share;
1201 #endif
1202 FieldTrialList::PopulateLaunchOptionsWithFieldTrialState(
1203 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
1204 kFDKey, fd_to_share,
1205 #endif
1206 &command_line, &launch_options);
1207 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
1208 launch_options.fds_to_remap.emplace_back(fd_to_share.get(), kFDKey);
1209 #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
1210
1211 // The shared memory handle should be specified.
1212 EXPECT_TRUE(command_line.HasSwitch(switches::kFieldTrialHandle));
1213
1214 // Explicitly specified enabled/disabled features should be specified.
1215 EXPECT_EQ(kTestFeatureA.name,
1216 command_line.GetSwitchValueASCII(switches::kEnableFeatures));
1217 EXPECT_EQ(kTestFeatureB.name,
1218 command_line.GetSwitchValueASCII(switches::kDisableFeatures));
1219
1220 // Run the child.
1221 Process process = SpawnMultiProcessTestChild("CreateTrialsInChildProcess",
1222 command_line, launch_options);
1223 int exit_code = -1;
1224 EXPECT_TRUE(WaitForMultiprocessTestChildExit(
1225 process, TestTimeouts::action_timeout(), &exit_code));
1226 EXPECT_EQ(0, exit_code);
1227 }
1228 #endif
1229
1230 // Verify that the field trial shared memory handle is really read-only, and
1231 // does not allow writable mappings.
TEST_F(FieldTrialListTest,CheckReadOnlySharedMemoryRegion)1232 TEST_F(FieldTrialListTest, CheckReadOnlySharedMemoryRegion) {
1233 test::ScopedFeatureList scoped_feature_list;
1234 scoped_feature_list.InitWithEmptyFeatureAndFieldTrialLists();
1235
1236 FieldTrialList::CreateFieldTrial("Trial1", "Group1");
1237
1238 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded();
1239
1240 base::ReadOnlySharedMemoryRegion region =
1241 FieldTrialList::DuplicateFieldTrialSharedMemoryForTesting();
1242 ASSERT_TRUE(region.IsValid());
1243
1244 ASSERT_TRUE(CheckReadOnlyPlatformSharedMemoryRegionForTesting(
1245 base::ReadOnlySharedMemoryRegion::TakeHandleForSerialization(
1246 std::move(region))));
1247 }
1248 #endif // BUILDFLAG(USE_BLINK)
1249
TEST_F(FieldTrialListTest,TestGetRandomizedFieldTrialCount)1250 TEST_F(FieldTrialListTest, TestGetRandomizedFieldTrialCount) {
1251 EXPECT_EQ(0u, FieldTrialList::GetFieldTrialCount());
1252 EXPECT_EQ(0u, FieldTrialList::GetRandomizedFieldTrialCount());
1253
1254 const char name1[] = "name 1 test";
1255 const char name2[] = "name 2 test";
1256 const char name3[] = "name 3 test";
1257 const char group1[] = "group 1";
1258
1259 // Create a field trial with a single group.
1260 scoped_refptr<FieldTrial> trial1 =
1261 FieldTrialList::CreateFieldTrial(name1, group1);
1262 EXPECT_NE(FieldTrial::kNotFinalized, trial1->group_);
1263 EXPECT_EQ(group1, trial1->group_name_internal());
1264
1265 EXPECT_EQ(1u, FieldTrialList::GetFieldTrialCount());
1266 EXPECT_EQ(0u, FieldTrialList::GetRandomizedFieldTrialCount());
1267
1268 // Create a randomized field trial.
1269 scoped_refptr<FieldTrial> trial2 =
1270 CreateFieldTrial(name2, 10, "default name 2 test");
1271 EXPECT_EQ(FieldTrial::kNotFinalized, trial2->group_);
1272 EXPECT_EQ(name2, trial2->trial_name());
1273 EXPECT_EQ("", trial2->group_name_internal());
1274
1275 EXPECT_EQ(2u, FieldTrialList::GetFieldTrialCount());
1276 EXPECT_EQ(1u, FieldTrialList::GetRandomizedFieldTrialCount());
1277
1278 // Append a first group to trial 2. This doesn't affect GetFieldTrialCount()
1279 // and GetRandomizedFieldTrialCount().
1280 trial2->AppendGroup("a first group", 7);
1281
1282 EXPECT_EQ(2u, FieldTrialList::GetFieldTrialCount());
1283 EXPECT_EQ(1u, FieldTrialList::GetRandomizedFieldTrialCount());
1284
1285 // Create another randomized field trial.
1286 scoped_refptr<FieldTrial> trial3 =
1287 CreateFieldTrial(name3, 10, "default name 3 test");
1288 EXPECT_EQ(FieldTrial::kNotFinalized, trial3->group_);
1289 EXPECT_EQ(name3, trial3->trial_name());
1290 EXPECT_EQ("", trial3->group_name_internal());
1291
1292 EXPECT_EQ(3u, FieldTrialList::GetFieldTrialCount());
1293 EXPECT_EQ(2u, FieldTrialList::GetRandomizedFieldTrialCount());
1294
1295 // Note: FieldTrialList should delete the objects at shutdown.
1296 }
1297
TEST_F(FieldTrialTest,TestAllParamsToString)1298 TEST_F(FieldTrialTest, TestAllParamsToString) {
1299 std::string exptected_output = "t1.g1:p1/v1/p2/v2";
1300
1301 // Create study with one group and two params.
1302 std::map<std::string, std::string> params;
1303 params["p1"] = "v1";
1304 params["p2"] = "v2";
1305 FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
1306 "t1", "g1", params);
1307 EXPECT_EQ("", FieldTrialList::AllParamsToString(&MockEscapeQueryParamValue));
1308
1309 scoped_refptr<FieldTrial> trial1 = CreateFieldTrial("t1", 100, "Default");
1310 trial1->AppendGroup("g1", 100);
1311 trial1->Activate();
1312 EXPECT_EQ(exptected_output,
1313 FieldTrialList::AllParamsToString(&MockEscapeQueryParamValue));
1314
1315 // Create study with two groups and params that don't belog to the assigned
1316 // group. This should be in the output.
1317 FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
1318 "t2", "g2", params);
1319 scoped_refptr<FieldTrial> trial2 = CreateFieldTrial("t2", 100, "Default");
1320 trial2->AppendGroup("g1", 100);
1321 trial2->AppendGroup("g2", 0);
1322 trial2->Activate();
1323 EXPECT_EQ(exptected_output,
1324 FieldTrialList::AllParamsToString(&MockEscapeQueryParamValue));
1325 }
1326
TEST_F(FieldTrialTest,GetActiveFieldTrialGroups_LowAnonymity)1327 TEST_F(FieldTrialTest, GetActiveFieldTrialGroups_LowAnonymity) {
1328 // Create a field trial with a single winning group.
1329 scoped_refptr<FieldTrial> trial_1 = CreateFieldTrial("Normal", 10, "Default");
1330 trial_1->AppendGroup("Winner 1", 10);
1331 trial_1->Activate();
1332
1333 // Create a second field trial with a single winning group, marked as
1334 // low-anonymity.
1335 scoped_refptr<FieldTrial> trial_2 = CreateFieldTrial(
1336 "Low anonymity", 10, "Default", /*is_low_anonymity=*/true);
1337 trial_2->AppendGroup("Winner 2", 10);
1338 trial_2->Activate();
1339
1340 // Check that |FieldTrialList::GetActiveFieldTrialGroups()| does not include
1341 // the low-anonymity trial.
1342 FieldTrial::ActiveGroups active_groups_for_metrics;
1343 FieldTrialList::GetActiveFieldTrialGroups(&active_groups_for_metrics);
1344 EXPECT_THAT(
1345 active_groups_for_metrics,
1346 testing::UnorderedPointwise(CompareActiveGroupToFieldTrial(), {trial_1}));
1347
1348 // Check that
1349 // |FieldTrialListIncludingLowAnonymity::GetActiveFieldTrialGroups()| includes
1350 // both trials.
1351 FieldTrial::ActiveGroups active_groups;
1352 FieldTrialListIncludingLowAnonymity::GetActiveFieldTrialGroupsForTesting(
1353 &active_groups);
1354 EXPECT_THAT(active_groups,
1355 testing::UnorderedPointwise(CompareActiveGroupToFieldTrial(),
1356 {trial_1, trial_2}));
1357 }
1358
TEST_F(FieldTrialTest,ObserveIncludingLowAnonymity)1359 TEST_F(FieldTrialTest, ObserveIncludingLowAnonymity) {
1360 TestFieldTrialObserver observer;
1361 TestFieldTrialObserverIncludingLowAnonymity low_anonymity_observer;
1362
1363 // Create a low-anonymity trial with one active group.
1364 const char kTrialName[] = "TrialToObserve1";
1365 scoped_refptr<FieldTrial> trial = CreateFieldTrial(
1366 kTrialName, 100, kDefaultGroupName, /*is_low_anonymity=*/true);
1367 trial->Activate();
1368
1369 // Only the low_anonymity_observer should be notified.
1370 EXPECT_EQ("", observer.trial_name());
1371 EXPECT_EQ("", observer.group_name());
1372 EXPECT_EQ(kTrialName, low_anonymity_observer.trial_name());
1373 EXPECT_EQ(kDefaultGroupName, low_anonymity_observer.group_name());
1374 }
1375
TEST_F(FieldTrialTest,ParseFieldTrialsString)1376 TEST_F(FieldTrialTest, ParseFieldTrialsString) {
1377 std::vector<FieldTrial::State> entries;
1378 ASSERT_TRUE(FieldTrial::ParseFieldTrialsString(
1379 "Trial1/Group1", /*override_trials=*/false, entries));
1380
1381 ASSERT_EQ(entries.size(), 1ul);
1382 const FieldTrial::State& entry = entries[0];
1383 EXPECT_EQ("Trial1", entry.trial_name);
1384 EXPECT_EQ("Group1", entry.group_name);
1385 EXPECT_EQ(false, entry.activated);
1386 EXPECT_EQ(false, entry.is_overridden);
1387 }
1388
TEST_F(FieldTrialTest,ParseFieldTrialsStringTwoStudies)1389 TEST_F(FieldTrialTest, ParseFieldTrialsStringTwoStudies) {
1390 std::vector<FieldTrial::State> entries;
1391 ASSERT_TRUE(FieldTrial::ParseFieldTrialsString(
1392 "Trial1/Group1/*Trial2/Group2/", /*override_trials=*/false, entries));
1393
1394 ASSERT_EQ(entries.size(), 2ul);
1395 const FieldTrial::State& entry1 = entries[0];
1396 EXPECT_EQ("Trial1", entry1.trial_name);
1397 EXPECT_EQ("Group1", entry1.group_name);
1398 EXPECT_EQ(false, entry1.activated);
1399 EXPECT_EQ(false, entry1.is_overridden);
1400
1401 const FieldTrial::State& entry2 = entries[1];
1402 EXPECT_EQ("Trial2", entry2.trial_name);
1403 EXPECT_EQ("Group2", entry2.group_name);
1404 EXPECT_EQ(true, entry2.activated);
1405 EXPECT_EQ(false, entry2.is_overridden);
1406 }
1407
TEST_F(FieldTrialTest,ParseFieldTrialsStringEmpty)1408 TEST_F(FieldTrialTest, ParseFieldTrialsStringEmpty) {
1409 std::vector<FieldTrial::State> entries;
1410 ASSERT_TRUE(FieldTrial::ParseFieldTrialsString("", /*override_trials=*/false,
1411 entries));
1412
1413 ASSERT_EQ(entries.size(), 0ul);
1414 }
1415
TEST_F(FieldTrialTest,ParseFieldTrialsStringInvalid)1416 TEST_F(FieldTrialTest, ParseFieldTrialsStringInvalid) {
1417 std::vector<FieldTrial::State> entries;
1418 EXPECT_FALSE(FieldTrial::ParseFieldTrialsString(
1419 "A/", /*override_trials=*/false, entries));
1420 EXPECT_FALSE(FieldTrial::ParseFieldTrialsString(
1421 "/A", /*override_trials=*/false, entries));
1422 EXPECT_FALSE(FieldTrial::ParseFieldTrialsString(
1423 "//", /*override_trials=*/false, entries));
1424 EXPECT_FALSE(FieldTrial::ParseFieldTrialsString(
1425 "///", /*override_trials=*/false, entries));
1426 }
1427
TEST_F(FieldTrialTest,BuildFieldTrialStateString)1428 TEST_F(FieldTrialTest, BuildFieldTrialStateString) {
1429 FieldTrial::State state1;
1430 state1.trial_name = "Trial";
1431 state1.group_name = "Group";
1432 state1.activated = false;
1433
1434 FieldTrial::State state2;
1435 state2.trial_name = "Foo";
1436 state2.group_name = "Bar";
1437 state2.activated = true;
1438
1439 EXPECT_EQ("Trial/Group", FieldTrial::BuildFieldTrialStateString({state1}));
1440 EXPECT_EQ("Trial/Group/*Foo/Bar",
1441 FieldTrial::BuildFieldTrialStateString({state1, state2}));
1442 }
1443
1444 } // namespace base
1445