Roll src/third_party/WebKit 605a979:06cb9e9 (svn 202556:202558)
[chromium-blink-merge.git] / components / variations / variations_seed_processor_unittest.cc
blob437c611fb39878a917a3879dea2189df7b38e69a
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"
7 #include <map>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "components/variations/processed_study.h"
15 #include "components/variations/variations_associated_data.h"
16 #include "testing/gtest/include/gtest/gtest.h"
18 namespace variations {
20 namespace {
22 // Converts |time| to Study proto format.
23 int64 TimeToProtoTime(const base::Time& time) {
24 return (time - base::Time::UnixEpoch()).InSeconds();
27 // Constants for testing associating command line flags with trial groups.
28 const char kFlagStudyName[] = "flag_test_trial";
29 const char kFlagGroup1Name[] = "flag_group1";
30 const char kFlagGroup2Name[] = "flag_group2";
31 const char kNonFlagGroupName[] = "non_flag_group";
32 const char kForcingFlag1[] = "flag_test1";
33 const char kForcingFlag2[] = "flag_test2";
35 const VariationID kExperimentId = 123;
37 // Adds an experiment to |study| with the specified |name| and |probability|.
38 Study_Experiment* AddExperiment(const std::string& name, int probability,
39 Study* study) {
40 Study_Experiment* experiment = study->add_experiment();
41 experiment->set_name(name);
42 experiment->set_probability_weight(probability);
43 return experiment;
46 // Populates |study| with test data used for testing associating command line
47 // flags with trials groups. The study will contain three groups, a default
48 // group that isn't associated with a flag, and two other groups, both
49 // associated with different flags.
50 Study CreateStudyWithFlagGroups(int default_group_probability,
51 int flag_group1_probability,
52 int flag_group2_probability) {
53 DCHECK_GE(default_group_probability, 0);
54 DCHECK_GE(flag_group1_probability, 0);
55 DCHECK_GE(flag_group2_probability, 0);
56 Study study;
57 study.set_name(kFlagStudyName);
58 study.set_default_experiment_name(kNonFlagGroupName);
60 AddExperiment(kNonFlagGroupName, default_group_probability, &study);
61 AddExperiment(kFlagGroup1Name, flag_group1_probability, &study)
62 ->set_forcing_flag(kForcingFlag1);
63 AddExperiment(kFlagGroup2Name, flag_group2_probability, &study)
64 ->set_forcing_flag(kForcingFlag2);
66 return study;
69 // Tests whether a field trial is active (i.e. group() has been called on it).
70 bool IsFieldTrialActive(const std::string& trial_name) {
71 base::FieldTrial::ActiveGroups active_groups;
72 base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
73 for (size_t i = 0; i < active_groups.size(); ++i) {
74 if (active_groups[i].trial_name == trial_name)
75 return true;
77 return false;
80 class TestOverrideStringCallback {
81 public:
82 typedef std::map<uint32_t, base::string16> OverrideMap;
84 TestOverrideStringCallback()
85 : callback_(base::Bind(&TestOverrideStringCallback::Override,
86 base::Unretained(this))) {}
88 virtual ~TestOverrideStringCallback() {}
90 const VariationsSeedProcessor::UIStringOverrideCallback& callback() const {
91 return callback_;
94 const OverrideMap& overrides() const { return overrides_; }
96 private:
97 void Override(uint32_t hash, const base::string16& string) {
98 overrides_[hash] = string;
101 VariationsSeedProcessor::UIStringOverrideCallback callback_;
102 OverrideMap overrides_;
104 DISALLOW_COPY_AND_ASSIGN(TestOverrideStringCallback);
107 } // namespace
109 class VariationsSeedProcessorTest : public ::testing::Test {
110 public:
111 VariationsSeedProcessorTest() {
114 ~VariationsSeedProcessorTest() override {
115 // Ensure that the maps are cleared between tests, since they are stored as
116 // process singletons.
117 testing::ClearAllVariationIDs();
118 testing::ClearAllVariationParams();
121 bool CreateTrialFromStudy(const Study* study) {
122 ProcessedStudy processed_study;
123 if (processed_study.Init(study, false)) {
124 VariationsSeedProcessor().CreateTrialFromStudy(
125 processed_study, override_callback_.callback());
126 return true;
128 return false;
131 protected:
132 TestOverrideStringCallback override_callback_;
134 private:
135 DISALLOW_COPY_AND_ASSIGN(VariationsSeedProcessorTest);
138 TEST_F(VariationsSeedProcessorTest, AllowForceGroupAndVariationId) {
139 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
141 base::FieldTrialList field_trial_list(NULL);
143 Study study = CreateStudyWithFlagGroups(100, 0, 0);
144 study.mutable_experiment(1)->set_google_web_experiment_id(kExperimentId);
146 EXPECT_TRUE(CreateTrialFromStudy(&study));
147 EXPECT_EQ(kFlagGroup1Name,
148 base::FieldTrialList::FindFullName(kFlagStudyName));
150 VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, kFlagStudyName,
151 kFlagGroup1Name);
152 EXPECT_EQ(kExperimentId, id);
155 // Test that the group for kForcingFlag1 is forced.
156 TEST_F(VariationsSeedProcessorTest, ForceGroupWithFlag1) {
157 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
159 base::FieldTrialList field_trial_list(NULL);
161 Study study = CreateStudyWithFlagGroups(100, 0, 0);
162 EXPECT_TRUE(CreateTrialFromStudy(&study));
163 EXPECT_EQ(kFlagGroup1Name,
164 base::FieldTrialList::FindFullName(kFlagStudyName));
167 // Test that the group for kForcingFlag2 is forced.
168 TEST_F(VariationsSeedProcessorTest, ForceGroupWithFlag2) {
169 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
171 base::FieldTrialList field_trial_list(NULL);
173 Study study = CreateStudyWithFlagGroups(100, 0, 0);
174 EXPECT_TRUE(CreateTrialFromStudy(&study));
175 EXPECT_EQ(kFlagGroup2Name,
176 base::FieldTrialList::FindFullName(kFlagStudyName));
179 TEST_F(VariationsSeedProcessorTest, ForceGroup_ChooseFirstGroupWithFlag) {
180 // Add the flag to the command line arguments so the flag group is forced.
181 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
182 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
184 base::FieldTrialList field_trial_list(NULL);
186 Study study = CreateStudyWithFlagGroups(100, 0, 0);
187 EXPECT_TRUE(CreateTrialFromStudy(&study));
188 EXPECT_EQ(kFlagGroup1Name,
189 base::FieldTrialList::FindFullName(kFlagStudyName));
192 TEST_F(VariationsSeedProcessorTest, ForceGroup_DontChooseGroupWithFlag) {
193 base::FieldTrialList field_trial_list(NULL);
195 // The two flag groups are given high probability, which would normally make
196 // them very likely to be chosen. They won't be chosen since flag groups are
197 // never chosen when their flag isn't present.
198 Study study = CreateStudyWithFlagGroups(1, 999, 999);
199 EXPECT_TRUE(CreateTrialFromStudy(&study));
200 EXPECT_EQ(kNonFlagGroupName,
201 base::FieldTrialList::FindFullName(kFlagStudyName));
204 TEST_F(VariationsSeedProcessorTest,
205 NonExpiredStudyPrioritizedOverExpiredStudy) {
206 VariationsSeedProcessor seed_processor;
208 const std::string kTrialName = "A";
209 const std::string kGroup1Name = "Group1";
211 VariationsSeed seed;
212 Study* study1 = seed.add_study();
213 study1->set_name(kTrialName);
214 study1->set_default_experiment_name("Default");
215 AddExperiment(kGroup1Name, 100, study1);
216 AddExperiment("Default", 0, study1);
217 Study* study2 = seed.add_study();
218 *study2 = *study1;
219 ASSERT_EQ(seed.study(0).name(), seed.study(1).name());
221 const base::Time year_ago =
222 base::Time::Now() - base::TimeDelta::FromDays(365);
224 const base::Version version("20.0.0.0");
226 // Check that adding [expired, non-expired] activates the non-expired one.
227 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
229 base::FieldTrialList field_trial_list(NULL);
230 study1->set_expiry_date(TimeToProtoTime(year_ago));
231 seed_processor.CreateTrialsFromSeed(
232 seed, "en-CA", base::Time::Now(), version, Study_Channel_STABLE,
233 Study_FormFactor_DESKTOP, "", "", "", override_callback_.callback());
234 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
237 // Check that adding [non-expired, expired] activates the non-expired one.
238 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
240 base::FieldTrialList field_trial_list(NULL);
241 study1->clear_expiry_date();
242 study2->set_expiry_date(TimeToProtoTime(year_ago));
243 seed_processor.CreateTrialsFromSeed(
244 seed, "en-CA", base::Time::Now(), version, Study_Channel_STABLE,
245 Study_FormFactor_DESKTOP, "", "", "", override_callback_.callback());
246 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
250 TEST_F(VariationsSeedProcessorTest, OverrideUIStrings) {
251 base::FieldTrialList field_trial_list(NULL);
253 Study study;
254 study.set_name("Study1");
255 study.set_default_experiment_name("B");
256 study.set_activation_type(Study_ActivationType_ACTIVATION_AUTO);
258 Study_Experiment* experiment1 = AddExperiment("A", 0, &study);
259 Study_Experiment_OverrideUIString* override =
260 experiment1->add_override_ui_string();
262 override->set_name_hash(1234);
263 override->set_value("test");
265 Study_Experiment* experiment2 = AddExperiment("B", 1, &study);
267 EXPECT_TRUE(CreateTrialFromStudy(&study));
269 const TestOverrideStringCallback::OverrideMap& overrides =
270 override_callback_.overrides();
272 EXPECT_TRUE(overrides.empty());
274 study.set_name("Study2");
275 experiment1->set_probability_weight(1);
276 experiment2->set_probability_weight(0);
278 EXPECT_TRUE(CreateTrialFromStudy(&study));
280 EXPECT_EQ(1u, overrides.size());
281 TestOverrideStringCallback::OverrideMap::const_iterator it =
282 overrides.find(1234);
283 EXPECT_EQ(base::ASCIIToUTF16("test"), it->second);
286 TEST_F(VariationsSeedProcessorTest, OverrideUIStringsWithForcingFlag) {
287 Study study = CreateStudyWithFlagGroups(100, 0, 0);
288 ASSERT_EQ(kForcingFlag1, study.experiment(1).forcing_flag());
290 study.set_activation_type(Study_ActivationType_ACTIVATION_AUTO);
291 Study_Experiment_OverrideUIString* override =
292 study.mutable_experiment(1)->add_override_ui_string();
293 override->set_name_hash(1234);
294 override->set_value("test");
296 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
297 base::FieldTrialList field_trial_list(NULL);
298 EXPECT_TRUE(CreateTrialFromStudy(&study));
299 EXPECT_EQ(kFlagGroup1Name, base::FieldTrialList::FindFullName(study.name()));
301 const TestOverrideStringCallback::OverrideMap& overrides =
302 override_callback_.overrides();
303 EXPECT_EQ(1u, overrides.size());
304 TestOverrideStringCallback::OverrideMap::const_iterator it =
305 overrides.find(1234);
306 EXPECT_EQ(base::ASCIIToUTF16("test"), it->second);
309 TEST_F(VariationsSeedProcessorTest, ValidateStudy) {
310 Study study;
311 study.set_default_experiment_name("def");
312 AddExperiment("abc", 100, &study);
313 Study_Experiment* default_group = AddExperiment("def", 200, &study);
315 ProcessedStudy processed_study;
316 EXPECT_TRUE(processed_study.Init(&study, false));
317 EXPECT_EQ(300, processed_study.total_probability());
318 EXPECT_FALSE(processed_study.all_assignments_to_one_group());
320 // Min version checks.
321 study.mutable_filter()->set_min_version("1.2.3.*");
322 EXPECT_TRUE(processed_study.Init(&study, false));
323 study.mutable_filter()->set_min_version("1.*.3");
324 EXPECT_FALSE(processed_study.Init(&study, false));
325 study.mutable_filter()->set_min_version("1.2.3");
326 EXPECT_TRUE(processed_study.Init(&study, false));
328 // Max version checks.
329 study.mutable_filter()->set_max_version("2.3.4.*");
330 EXPECT_TRUE(processed_study.Init(&study, false));
331 study.mutable_filter()->set_max_version("*.3");
332 EXPECT_FALSE(processed_study.Init(&study, false));
333 study.mutable_filter()->set_max_version("2.3.4");
334 EXPECT_TRUE(processed_study.Init(&study, false));
336 study.clear_default_experiment_name();
337 EXPECT_FALSE(processed_study.Init(&study, false));
339 study.set_default_experiment_name("xyz");
340 EXPECT_FALSE(processed_study.Init(&study, false));
342 study.set_default_experiment_name("def");
343 default_group->clear_name();
344 EXPECT_FALSE(processed_study.Init(&study, false));
346 default_group->set_name("def");
347 EXPECT_TRUE(processed_study.Init(&study, false));
348 Study_Experiment* repeated_group = study.add_experiment();
349 repeated_group->set_name("abc");
350 repeated_group->set_probability_weight(1);
351 EXPECT_FALSE(processed_study.Init(&study, false));
354 TEST_F(VariationsSeedProcessorTest, ProcessedStudyAllAssignmentsToOneGroup) {
355 Study study;
356 study.set_default_experiment_name("def");
357 AddExperiment("def", 100, &study);
359 ProcessedStudy processed_study;
360 EXPECT_TRUE(processed_study.Init(&study, false));
361 EXPECT_TRUE(processed_study.all_assignments_to_one_group());
363 AddExperiment("abc", 0, &study);
364 AddExperiment("flag", 0, &study)->set_forcing_flag(kForcingFlag1);
365 EXPECT_TRUE(processed_study.Init(&study, false));
366 EXPECT_TRUE(processed_study.all_assignments_to_one_group());
368 AddExperiment("xyz", 1, &study);
369 EXPECT_TRUE(processed_study.Init(&study, false));
370 EXPECT_FALSE(processed_study.all_assignments_to_one_group());
372 // Try with default group and first group being at 0.
373 Study study2;
374 study2.set_default_experiment_name("def");
375 AddExperiment("def", 0, &study2);
376 AddExperiment("xyz", 34, &study2);
377 EXPECT_TRUE(processed_study.Init(&study2, false));
378 EXPECT_TRUE(processed_study.all_assignments_to_one_group());
379 AddExperiment("abc", 12, &study2);
380 EXPECT_TRUE(processed_study.Init(&study2, false));
381 EXPECT_FALSE(processed_study.all_assignments_to_one_group());
384 TEST_F(VariationsSeedProcessorTest, VariationParams) {
385 base::FieldTrialList field_trial_list(NULL);
387 Study study;
388 study.set_name("Study1");
389 study.set_default_experiment_name("B");
391 Study_Experiment* experiment1 = AddExperiment("A", 1, &study);
392 Study_Experiment_Param* param = experiment1->add_param();
393 param->set_name("x");
394 param->set_value("y");
396 Study_Experiment* experiment2 = AddExperiment("B", 0, &study);
398 EXPECT_TRUE(CreateTrialFromStudy(&study));
399 EXPECT_EQ("y", GetVariationParamValue("Study1", "x"));
401 study.set_name("Study2");
402 experiment1->set_probability_weight(0);
403 experiment2->set_probability_weight(1);
404 EXPECT_TRUE(CreateTrialFromStudy(&study));
405 EXPECT_EQ(std::string(), GetVariationParamValue("Study2", "x"));
408 TEST_F(VariationsSeedProcessorTest, VariationParamsWithForcingFlag) {
409 Study study = CreateStudyWithFlagGroups(100, 0, 0);
410 ASSERT_EQ(kForcingFlag1, study.experiment(1).forcing_flag());
411 Study_Experiment_Param* param = study.mutable_experiment(1)->add_param();
412 param->set_name("x");
413 param->set_value("y");
415 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
416 base::FieldTrialList field_trial_list(NULL);
417 EXPECT_TRUE(CreateTrialFromStudy(&study));
418 EXPECT_EQ(kFlagGroup1Name, base::FieldTrialList::FindFullName(study.name()));
419 EXPECT_EQ("y", GetVariationParamValue(study.name(), "x"));
422 TEST_F(VariationsSeedProcessorTest, StartsActive) {
423 base::FieldTrialList field_trial_list(NULL);
425 VariationsSeed seed;
426 Study* study1 = seed.add_study();
427 study1->set_name("A");
428 study1->set_default_experiment_name("Default");
429 AddExperiment("AA", 100, study1);
430 AddExperiment("Default", 0, study1);
432 Study* study2 = seed.add_study();
433 study2->set_name("B");
434 study2->set_default_experiment_name("Default");
435 AddExperiment("BB", 100, study2);
436 AddExperiment("Default", 0, study2);
437 study2->set_activation_type(Study_ActivationType_ACTIVATION_AUTO);
439 Study* study3 = seed.add_study();
440 study3->set_name("C");
441 study3->set_default_experiment_name("Default");
442 AddExperiment("CC", 100, study3);
443 AddExperiment("Default", 0, study3);
444 study3->set_activation_type(Study_ActivationType_ACTIVATION_EXPLICIT);
446 VariationsSeedProcessor seed_processor;
447 seed_processor.CreateTrialsFromSeed(
448 seed, "en-CA", base::Time::Now(), base::Version("20.0.0.0"),
449 Study_Channel_STABLE, Study_FormFactor_DESKTOP, "", "", "",
450 override_callback_.callback());
452 // Non-specified and ACTIVATION_EXPLICIT should not start active, but
453 // ACTIVATION_AUTO should.
454 EXPECT_FALSE(IsFieldTrialActive("A"));
455 EXPECT_TRUE(IsFieldTrialActive("B"));
456 EXPECT_FALSE(IsFieldTrialActive("C"));
458 EXPECT_EQ("AA", base::FieldTrialList::FindFullName("A"));
459 EXPECT_EQ("BB", base::FieldTrialList::FindFullName("B"));
460 EXPECT_EQ("CC", base::FieldTrialList::FindFullName("C"));
462 // Now, all studies should be active.
463 EXPECT_TRUE(IsFieldTrialActive("A"));
464 EXPECT_TRUE(IsFieldTrialActive("B"));
465 EXPECT_TRUE(IsFieldTrialActive("C"));
468 TEST_F(VariationsSeedProcessorTest, StartsActiveWithFlag) {
469 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
471 base::FieldTrialList field_trial_list(NULL);
473 Study study = CreateStudyWithFlagGroups(100, 0, 0);
474 study.set_activation_type(Study_ActivationType_ACTIVATION_AUTO);
476 EXPECT_TRUE(CreateTrialFromStudy(&study));
477 EXPECT_TRUE(IsFieldTrialActive(kFlagStudyName));
479 EXPECT_EQ(kFlagGroup1Name,
480 base::FieldTrialList::FindFullName(kFlagStudyName));
483 TEST_F(VariationsSeedProcessorTest, ForcingFlagAlreadyForced) {
484 Study study = CreateStudyWithFlagGroups(100, 0, 0);
485 ASSERT_EQ(kNonFlagGroupName, study.experiment(0).name());
486 Study_Experiment_Param* param = study.mutable_experiment(0)->add_param();
487 param->set_name("x");
488 param->set_value("y");
489 study.mutable_experiment(0)->set_google_web_experiment_id(kExperimentId);
491 base::FieldTrialList field_trial_list(NULL);
492 base::FieldTrialList::CreateFieldTrial(kFlagStudyName, kNonFlagGroupName);
494 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
495 EXPECT_TRUE(CreateTrialFromStudy(&study));
496 // The previously forced experiment should still hold.
497 EXPECT_EQ(kNonFlagGroupName,
498 base::FieldTrialList::FindFullName(study.name()));
500 // Check that params and experiment ids correspond.
501 EXPECT_EQ("y", GetVariationParamValue(study.name(), "x"));
502 VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, kFlagStudyName,
503 kNonFlagGroupName);
504 EXPECT_EQ(kExperimentId, id);
507 } // namespace variations