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