field_trial_unittest.cc revision 731df977c0511bca2206b5f333555b1205ff1f43
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Test of FieldTrial class
6
7#include "base/metrics/field_trial.h"
8
9#include "base/stringprintf.h"
10#include "testing/gtest/include/gtest/gtest.h"
11
12namespace base {
13
14class FieldTrialTest : public testing::Test {
15 public:
16  FieldTrialTest() : trial_list_() { }
17
18 private:
19  FieldTrialList trial_list_;
20};
21
22// Test registration, and also check that destructors are called for trials
23// (and that Purify doesn't catch us leaking).
24TEST_F(FieldTrialTest, Registration) {
25  const char* name1 = "name 1 test";
26  const char* name2 = "name 2 test";
27  EXPECT_FALSE(FieldTrialList::Find(name1));
28  EXPECT_FALSE(FieldTrialList::Find(name2));
29
30  FieldTrial* trial1 = new FieldTrial(name1, 10);
31  EXPECT_EQ(FieldTrial::kNotParticipating, trial1->group());
32  EXPECT_EQ(name1, trial1->name());
33  EXPECT_EQ("", trial1->group_name());
34
35  trial1->AppendGroup("", 7);
36
37  EXPECT_EQ(trial1, FieldTrialList::Find(name1));
38  EXPECT_FALSE(FieldTrialList::Find(name2));
39
40  FieldTrial* trial2 = new FieldTrial(name2, 10);
41  EXPECT_EQ(FieldTrial::kNotParticipating, trial2->group());
42  EXPECT_EQ(name2, trial2->name());
43  EXPECT_EQ("", trial2->group_name());
44
45  trial2->AppendGroup("a first group", 7);
46
47  EXPECT_EQ(trial1, FieldTrialList::Find(name1));
48  EXPECT_EQ(trial2, FieldTrialList::Find(name2));
49  // Note: FieldTrialList should delete the objects at shutdown.
50}
51
52TEST_F(FieldTrialTest, AbsoluteProbabilities) {
53  char always_true[] = " always true";
54  char always_false[] = " always false";
55  for (int i = 1; i < 250; ++i) {
56    // Try lots of names, by changing the first character of the name.
57    always_true[0] = i;
58    always_false[0] = i;
59
60    FieldTrial* trial_true = new FieldTrial(always_true, 10);
61    const std::string winner = "TheWinner";
62    int winner_group = trial_true->AppendGroup(winner, 10);
63
64    EXPECT_EQ(winner_group, trial_true->group());
65    EXPECT_EQ(winner, trial_true->group_name());
66
67    FieldTrial* trial_false = new FieldTrial(always_false, 10);
68    int loser_group = trial_false->AppendGroup("ALoser", 0);
69
70    EXPECT_NE(loser_group, trial_false->group());
71  }
72}
73
74TEST_F(FieldTrialTest, RemainingProbability) {
75  // First create a test that hasn't had a winner yet.
76  const std::string winner = "Winner";
77  const std::string loser = "Loser";
78  scoped_refptr<FieldTrial> trial;
79  int counter = 0;
80  do {
81    std::string name = StringPrintf("trial%d", ++counter);
82    trial = new FieldTrial(name, 10);
83    trial->AppendGroup(loser, 5);  // 50% chance of not being chosen.
84  } while (trial->group() != FieldTrial::kNotParticipating);
85
86  // Now add a winner with all remaining probability.
87  trial->AppendGroup(winner, FieldTrial::kAllRemainingProbability);
88
89  // And that winner should ALWAYS win.
90  EXPECT_EQ(winner, trial->group_name());
91}
92
93TEST_F(FieldTrialTest, FiftyFiftyProbability) {
94  // Check that even with small divisors, we have the proper probabilities, and
95  // all outcomes are possible.  Since this is a 50-50 test, it should get both
96  // outcomes in a few tries, but we'll try no more than 100 times (and be flaky
97  // with probability around 1 in 2^99).
98  bool first_winner = false;
99  bool second_winner = false;
100  int counter = 0;
101  do {
102    std::string name = base::StringPrintf("FiftyFifty%d", ++counter);
103    scoped_refptr<FieldTrial> trial = new FieldTrial(name, 2);
104    trial->AppendGroup("first", 1);  // 50% chance of being chosen.
105    if (trial->group() != FieldTrial::kNotParticipating) {
106      first_winner = true;
107      continue;
108    }
109    trial->AppendGroup("second", 1);  // Always chosen at this point.
110    EXPECT_NE(FieldTrial::kNotParticipating, trial->group());
111    second_winner = true;
112  } while ((!second_winner || !first_winner) && counter < 100);
113  EXPECT_TRUE(second_winner);
114  EXPECT_TRUE(first_winner);
115}
116
117TEST_F(FieldTrialTest, MiddleProbabilities) {
118  char name[] = " same name";
119  bool false_event_seen = false;
120  bool true_event_seen = false;
121  for (int i = 1; i < 250; ++i) {
122    name[0] = i;
123    FieldTrial* trial = new FieldTrial(name, 10);
124    int might_win = trial->AppendGroup("MightWin", 5);
125
126    if (trial->group() == might_win) {
127      true_event_seen = true;
128    } else {
129      false_event_seen = true;
130    }
131    if (false_event_seen && true_event_seen)
132      return;  // Successful test!!!
133  }
134  // Very surprising to get here. Probability should be around 1 in 2 ** 250.
135  // One of the following will fail.
136  EXPECT_TRUE(false_event_seen);
137  EXPECT_TRUE(true_event_seen);
138}
139
140TEST_F(FieldTrialTest, OneWinner) {
141  char name[] = "Some name";
142  int group_count(10);
143
144  FieldTrial* trial = new FieldTrial(name, group_count);
145  int winner_index(-2);
146  std::string winner_name;
147
148  for (int i = 1; i <= group_count; ++i) {
149    int might_win = trial->AppendGroup("", 1);
150
151    if (trial->group() == might_win) {
152      EXPECT_EQ(-2, winner_index);
153      winner_index = might_win;
154      StringAppendF(&winner_name, "%d", might_win);
155      EXPECT_EQ(winner_name, trial->group_name());
156    }
157  }
158  EXPECT_GE(winner_index, 0);
159  EXPECT_EQ(trial->group(), winner_index);
160  EXPECT_EQ(trial->group_name(), winner_name);
161}
162
163TEST_F(FieldTrialTest, Save) {
164  std::string save_string;
165
166  FieldTrial* trial = new FieldTrial("Some name", 10);
167  // There is no winner yet, so no textual group name is associated with trial.
168  EXPECT_EQ("", trial->group_name());
169  FieldTrialList::StatesToString(&save_string);
170  EXPECT_EQ("", save_string);
171  save_string.clear();
172
173  // Create a winning group.
174  trial->AppendGroup("Winner", 10);
175  FieldTrialList::StatesToString(&save_string);
176  EXPECT_EQ("Some name/Winner/", save_string);
177  save_string.clear();
178
179  // Create a second trial and winning group.
180  FieldTrial* trial2 = new FieldTrial("xxx", 10);
181  trial2->AppendGroup("yyyy", 10);
182
183  FieldTrialList::StatesToString(&save_string);
184  // We assume names are alphabetized... though this is not critical.
185  EXPECT_EQ("Some name/Winner/xxx/yyyy/", save_string);
186}
187
188TEST_F(FieldTrialTest, Restore) {
189  EXPECT_TRUE(FieldTrialList::Find("Some_name") == NULL);
190  EXPECT_TRUE(FieldTrialList::Find("xxx") == NULL);
191
192  FieldTrialList::StringAugmentsState("Some_name/Winner/xxx/yyyy/");
193
194  FieldTrial* trial = FieldTrialList::Find("Some_name");
195  ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
196  EXPECT_EQ("Winner", trial->group_name());
197  EXPECT_EQ("Some_name", trial->name());
198
199  trial = FieldTrialList::Find("xxx");
200  ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
201  EXPECT_EQ("yyyy", trial->group_name());
202  EXPECT_EQ("xxx", trial->name());
203}
204
205TEST_F(FieldTrialTest, BogusRestore) {
206  EXPECT_FALSE(FieldTrialList::StringAugmentsState("MissingSlash"));
207  EXPECT_FALSE(FieldTrialList::StringAugmentsState("MissingGroupName/"));
208  EXPECT_FALSE(FieldTrialList::StringAugmentsState("MissingFinalSlash/gname"));
209  EXPECT_FALSE(FieldTrialList::StringAugmentsState("/noname, only group/"));
210}
211
212TEST_F(FieldTrialTest, DuplicateRestore) {
213  FieldTrial* trial = new FieldTrial("Some name", 10);
214  trial->AppendGroup("Winner", 10);
215  std::string save_string;
216  FieldTrialList::StatesToString(&save_string);
217  EXPECT_EQ("Some name/Winner/", save_string);
218
219  // It is OK if we redundantly specify a winner.
220  EXPECT_TRUE(FieldTrialList::StringAugmentsState(save_string));
221
222  // But it is an error to try to change to a different winner.
223  EXPECT_FALSE(FieldTrialList::StringAugmentsState("Some name/Loser/"));
224}
225
226TEST_F(FieldTrialTest, MakeName) {
227  FieldTrial* trial = new FieldTrial("Field Trial", 10);
228  trial->AppendGroup("Winner", 10);
229  EXPECT_EQ("Histogram_Winner",
230            FieldTrial::MakeName("Histogram", "Field Trial"));
231}
232
233}  // namespace base
234