Breakdown ClientLoadedTime histograms into buckets based on number of clients. Fix...
[chromium-blink-merge.git] / components / variations / variations_seed_processor_unittest.cc
blobd1f76ccb00cb973628bd17f619644cfadc5cc725
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(seed,
232 "en-CA",
233 base::Time::Now(),
234 version,
235 Study_Channel_STABLE,
236 Study_FormFactor_DESKTOP,
238 override_callback_.callback());
239 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
242 // Check that adding [non-expired, expired] activates the non-expired one.
243 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
245 base::FieldTrialList field_trial_list(NULL);
246 study1->clear_expiry_date();
247 study2->set_expiry_date(TimeToProtoTime(year_ago));
248 seed_processor.CreateTrialsFromSeed(seed,
249 "en-CA",
250 base::Time::Now(),
251 version,
252 Study_Channel_STABLE,
253 Study_FormFactor_DESKTOP,
255 override_callback_.callback());
256 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
260 TEST_F(VariationsSeedProcessorTest, OverrideUIStrings) {
261 base::FieldTrialList field_trial_list(NULL);
263 Study study;
264 study.set_name("Study1");
265 study.set_default_experiment_name("B");
266 study.set_activation_type(Study_ActivationType_ACTIVATION_AUTO);
268 Study_Experiment* experiment1 = AddExperiment("A", 0, &study);
269 Study_Experiment_OverrideUIString* override =
270 experiment1->add_override_ui_string();
272 override->set_name_hash(1234);
273 override->set_value("test");
275 Study_Experiment* experiment2 = AddExperiment("B", 1, &study);
277 EXPECT_TRUE(CreateTrialFromStudy(&study));
279 const TestOverrideStringCallback::OverrideMap& overrides =
280 override_callback_.overrides();
282 EXPECT_TRUE(overrides.empty());
284 study.set_name("Study2");
285 experiment1->set_probability_weight(1);
286 experiment2->set_probability_weight(0);
288 EXPECT_TRUE(CreateTrialFromStudy(&study));
290 EXPECT_EQ(1u, overrides.size());
291 TestOverrideStringCallback::OverrideMap::const_iterator it =
292 overrides.find(1234);
293 EXPECT_EQ(base::ASCIIToUTF16("test"), it->second);
296 TEST_F(VariationsSeedProcessorTest, OverrideUIStringsWithForcingFlag) {
297 Study study = CreateStudyWithFlagGroups(100, 0, 0);
298 ASSERT_EQ(kForcingFlag1, study.experiment(1).forcing_flag());
300 study.set_activation_type(Study_ActivationType_ACTIVATION_AUTO);
301 Study_Experiment_OverrideUIString* override =
302 study.mutable_experiment(1)->add_override_ui_string();
303 override->set_name_hash(1234);
304 override->set_value("test");
306 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
307 base::FieldTrialList field_trial_list(NULL);
308 EXPECT_TRUE(CreateTrialFromStudy(&study));
309 EXPECT_EQ(kFlagGroup1Name, base::FieldTrialList::FindFullName(study.name()));
311 const TestOverrideStringCallback::OverrideMap& overrides =
312 override_callback_.overrides();
313 EXPECT_EQ(1u, overrides.size());
314 TestOverrideStringCallback::OverrideMap::const_iterator it =
315 overrides.find(1234);
316 EXPECT_EQ(base::ASCIIToUTF16("test"), it->second);
319 TEST_F(VariationsSeedProcessorTest, ValidateStudy) {
320 Study study;
321 study.set_default_experiment_name("def");
322 AddExperiment("abc", 100, &study);
323 Study_Experiment* default_group = AddExperiment("def", 200, &study);
325 ProcessedStudy processed_study;
326 EXPECT_TRUE(processed_study.Init(&study, false));
327 EXPECT_EQ(300, processed_study.total_probability());
329 // Min version checks.
330 study.mutable_filter()->set_min_version("1.2.3.*");
331 EXPECT_TRUE(processed_study.Init(&study, false));
332 study.mutable_filter()->set_min_version("1.*.3");
333 EXPECT_FALSE(processed_study.Init(&study, false));
334 study.mutable_filter()->set_min_version("1.2.3");
335 EXPECT_TRUE(processed_study.Init(&study, false));
337 // Max version checks.
338 study.mutable_filter()->set_max_version("2.3.4.*");
339 EXPECT_TRUE(processed_study.Init(&study, false));
340 study.mutable_filter()->set_max_version("*.3");
341 EXPECT_FALSE(processed_study.Init(&study, false));
342 study.mutable_filter()->set_max_version("2.3.4");
343 EXPECT_TRUE(processed_study.Init(&study, false));
345 study.clear_default_experiment_name();
346 EXPECT_FALSE(processed_study.Init(&study, false));
348 study.set_default_experiment_name("xyz");
349 EXPECT_FALSE(processed_study.Init(&study, false));
351 study.set_default_experiment_name("def");
352 default_group->clear_name();
353 EXPECT_FALSE(processed_study.Init(&study, false));
355 default_group->set_name("def");
356 EXPECT_TRUE(processed_study.Init(&study, false));
357 Study_Experiment* repeated_group = study.add_experiment();
358 repeated_group->set_name("abc");
359 repeated_group->set_probability_weight(1);
360 EXPECT_FALSE(processed_study.Init(&study, false));
363 TEST_F(VariationsSeedProcessorTest, VariationParams) {
364 base::FieldTrialList field_trial_list(NULL);
366 Study study;
367 study.set_name("Study1");
368 study.set_default_experiment_name("B");
370 Study_Experiment* experiment1 = AddExperiment("A", 1, &study);
371 Study_Experiment_Param* param = experiment1->add_param();
372 param->set_name("x");
373 param->set_value("y");
375 Study_Experiment* experiment2 = AddExperiment("B", 0, &study);
377 EXPECT_TRUE(CreateTrialFromStudy(&study));
378 EXPECT_EQ("y", GetVariationParamValue("Study1", "x"));
380 study.set_name("Study2");
381 experiment1->set_probability_weight(0);
382 experiment2->set_probability_weight(1);
383 EXPECT_TRUE(CreateTrialFromStudy(&study));
384 EXPECT_EQ(std::string(), GetVariationParamValue("Study2", "x"));
387 TEST_F(VariationsSeedProcessorTest, VariationParamsWithForcingFlag) {
388 Study study = CreateStudyWithFlagGroups(100, 0, 0);
389 ASSERT_EQ(kForcingFlag1, study.experiment(1).forcing_flag());
390 Study_Experiment_Param* param = study.mutable_experiment(1)->add_param();
391 param->set_name("x");
392 param->set_value("y");
394 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
395 base::FieldTrialList field_trial_list(NULL);
396 EXPECT_TRUE(CreateTrialFromStudy(&study));
397 EXPECT_EQ(kFlagGroup1Name, base::FieldTrialList::FindFullName(study.name()));
398 EXPECT_EQ("y", GetVariationParamValue(study.name(), "x"));
401 TEST_F(VariationsSeedProcessorTest, StartsActive) {
402 base::FieldTrialList field_trial_list(NULL);
404 VariationsSeed seed;
405 Study* study1 = seed.add_study();
406 study1->set_name("A");
407 study1->set_default_experiment_name("Default");
408 AddExperiment("AA", 100, study1);
409 AddExperiment("Default", 0, study1);
411 Study* study2 = seed.add_study();
412 study2->set_name("B");
413 study2->set_default_experiment_name("Default");
414 AddExperiment("BB", 100, study2);
415 AddExperiment("Default", 0, study2);
416 study2->set_activation_type(Study_ActivationType_ACTIVATION_AUTO);
418 Study* study3 = seed.add_study();
419 study3->set_name("C");
420 study3->set_default_experiment_name("Default");
421 AddExperiment("CC", 100, study3);
422 AddExperiment("Default", 0, study3);
423 study3->set_activation_type(Study_ActivationType_ACTIVATION_EXPLICIT);
425 VariationsSeedProcessor seed_processor;
426 seed_processor.CreateTrialsFromSeed(seed,
427 "en-CA",
428 base::Time::Now(),
429 base::Version("20.0.0.0"),
430 Study_Channel_STABLE,
431 Study_FormFactor_DESKTOP,
433 override_callback_.callback());
435 // Non-specified and ACTIVATION_EXPLICIT should not start active, but
436 // ACTIVATION_AUTO should.
437 EXPECT_FALSE(IsFieldTrialActive("A"));
438 EXPECT_TRUE(IsFieldTrialActive("B"));
439 EXPECT_FALSE(IsFieldTrialActive("C"));
441 EXPECT_EQ("AA", base::FieldTrialList::FindFullName("A"));
442 EXPECT_EQ("BB", base::FieldTrialList::FindFullName("B"));
443 EXPECT_EQ("CC", base::FieldTrialList::FindFullName("C"));
445 // Now, all studies should be active.
446 EXPECT_TRUE(IsFieldTrialActive("A"));
447 EXPECT_TRUE(IsFieldTrialActive("B"));
448 EXPECT_TRUE(IsFieldTrialActive("C"));
451 TEST_F(VariationsSeedProcessorTest, StartsActiveWithFlag) {
452 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
454 base::FieldTrialList field_trial_list(NULL);
456 Study study = CreateStudyWithFlagGroups(100, 0, 0);
457 study.set_activation_type(Study_ActivationType_ACTIVATION_AUTO);
459 EXPECT_TRUE(CreateTrialFromStudy(&study));
460 EXPECT_TRUE(IsFieldTrialActive(kFlagStudyName));
462 EXPECT_EQ(kFlagGroup1Name,
463 base::FieldTrialList::FindFullName(kFlagStudyName));
466 TEST_F(VariationsSeedProcessorTest, ForcingFlagAlreadyForced) {
467 Study study = CreateStudyWithFlagGroups(100, 0, 0);
468 ASSERT_EQ(kNonFlagGroupName, study.experiment(0).name());
469 Study_Experiment_Param* param = study.mutable_experiment(0)->add_param();
470 param->set_name("x");
471 param->set_value("y");
472 study.mutable_experiment(0)->set_google_web_experiment_id(kExperimentId);
474 base::FieldTrialList field_trial_list(NULL);
475 base::FieldTrialList::CreateFieldTrial(kFlagStudyName, kNonFlagGroupName);
477 base::CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
478 EXPECT_TRUE(CreateTrialFromStudy(&study));
479 // The previously forced experiment should still hold.
480 EXPECT_EQ(kNonFlagGroupName,
481 base::FieldTrialList::FindFullName(study.name()));
483 // Check that params and experiment ids correspond.
484 EXPECT_EQ("y", GetVariationParamValue(study.name(), "x"));
485 VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, kFlagStudyName,
486 kNonFlagGroupName);
487 EXPECT_EQ(kExperimentId, id);
490 } // namespace variations