• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "components/variations/variations_seed_processor.h"
6 
7 #include <vector>
8 
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"
14 
15 namespace chrome_variations {
16 
17 namespace {
18 
19 // Converts |time| to Study proto format.
TimeToProtoTime(const base::Time & time)20 int64 TimeToProtoTime(const base::Time& time) {
21   return (time - base::Time::UnixEpoch()).InSeconds();
22 }
23 
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";
31 
32 const VariationID kExperimentId = 123;
33 
34 // Adds an experiment to |study| with the specified |name| and |probability|.
AddExperiment(const std::string & name,int probability,Study * study)35 Study_Experiment* AddExperiment(const std::string& name, int probability,
36                                 Study* study) {
37   Study_Experiment* experiment = study->add_experiment();
38   experiment->set_name(name);
39   experiment->set_probability_weight(probability);
40   return experiment;
41 }
42 
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.
CreateStudyWithFlagGroups(int default_group_probability,int flag_group1_probability,int flag_group2_probability)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);
53   Study study;
54   study.set_name(kFlagStudyName);
55   study.set_default_experiment_name(kNonFlagGroupName);
56 
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);
62 
63   return study;
64 }
65 
66 // Tests whether a field trial is active (i.e. group() has been called on it).
IsFieldTrialActive(const std::string & trial_name)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)
72       return true;
73   }
74   return false;
75 }
76 
77 }  // namespace
78 
79 class VariationsSeedProcessorTest : public ::testing::Test {
80  public:
VariationsSeedProcessorTest()81   VariationsSeedProcessorTest() {
82   }
83 
~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();
89   }
90 
CreateTrialFromStudy(const Study * study)91   bool CreateTrialFromStudy(const Study* study) {
92     ProcessedStudy processed_study;
93     if (processed_study.Init(study, false)) {
94       VariationsSeedProcessor().CreateTrialFromStudy(processed_study);
95       return true;
96     }
97     return false;
98   }
99 
100  private:
101   DISALLOW_COPY_AND_ASSIGN(VariationsSeedProcessorTest);
102 };
103 
TEST_F(VariationsSeedProcessorTest,AllowForceGroupAndVariationId)104 TEST_F(VariationsSeedProcessorTest, AllowForceGroupAndVariationId) {
105   CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
106 
107   base::FieldTrialList field_trial_list(NULL);
108 
109   Study study = CreateStudyWithFlagGroups(100, 0, 0);
110   study.mutable_experiment(1)->set_google_web_experiment_id(kExperimentId);
111   study.mutable_filter()->add_channel(Study_Channel_DEV);
112   study.mutable_filter()->add_channel(Study_Channel_CANARY);
113   study.mutable_filter()->add_platform(Study_Platform_PLATFORM_ANDROID);
114 
115   EXPECT_TRUE(CreateTrialFromStudy(&study));
116   EXPECT_EQ(kFlagGroup1Name,
117             base::FieldTrialList::FindFullName(kFlagStudyName));
118 
119   VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, kFlagStudyName,
120                                         kFlagGroup1Name);
121   EXPECT_EQ(kExperimentId, id);
122 }
123 
TEST_F(VariationsSeedProcessorTest,AllowVariationIdWithForcingFlag)124 TEST_F(VariationsSeedProcessorTest, AllowVariationIdWithForcingFlag) {
125   VariationsSeedProcessor seed_processor;
126   Study study = CreateStudyWithFlagGroups(100, 0, 0);
127   EXPECT_FALSE(seed_processor.AllowVariationIdWithForcingFlag(study));
128 
129   study.mutable_filter()->add_channel(Study_Channel_DEV);
130   EXPECT_FALSE(seed_processor.AllowVariationIdWithForcingFlag(study));
131 
132   study.mutable_filter()->add_platform(Study_Platform_PLATFORM_ANDROID);
133   EXPECT_TRUE(seed_processor.AllowVariationIdWithForcingFlag(study));
134 
135   study.mutable_filter()->add_channel(Study_Channel_CANARY);
136   study.mutable_filter()->add_platform(Study_Platform_PLATFORM_IOS);
137   EXPECT_TRUE(seed_processor.AllowVariationIdWithForcingFlag(study));
138 
139   study.mutable_filter()->add_platform(Study_Platform_PLATFORM_WINDOWS);
140   EXPECT_FALSE(seed_processor.AllowVariationIdWithForcingFlag(study));
141 }
142 
TEST_F(VariationsSeedProcessorTest,CheckStudyChannel)143 TEST_F(VariationsSeedProcessorTest, CheckStudyChannel) {
144   VariationsSeedProcessor seed_processor;
145 
146   const Study_Channel channels[] = {
147     Study_Channel_CANARY,
148     Study_Channel_DEV,
149     Study_Channel_BETA,
150     Study_Channel_STABLE,
151   };
152   bool channel_added[arraysize(channels)] = { 0 };
153 
154   Study_Filter filter;
155 
156   // Check in the forwarded order. The loop cond is <= arraysize(channels)
157   // instead of < so that the result of adding the last channel gets checked.
158   for (size_t i = 0; i <= arraysize(channels); ++i) {
159     for (size_t j = 0; j < arraysize(channels); ++j) {
160       const bool expected = channel_added[j] || filter.channel_size() == 0;
161       const bool result = seed_processor.CheckStudyChannel(filter, channels[j]);
162       EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
163     }
164 
165     if (i < arraysize(channels)) {
166       filter.add_channel(channels[i]);
167       channel_added[i] = true;
168     }
169   }
170 
171   // Do the same check in the reverse order.
172   filter.clear_channel();
173   memset(&channel_added, 0, sizeof(channel_added));
174   for (size_t i = 0; i <= arraysize(channels); ++i) {
175     for (size_t j = 0; j < arraysize(channels); ++j) {
176       const bool expected = channel_added[j] || filter.channel_size() == 0;
177       const bool result = seed_processor.CheckStudyChannel(filter, channels[j]);
178       EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
179     }
180 
181     if (i < arraysize(channels)) {
182       const int index = arraysize(channels) - i - 1;
183       filter.add_channel(channels[index]);
184       channel_added[index] = true;
185     }
186   }
187 }
188 
TEST_F(VariationsSeedProcessorTest,CheckStudyFormFactor)189 TEST_F(VariationsSeedProcessorTest, CheckStudyFormFactor) {
190   VariationsSeedProcessor seed_processor;
191 
192   const Study_FormFactor form_factors[] = {
193     Study_FormFactor_DESKTOP,
194     Study_FormFactor_PHONE,
195     Study_FormFactor_TABLET,
196   };
197 
198   ASSERT_EQ(Study_FormFactor_FormFactor_ARRAYSIZE,
199             static_cast<int>(arraysize(form_factors)));
200 
201   bool form_factor_added[arraysize(form_factors)] = { 0 };
202   Study_Filter filter;
203 
204   for (size_t i = 0; i <= arraysize(form_factors); ++i) {
205     for (size_t j = 0; j < arraysize(form_factors); ++j) {
206       const bool expected = form_factor_added[j] ||
207                             filter.form_factor_size() == 0;
208       const bool result = seed_processor.CheckStudyFormFactor(filter,
209                                                               form_factors[j]);
210       EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
211     }
212 
213     if (i < arraysize(form_factors)) {
214       filter.add_form_factor(form_factors[i]);
215       form_factor_added[i] = true;
216     }
217   }
218 
219   // Do the same check in the reverse order.
220   filter.clear_form_factor();
221   memset(&form_factor_added, 0, sizeof(form_factor_added));
222   for (size_t i = 0; i <= arraysize(form_factors); ++i) {
223     for (size_t j = 0; j < arraysize(form_factors); ++j) {
224       const bool expected = form_factor_added[j] ||
225                             filter.form_factor_size() == 0;
226       const bool result = seed_processor.CheckStudyFormFactor(filter,
227                                                               form_factors[j]);
228       EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
229     }
230 
231     if (i < arraysize(form_factors)) {
232       const int index = arraysize(form_factors) - i - 1;;
233       filter.add_form_factor(form_factors[index]);
234       form_factor_added[index] = true;
235     }
236   }
237 }
238 
TEST_F(VariationsSeedProcessorTest,CheckStudyLocale)239 TEST_F(VariationsSeedProcessorTest, CheckStudyLocale) {
240   VariationsSeedProcessor seed_processor;
241 
242   struct {
243     const char* filter_locales;
244     bool en_us_result;
245     bool en_ca_result;
246     bool fr_result;
247   } test_cases[] = {
248     {"en-US", true, false, false},
249     {"en-US,en-CA,fr", true, true, true},
250     {"en-US,en-CA,en-GB", true, true, false},
251     {"en-GB,en-CA,en-US", true, true, false},
252     {"ja,kr,vi", false, false, false},
253     {"fr-CA", false, false, false},
254     {"", true, true, true},
255   };
256 
257   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
258     std::vector<std::string> filter_locales;
259     Study_Filter filter;
260     base::SplitString(test_cases[i].filter_locales, ',', &filter_locales);
261     for (size_t j = 0; j < filter_locales.size(); ++j)
262       filter.add_locale(filter_locales[j]);
263     EXPECT_EQ(test_cases[i].en_us_result,
264               seed_processor.CheckStudyLocale(filter, "en-US"));
265     EXPECT_EQ(test_cases[i].en_ca_result,
266               seed_processor.CheckStudyLocale(filter, "en-CA"));
267     EXPECT_EQ(test_cases[i].fr_result,
268               seed_processor.CheckStudyLocale(filter, "fr"));
269   }
270 }
271 
TEST_F(VariationsSeedProcessorTest,CheckStudyPlatform)272 TEST_F(VariationsSeedProcessorTest, CheckStudyPlatform) {
273   VariationsSeedProcessor seed_processor;
274 
275   const Study_Platform platforms[] = {
276     Study_Platform_PLATFORM_WINDOWS,
277     Study_Platform_PLATFORM_MAC,
278     Study_Platform_PLATFORM_LINUX,
279     Study_Platform_PLATFORM_CHROMEOS,
280     Study_Platform_PLATFORM_ANDROID,
281     Study_Platform_PLATFORM_IOS,
282   };
283   ASSERT_EQ(Study_Platform_Platform_ARRAYSIZE,
284             static_cast<int>(arraysize(platforms)));
285   bool platform_added[arraysize(platforms)] = { 0 };
286 
287   Study_Filter filter;
288 
289   // Check in the forwarded order. The loop cond is <= arraysize(platforms)
290   // instead of < so that the result of adding the last channel gets checked.
291   for (size_t i = 0; i <= arraysize(platforms); ++i) {
292     for (size_t j = 0; j < arraysize(platforms); ++j) {
293       const bool expected = platform_added[j] || filter.platform_size() == 0;
294       const bool result = seed_processor.CheckStudyPlatform(filter,
295                                                             platforms[j]);
296       EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
297     }
298 
299     if (i < arraysize(platforms)) {
300       filter.add_platform(platforms[i]);
301       platform_added[i] = true;
302     }
303   }
304 
305   // Do the same check in the reverse order.
306   filter.clear_platform();
307   memset(&platform_added, 0, sizeof(platform_added));
308   for (size_t i = 0; i <= arraysize(platforms); ++i) {
309     for (size_t j = 0; j < arraysize(platforms); ++j) {
310       const bool expected = platform_added[j] || filter.platform_size() == 0;
311       const bool result = seed_processor.CheckStudyPlatform(filter,
312                                                             platforms[j]);
313       EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
314     }
315 
316     if (i < arraysize(platforms)) {
317       const int index = arraysize(platforms) - i - 1;
318       filter.add_platform(platforms[index]);
319       platform_added[index] = true;
320     }
321   }
322 }
323 
TEST_F(VariationsSeedProcessorTest,CheckStudyStartDate)324 TEST_F(VariationsSeedProcessorTest, CheckStudyStartDate) {
325   VariationsSeedProcessor seed_processor;
326 
327   const base::Time now = base::Time::Now();
328   const base::TimeDelta delta = base::TimeDelta::FromHours(1);
329   const struct {
330     const base::Time start_date;
331     bool expected_result;
332   } start_test_cases[] = {
333     { now - delta, true },
334     { now, true },
335     { now + delta, false },
336   };
337 
338   Study_Filter filter;
339 
340   // Start date not set should result in true.
341   EXPECT_TRUE(seed_processor.CheckStudyStartDate(filter, now));
342 
343   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(start_test_cases); ++i) {
344     filter.set_start_date(TimeToProtoTime(start_test_cases[i].start_date));
345     const bool result = seed_processor.CheckStudyStartDate(filter, now);
346     EXPECT_EQ(start_test_cases[i].expected_result, result)
347         << "Case " << i << " failed!";
348   }
349 }
350 
TEST_F(VariationsSeedProcessorTest,CheckStudyVersion)351 TEST_F(VariationsSeedProcessorTest, CheckStudyVersion) {
352   VariationsSeedProcessor seed_processor;
353 
354   const struct {
355     const char* min_version;
356     const char* version;
357     bool expected_result;
358   } min_test_cases[] = {
359     { "1.2.2", "1.2.3", true },
360     { "1.2.3", "1.2.3", true },
361     { "1.2.4", "1.2.3", false },
362     { "1.3.2", "1.2.3", false },
363     { "2.1.2", "1.2.3", false },
364     { "0.3.4", "1.2.3", true },
365     // Wildcards.
366     { "1.*", "1.2.3", true },
367     { "1.2.*", "1.2.3", true },
368     { "1.2.3.*", "1.2.3", true },
369     { "1.2.4.*", "1.2.3", false },
370     { "2.*", "1.2.3", false },
371     { "0.3.*", "1.2.3", true },
372   };
373 
374   const struct {
375     const char* max_version;
376     const char* version;
377     bool expected_result;
378   } max_test_cases[] = {
379     { "1.2.2", "1.2.3", false },
380     { "1.2.3", "1.2.3", true },
381     { "1.2.4", "1.2.3", true },
382     { "2.1.1", "1.2.3", true },
383     { "2.1.1", "2.3.4", false },
384     // Wildcards
385     { "2.1.*", "2.3.4", false },
386     { "2.*", "2.3.4", true },
387     { "2.3.*", "2.3.4", true },
388     { "2.3.4.*", "2.3.4", true },
389     { "2.3.4.0.*", "2.3.4", true },
390     { "2.4.*", "2.3.4", true },
391     { "1.3.*", "2.3.4", false },
392     { "1.*", "2.3.4", false },
393   };
394 
395   Study_Filter filter;
396 
397   // Min/max version not set should result in true.
398   EXPECT_TRUE(seed_processor.CheckStudyVersion(filter, base::Version("1.2.3")));
399 
400   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(min_test_cases); ++i) {
401     filter.set_min_version(min_test_cases[i].min_version);
402     const bool result =
403         seed_processor.CheckStudyVersion(filter,
404                                          Version(min_test_cases[i].version));
405     EXPECT_EQ(min_test_cases[i].expected_result, result) <<
406         "Min. version case " << i << " failed!";
407   }
408   filter.clear_min_version();
409 
410   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(max_test_cases); ++i) {
411     filter.set_max_version(max_test_cases[i].max_version);
412     const bool result =
413         seed_processor.CheckStudyVersion(filter,
414                                          Version(max_test_cases[i].version));
415     EXPECT_EQ(max_test_cases[i].expected_result, result) <<
416         "Max version case " << i << " failed!";
417   }
418 
419   // Check intersection semantics.
420   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(min_test_cases); ++i) {
421     for (size_t j = 0; j < ARRAYSIZE_UNSAFE(max_test_cases); ++j) {
422       filter.set_min_version(min_test_cases[i].min_version);
423       filter.set_max_version(max_test_cases[j].max_version);
424 
425       if (!min_test_cases[i].expected_result) {
426         const bool result =
427             seed_processor.CheckStudyVersion(
428                 filter, Version(min_test_cases[i].version));
429         EXPECT_FALSE(result) << "Case " << i << "," << j << " failed!";
430       }
431 
432       if (!max_test_cases[j].expected_result) {
433         const bool result =
434             seed_processor.CheckStudyVersion(
435                 filter, Version(max_test_cases[j].version));
436         EXPECT_FALSE(result) << "Case " << i << "," << j << " failed!";
437       }
438     }
439   }
440 }
441 
TEST_F(VariationsSeedProcessorTest,FilterAndValidateStudies)442 TEST_F(VariationsSeedProcessorTest, FilterAndValidateStudies) {
443   const std::string kTrial1Name = "A";
444   const std::string kGroup1Name = "Group1";
445   const std::string kTrial3Name = "B";
446 
447   VariationsSeed seed;
448   Study* study1 = seed.add_study();
449   study1->set_name(kTrial1Name);
450   study1->set_default_experiment_name("Default");
451   AddExperiment(kGroup1Name, 100, study1);
452   AddExperiment("Default", 0, study1);
453 
454   Study* study2 = seed.add_study();
455   *study2 = *study1;
456   study2->mutable_experiment(0)->set_name("Bam");
457   ASSERT_EQ(seed.study(0).name(), seed.study(1).name());
458 
459   Study* study3 = seed.add_study();
460   study3->set_name(kTrial3Name);
461   study3->set_default_experiment_name("Default");
462   AddExperiment("A", 10, study3);
463   AddExperiment("Default", 25, study3);
464 
465   std::vector<ProcessedStudy> processed_studies;
466   VariationsSeedProcessor().FilterAndValidateStudies(
467       seed, "en-CA", base::Time::Now(), base::Version("20.0.0.0"),
468       Study_Channel_STABLE, Study_FormFactor_DESKTOP, &processed_studies);
469 
470   // Check that only the first kTrial1Name study was kept.
471   ASSERT_EQ(2U, processed_studies.size());
472   EXPECT_EQ(kTrial1Name, processed_studies[0].study()->name());
473   EXPECT_EQ(kGroup1Name, processed_studies[0].study()->experiment(0).name());
474   EXPECT_EQ(kTrial3Name, processed_studies[1].study()->name());
475 }
476 
TEST_F(VariationsSeedProcessorTest,ForbidForceGroupWithVariationId)477 TEST_F(VariationsSeedProcessorTest, ForbidForceGroupWithVariationId) {
478   CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
479 
480   base::FieldTrialList field_trial_list(NULL);
481 
482   Study study = CreateStudyWithFlagGroups(100, 0, 0);
483   study.mutable_experiment(1)->set_google_web_experiment_id(kExperimentId);
484   // Adding windows platform makes forcing_flag and variation Id incompatible.
485   study.mutable_filter()->add_platform(Study_Platform_PLATFORM_WINDOWS);
486 
487   EXPECT_TRUE(CreateTrialFromStudy(&study));
488   EXPECT_EQ(kFlagGroup1Name,
489             base::FieldTrialList::FindFullName(kFlagStudyName));
490   VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, kFlagStudyName,
491                                         kFlagGroup1Name);
492   EXPECT_EQ(EMPTY_ID, id);
493 }
494 
495 // Test that the group for kForcingFlag1 is forced.
TEST_F(VariationsSeedProcessorTest,ForceGroupWithFlag1)496 TEST_F(VariationsSeedProcessorTest, ForceGroupWithFlag1) {
497   CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
498 
499   base::FieldTrialList field_trial_list(NULL);
500 
501   Study study = CreateStudyWithFlagGroups(100, 0, 0);
502   EXPECT_TRUE(CreateTrialFromStudy(&study));
503   EXPECT_EQ(kFlagGroup1Name,
504             base::FieldTrialList::FindFullName(kFlagStudyName));
505 }
506 
507 // Test that the group for kForcingFlag2 is forced.
TEST_F(VariationsSeedProcessorTest,ForceGroupWithFlag2)508 TEST_F(VariationsSeedProcessorTest, ForceGroupWithFlag2) {
509   CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
510 
511   base::FieldTrialList field_trial_list(NULL);
512 
513   Study study = CreateStudyWithFlagGroups(100, 0, 0);
514   EXPECT_TRUE(CreateTrialFromStudy(&study));
515   EXPECT_EQ(kFlagGroup2Name,
516             base::FieldTrialList::FindFullName(kFlagStudyName));
517 }
518 
TEST_F(VariationsSeedProcessorTest,ForceGroup_ChooseFirstGroupWithFlag)519 TEST_F(VariationsSeedProcessorTest, ForceGroup_ChooseFirstGroupWithFlag) {
520   // Add the flag to the command line arguments so the flag group is forced.
521   CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
522   CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
523 
524   base::FieldTrialList field_trial_list(NULL);
525 
526   Study study = CreateStudyWithFlagGroups(100, 0, 0);
527   EXPECT_TRUE(CreateTrialFromStudy(&study));
528   EXPECT_EQ(kFlagGroup1Name,
529             base::FieldTrialList::FindFullName(kFlagStudyName));
530 }
531 
TEST_F(VariationsSeedProcessorTest,ForceGroup_DontChooseGroupWithFlag)532 TEST_F(VariationsSeedProcessorTest, ForceGroup_DontChooseGroupWithFlag) {
533   base::FieldTrialList field_trial_list(NULL);
534 
535   // The two flag groups are given high probability, which would normally make
536   // them very likely to be chosen. They won't be chosen since flag groups are
537   // never chosen when their flag isn't present.
538   Study study = CreateStudyWithFlagGroups(1, 999, 999);
539   EXPECT_TRUE(CreateTrialFromStudy(&study));
540   EXPECT_EQ(kNonFlagGroupName,
541             base::FieldTrialList::FindFullName(kFlagStudyName));
542 }
543 
TEST_F(VariationsSeedProcessorTest,IsStudyExpired)544 TEST_F(VariationsSeedProcessorTest, IsStudyExpired) {
545   VariationsSeedProcessor seed_processor;
546 
547   const base::Time now = base::Time::Now();
548   const base::TimeDelta delta = base::TimeDelta::FromHours(1);
549   const struct {
550     const base::Time expiry_date;
551     bool expected_result;
552   } expiry_test_cases[] = {
553     { now - delta, true },
554     { now, true },
555     { now + delta, false },
556   };
557 
558   Study study;
559 
560   // Expiry date not set should result in false.
561   EXPECT_FALSE(seed_processor.IsStudyExpired(study, now));
562 
563   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(expiry_test_cases); ++i) {
564     study.set_expiry_date(TimeToProtoTime(expiry_test_cases[i].expiry_date));
565     const bool result = seed_processor.IsStudyExpired(study, now);
566     EXPECT_EQ(expiry_test_cases[i].expected_result, result)
567         << "Case " << i << " failed!";
568   }
569 }
570 
TEST_F(VariationsSeedProcessorTest,NonExpiredStudyPrioritizedOverExpiredStudy)571 TEST_F(VariationsSeedProcessorTest,
572        NonExpiredStudyPrioritizedOverExpiredStudy) {
573   VariationsSeedProcessor seed_processor;
574 
575   const std::string kTrialName = "A";
576   const std::string kGroup1Name = "Group1";
577 
578   VariationsSeed seed;
579   Study* study1 = seed.add_study();
580   study1->set_name(kTrialName);
581   study1->set_default_experiment_name("Default");
582   AddExperiment(kGroup1Name, 100, study1);
583   AddExperiment("Default", 0, study1);
584   Study* study2 = seed.add_study();
585   *study2 = *study1;
586   ASSERT_EQ(seed.study(0).name(), seed.study(1).name());
587 
588   const base::Time year_ago =
589       base::Time::Now() - base::TimeDelta::FromDays(365);
590 
591   const base::Version version("20.0.0.0");
592 
593   // Check that adding [expired, non-expired] activates the non-expired one.
594   ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
595   {
596     base::FieldTrialList field_trial_list(NULL);
597     study1->set_expiry_date(TimeToProtoTime(year_ago));
598     seed_processor.CreateTrialsFromSeed(seed, "en-CA", base::Time::Now(),
599                                         version, Study_Channel_STABLE,
600                                         Study_FormFactor_DESKTOP);
601     EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
602   }
603 
604   // Check that adding [non-expired, expired] activates the non-expired one.
605   ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
606   {
607     base::FieldTrialList field_trial_list(NULL);
608     study1->clear_expiry_date();
609     study2->set_expiry_date(TimeToProtoTime(year_ago));
610     seed_processor.CreateTrialsFromSeed(seed, "en-CA", base::Time::Now(),
611                                         version, Study_Channel_STABLE,
612                                         Study_FormFactor_DESKTOP);
613     EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
614   }
615 }
616 
TEST_F(VariationsSeedProcessorTest,ValidateStudy)617 TEST_F(VariationsSeedProcessorTest, ValidateStudy) {
618   Study study;
619   study.set_default_experiment_name("def");
620   AddExperiment("abc", 100, &study);
621   Study_Experiment* default_group = AddExperiment("def", 200, &study);
622 
623   ProcessedStudy processed_study;
624   EXPECT_TRUE(processed_study.Init(&study, false));
625   EXPECT_EQ(300, processed_study.total_probability());
626 
627   // Min version checks.
628   study.mutable_filter()->set_min_version("1.2.3.*");
629   EXPECT_TRUE(processed_study.Init(&study, false));
630   study.mutable_filter()->set_min_version("1.*.3");
631   EXPECT_FALSE(processed_study.Init(&study, false));
632   study.mutable_filter()->set_min_version("1.2.3");
633   EXPECT_TRUE(processed_study.Init(&study, false));
634 
635   // Max version checks.
636   study.mutable_filter()->set_max_version("2.3.4.*");
637   EXPECT_TRUE(processed_study.Init(&study, false));
638   study.mutable_filter()->set_max_version("*.3");
639   EXPECT_FALSE(processed_study.Init(&study, false));
640   study.mutable_filter()->set_max_version("2.3.4");
641   EXPECT_TRUE(processed_study.Init(&study, false));
642 
643   study.clear_default_experiment_name();
644   EXPECT_FALSE(processed_study.Init(&study, false));
645 
646   study.set_default_experiment_name("xyz");
647   EXPECT_FALSE(processed_study.Init(&study, false));
648 
649   study.set_default_experiment_name("def");
650   default_group->clear_name();
651   EXPECT_FALSE(processed_study.Init(&study, false));
652 
653   default_group->set_name("def");
654   EXPECT_TRUE(processed_study.Init(&study, false));
655   Study_Experiment* repeated_group = study.add_experiment();
656   repeated_group->set_name("abc");
657   repeated_group->set_probability_weight(1);
658   EXPECT_FALSE(processed_study.Init(&study, false));
659 }
660 
TEST_F(VariationsSeedProcessorTest,VariationParams)661 TEST_F(VariationsSeedProcessorTest, VariationParams) {
662   base::FieldTrialList field_trial_list(NULL);
663 
664   Study study;
665   study.set_name("Study1");
666   study.set_default_experiment_name("B");
667 
668   Study_Experiment* experiment1 = AddExperiment("A", 1, &study);
669   Study_Experiment_Param* param = experiment1->add_param();
670   param->set_name("x");
671   param->set_value("y");
672 
673   Study_Experiment* experiment2 = AddExperiment("B", 0, &study);
674 
675   EXPECT_TRUE(CreateTrialFromStudy(&study));
676   EXPECT_EQ("y", GetVariationParamValue("Study1", "x"));
677 
678   study.set_name("Study2");
679   experiment1->set_probability_weight(0);
680   experiment2->set_probability_weight(1);
681   EXPECT_TRUE(CreateTrialFromStudy(&study));
682   EXPECT_EQ(std::string(), GetVariationParamValue("Study2", "x"));
683 }
684 
TEST_F(VariationsSeedProcessorTest,VariationParamsWithForcingFlag)685 TEST_F(VariationsSeedProcessorTest, VariationParamsWithForcingFlag) {
686   Study study = CreateStudyWithFlagGroups(100, 0, 0);
687   ASSERT_EQ(kForcingFlag1, study.experiment(1).forcing_flag());
688   Study_Experiment_Param* param = study.mutable_experiment(1)->add_param();
689   param->set_name("x");
690   param->set_value("y");
691 
692   CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
693   base::FieldTrialList field_trial_list(NULL);
694   EXPECT_TRUE(CreateTrialFromStudy(&study));
695   EXPECT_EQ(kFlagGroup1Name, base::FieldTrialList::FindFullName(study.name()));
696   EXPECT_EQ("y", GetVariationParamValue(study.name(), "x"));
697 }
698 
TEST_F(VariationsSeedProcessorTest,StartsActive)699 TEST_F(VariationsSeedProcessorTest, StartsActive) {
700   base::FieldTrialList field_trial_list(NULL);
701 
702   VariationsSeed seed;
703   Study* study1 = seed.add_study();
704   study1->set_name("A");
705   study1->set_default_experiment_name("Default");
706   AddExperiment("AA", 100, study1);
707   AddExperiment("Default", 0, study1);
708 
709   Study* study2 = seed.add_study();
710   study2->set_name("B");
711   study2->set_default_experiment_name("Default");
712   AddExperiment("BB", 100, study2);
713   AddExperiment("Default", 0, study2);
714   study2->set_activation_type(Study_ActivationType_ACTIVATION_AUTO);
715 
716   Study* study3 = seed.add_study();
717   study3->set_name("C");
718   study3->set_default_experiment_name("Default");
719   AddExperiment("CC", 100, study3);
720   AddExperiment("Default", 0, study3);
721   study3->set_activation_type(Study_ActivationType_ACTIVATION_EXPLICIT);
722 
723   VariationsSeedProcessor seed_processor;
724   seed_processor.CreateTrialsFromSeed(seed, "en-CA", base::Time::Now(),
725                                       base::Version("20.0.0.0"),
726                                       Study_Channel_STABLE,
727                                       Study_FormFactor_DESKTOP);
728 
729   // Non-specified and ACTIVATION_EXPLICIT should not start active, but
730   // ACTIVATION_AUTO should.
731   EXPECT_FALSE(IsFieldTrialActive("A"));
732   EXPECT_TRUE(IsFieldTrialActive("B"));
733   EXPECT_FALSE(IsFieldTrialActive("C"));
734 
735   EXPECT_EQ("AA", base::FieldTrialList::FindFullName("A"));
736   EXPECT_EQ("BB", base::FieldTrialList::FindFullName("B"));
737   EXPECT_EQ("CC", base::FieldTrialList::FindFullName("C"));
738 
739   // Now, all studies should be active.
740   EXPECT_TRUE(IsFieldTrialActive("A"));
741   EXPECT_TRUE(IsFieldTrialActive("B"));
742   EXPECT_TRUE(IsFieldTrialActive("C"));
743 }
744 
745 }  // namespace chrome_variations
746