Home | History | Annotate | Download | only in variations
      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.
     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|.
     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.
     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).
     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:
     81   VariationsSeedProcessorTest() {
     82   }
     83 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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.
    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.
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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