1 // Copyright 2013 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.
5 #include "components/variations/variations_seed_processor.h"
9 #include "base/command_line.h"
10 #include "base/strings/string_split.h"
11 #include "components/variations/processed_study.h"
12 #include "components/variations/variations_associated_data.h"
13 #include "testing/gtest/include/gtest/gtest.h"
15 namespace chrome_variations
{
19 // Converts |time| to Study proto format.
20 int64
TimeToProtoTime(const base::Time
& time
) {
21 return (time
- base::Time::UnixEpoch()).InSeconds();
24 // Constants for testing associating command line flags with trial groups.
25 const char kFlagStudyName
[] = "flag_test_trial";
26 const char kFlagGroup1Name
[] = "flag_group1";
27 const char kFlagGroup2Name
[] = "flag_group2";
28 const char kNonFlagGroupName
[] = "non_flag_group";
29 const char kForcingFlag1
[] = "flag_test1";
30 const char kForcingFlag2
[] = "flag_test2";
32 const VariationID kExperimentId
= 123;
34 // Adds an experiment to |study| with the specified |name| and |probability|.
35 Study_Experiment
* AddExperiment(const std::string
& name
, int probability
,
37 Study_Experiment
* experiment
= study
->add_experiment();
38 experiment
->set_name(name
);
39 experiment
->set_probability_weight(probability
);
43 // Populates |study| with test data used for testing associating command line
44 // flags with trials groups. The study will contain three groups, a default
45 // group that isn't associated with a flag, and two other groups, both
46 // associated with different flags.
47 Study
CreateStudyWithFlagGroups(int default_group_probability
,
48 int flag_group1_probability
,
49 int flag_group2_probability
) {
50 DCHECK_GE(default_group_probability
, 0);
51 DCHECK_GE(flag_group1_probability
, 0);
52 DCHECK_GE(flag_group2_probability
, 0);
54 study
.set_name(kFlagStudyName
);
55 study
.set_default_experiment_name(kNonFlagGroupName
);
57 AddExperiment(kNonFlagGroupName
, default_group_probability
, &study
);
58 AddExperiment(kFlagGroup1Name
, flag_group1_probability
, &study
)
59 ->set_forcing_flag(kForcingFlag1
);
60 AddExperiment(kFlagGroup2Name
, flag_group2_probability
, &study
)
61 ->set_forcing_flag(kForcingFlag2
);
66 // Tests whether a field trial is active (i.e. group() has been called on it).
67 bool IsFieldTrialActive(const std::string
& trial_name
) {
68 base::FieldTrial::ActiveGroups active_groups
;
69 base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups
);
70 for (size_t i
= 0; i
< active_groups
.size(); ++i
) {
71 if (active_groups
[i
].trial_name
== trial_name
)
79 class VariationsSeedProcessorTest
: public ::testing::Test
{
81 VariationsSeedProcessorTest() {
84 virtual ~VariationsSeedProcessorTest() {
85 // Ensure that the maps are cleared between tests, since they are stored as
86 // process singletons.
87 testing::ClearAllVariationIDs();
88 testing::ClearAllVariationParams();
91 bool CreateTrialFromStudy(const Study
* study
) {
92 ProcessedStudy processed_study
;
93 if (processed_study
.Init(study
, false)) {
94 VariationsSeedProcessor().CreateTrialFromStudy(processed_study
);
101 DISALLOW_COPY_AND_ASSIGN(VariationsSeedProcessorTest
);
104 TEST_F(VariationsSeedProcessorTest
, AllowForceGroupAndVariationId
) {
105 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1
);
107 base::FieldTrialList
field_trial_list(NULL
);
109 Study study
= CreateStudyWithFlagGroups(100, 0, 0);
110 study
.mutable_experiment(1)->set_google_web_experiment_id(kExperimentId
);
112 EXPECT_TRUE(CreateTrialFromStudy(&study
));
113 EXPECT_EQ(kFlagGroup1Name
,
114 base::FieldTrialList::FindFullName(kFlagStudyName
));
116 VariationID id
= GetGoogleVariationID(GOOGLE_WEB_PROPERTIES
, kFlagStudyName
,
118 EXPECT_EQ(kExperimentId
, id
);
121 // Test that the group for kForcingFlag1 is forced.
122 TEST_F(VariationsSeedProcessorTest
, ForceGroupWithFlag1
) {
123 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1
);
125 base::FieldTrialList
field_trial_list(NULL
);
127 Study study
= CreateStudyWithFlagGroups(100, 0, 0);
128 EXPECT_TRUE(CreateTrialFromStudy(&study
));
129 EXPECT_EQ(kFlagGroup1Name
,
130 base::FieldTrialList::FindFullName(kFlagStudyName
));
133 // Test that the group for kForcingFlag2 is forced.
134 TEST_F(VariationsSeedProcessorTest
, ForceGroupWithFlag2
) {
135 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2
);
137 base::FieldTrialList
field_trial_list(NULL
);
139 Study study
= CreateStudyWithFlagGroups(100, 0, 0);
140 EXPECT_TRUE(CreateTrialFromStudy(&study
));
141 EXPECT_EQ(kFlagGroup2Name
,
142 base::FieldTrialList::FindFullName(kFlagStudyName
));
145 TEST_F(VariationsSeedProcessorTest
, ForceGroup_ChooseFirstGroupWithFlag
) {
146 // Add the flag to the command line arguments so the flag group is forced.
147 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1
);
148 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2
);
150 base::FieldTrialList
field_trial_list(NULL
);
152 Study study
= CreateStudyWithFlagGroups(100, 0, 0);
153 EXPECT_TRUE(CreateTrialFromStudy(&study
));
154 EXPECT_EQ(kFlagGroup1Name
,
155 base::FieldTrialList::FindFullName(kFlagStudyName
));
158 TEST_F(VariationsSeedProcessorTest
, ForceGroup_DontChooseGroupWithFlag
) {
159 base::FieldTrialList
field_trial_list(NULL
);
161 // The two flag groups are given high probability, which would normally make
162 // them very likely to be chosen. They won't be chosen since flag groups are
163 // never chosen when their flag isn't present.
164 Study study
= CreateStudyWithFlagGroups(1, 999, 999);
165 EXPECT_TRUE(CreateTrialFromStudy(&study
));
166 EXPECT_EQ(kNonFlagGroupName
,
167 base::FieldTrialList::FindFullName(kFlagStudyName
));
170 TEST_F(VariationsSeedProcessorTest
,
171 NonExpiredStudyPrioritizedOverExpiredStudy
) {
172 VariationsSeedProcessor seed_processor
;
174 const std::string kTrialName
= "A";
175 const std::string kGroup1Name
= "Group1";
178 Study
* study1
= seed
.add_study();
179 study1
->set_name(kTrialName
);
180 study1
->set_default_experiment_name("Default");
181 AddExperiment(kGroup1Name
, 100, study1
);
182 AddExperiment("Default", 0, study1
);
183 Study
* study2
= seed
.add_study();
185 ASSERT_EQ(seed
.study(0).name(), seed
.study(1).name());
187 const base::Time year_ago
=
188 base::Time::Now() - base::TimeDelta::FromDays(365);
190 const base::Version
version("20.0.0.0");
192 // Check that adding [expired, non-expired] activates the non-expired one.
193 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName
));
195 base::FieldTrialList
field_trial_list(NULL
);
196 study1
->set_expiry_date(TimeToProtoTime(year_ago
));
197 seed_processor
.CreateTrialsFromSeed(seed
, "en-CA", base::Time::Now(),
198 version
, Study_Channel_STABLE
,
199 Study_FormFactor_DESKTOP
, "");
200 EXPECT_EQ(kGroup1Name
, base::FieldTrialList::FindFullName(kTrialName
));
203 // Check that adding [non-expired, expired] activates the non-expired one.
204 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName
));
206 base::FieldTrialList
field_trial_list(NULL
);
207 study1
->clear_expiry_date();
208 study2
->set_expiry_date(TimeToProtoTime(year_ago
));
209 seed_processor
.CreateTrialsFromSeed(seed
, "en-CA", base::Time::Now(),
210 version
, Study_Channel_STABLE
,
211 Study_FormFactor_DESKTOP
, "");
212 EXPECT_EQ(kGroup1Name
, base::FieldTrialList::FindFullName(kTrialName
));
216 TEST_F(VariationsSeedProcessorTest
, ValidateStudy
) {
218 study
.set_default_experiment_name("def");
219 AddExperiment("abc", 100, &study
);
220 Study_Experiment
* default_group
= AddExperiment("def", 200, &study
);
222 ProcessedStudy processed_study
;
223 EXPECT_TRUE(processed_study
.Init(&study
, false));
224 EXPECT_EQ(300, processed_study
.total_probability());
226 // Min version checks.
227 study
.mutable_filter()->set_min_version("1.2.3.*");
228 EXPECT_TRUE(processed_study
.Init(&study
, false));
229 study
.mutable_filter()->set_min_version("1.*.3");
230 EXPECT_FALSE(processed_study
.Init(&study
, false));
231 study
.mutable_filter()->set_min_version("1.2.3");
232 EXPECT_TRUE(processed_study
.Init(&study
, false));
234 // Max version checks.
235 study
.mutable_filter()->set_max_version("2.3.4.*");
236 EXPECT_TRUE(processed_study
.Init(&study
, false));
237 study
.mutable_filter()->set_max_version("*.3");
238 EXPECT_FALSE(processed_study
.Init(&study
, false));
239 study
.mutable_filter()->set_max_version("2.3.4");
240 EXPECT_TRUE(processed_study
.Init(&study
, false));
242 study
.clear_default_experiment_name();
243 EXPECT_FALSE(processed_study
.Init(&study
, false));
245 study
.set_default_experiment_name("xyz");
246 EXPECT_FALSE(processed_study
.Init(&study
, false));
248 study
.set_default_experiment_name("def");
249 default_group
->clear_name();
250 EXPECT_FALSE(processed_study
.Init(&study
, false));
252 default_group
->set_name("def");
253 EXPECT_TRUE(processed_study
.Init(&study
, false));
254 Study_Experiment
* repeated_group
= study
.add_experiment();
255 repeated_group
->set_name("abc");
256 repeated_group
->set_probability_weight(1);
257 EXPECT_FALSE(processed_study
.Init(&study
, false));
260 TEST_F(VariationsSeedProcessorTest
, VariationParams
) {
261 base::FieldTrialList
field_trial_list(NULL
);
264 study
.set_name("Study1");
265 study
.set_default_experiment_name("B");
267 Study_Experiment
* experiment1
= AddExperiment("A", 1, &study
);
268 Study_Experiment_Param
* param
= experiment1
->add_param();
269 param
->set_name("x");
270 param
->set_value("y");
272 Study_Experiment
* experiment2
= AddExperiment("B", 0, &study
);
274 EXPECT_TRUE(CreateTrialFromStudy(&study
));
275 EXPECT_EQ("y", GetVariationParamValue("Study1", "x"));
277 study
.set_name("Study2");
278 experiment1
->set_probability_weight(0);
279 experiment2
->set_probability_weight(1);
280 EXPECT_TRUE(CreateTrialFromStudy(&study
));
281 EXPECT_EQ(std::string(), GetVariationParamValue("Study2", "x"));
284 TEST_F(VariationsSeedProcessorTest
, VariationParamsWithForcingFlag
) {
285 Study study
= CreateStudyWithFlagGroups(100, 0, 0);
286 ASSERT_EQ(kForcingFlag1
, study
.experiment(1).forcing_flag());
287 Study_Experiment_Param
* param
= study
.mutable_experiment(1)->add_param();
288 param
->set_name("x");
289 param
->set_value("y");
291 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1
);
292 base::FieldTrialList
field_trial_list(NULL
);
293 EXPECT_TRUE(CreateTrialFromStudy(&study
));
294 EXPECT_EQ(kFlagGroup1Name
, base::FieldTrialList::FindFullName(study
.name()));
295 EXPECT_EQ("y", GetVariationParamValue(study
.name(), "x"));
298 TEST_F(VariationsSeedProcessorTest
, StartsActive
) {
299 base::FieldTrialList
field_trial_list(NULL
);
302 Study
* study1
= seed
.add_study();
303 study1
->set_name("A");
304 study1
->set_default_experiment_name("Default");
305 AddExperiment("AA", 100, study1
);
306 AddExperiment("Default", 0, study1
);
308 Study
* study2
= seed
.add_study();
309 study2
->set_name("B");
310 study2
->set_default_experiment_name("Default");
311 AddExperiment("BB", 100, study2
);
312 AddExperiment("Default", 0, study2
);
313 study2
->set_activation_type(Study_ActivationType_ACTIVATION_AUTO
);
315 Study
* study3
= seed
.add_study();
316 study3
->set_name("C");
317 study3
->set_default_experiment_name("Default");
318 AddExperiment("CC", 100, study3
);
319 AddExperiment("Default", 0, study3
);
320 study3
->set_activation_type(Study_ActivationType_ACTIVATION_EXPLICIT
);
322 VariationsSeedProcessor seed_processor
;
323 seed_processor
.CreateTrialsFromSeed(seed
, "en-CA", base::Time::Now(),
324 base::Version("20.0.0.0"),
325 Study_Channel_STABLE
,
326 Study_FormFactor_DESKTOP
, "");
328 // Non-specified and ACTIVATION_EXPLICIT should not start active, but
329 // ACTIVATION_AUTO should.
330 EXPECT_FALSE(IsFieldTrialActive("A"));
331 EXPECT_TRUE(IsFieldTrialActive("B"));
332 EXPECT_FALSE(IsFieldTrialActive("C"));
334 EXPECT_EQ("AA", base::FieldTrialList::FindFullName("A"));
335 EXPECT_EQ("BB", base::FieldTrialList::FindFullName("B"));
336 EXPECT_EQ("CC", base::FieldTrialList::FindFullName("C"));
338 // Now, all studies should be active.
339 EXPECT_TRUE(IsFieldTrialActive("A"));
340 EXPECT_TRUE(IsFieldTrialActive("B"));
341 EXPECT_TRUE(IsFieldTrialActive("C"));
344 TEST_F(VariationsSeedProcessorTest
, StartsActiveWithFlag
) {
345 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1
);
347 base::FieldTrialList
field_trial_list(NULL
);
349 Study study
= CreateStudyWithFlagGroups(100, 0, 0);
350 study
.set_activation_type(Study_ActivationType_ACTIVATION_AUTO
);
352 EXPECT_TRUE(CreateTrialFromStudy(&study
));
353 EXPECT_TRUE(IsFieldTrialActive(kFlagStudyName
));
355 EXPECT_EQ(kFlagGroup1Name
,
356 base::FieldTrialList::FindFullName(kFlagStudyName
));
359 } // namespace chrome_variations