Home | History | Annotate | Download | only in variations
      1 // Copyright (c) 2012 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 "chrome/common/metrics/variations/variations_util.h"
      6 
      7 #include <set>
      8 #include <string>
      9 
     10 #include "base/metrics/field_trial.h"
     11 #include "base/strings/string_split.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "base/time/time.h"
     14 #include "chrome/common/metrics/metrics_util.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 
     17 namespace chrome_variations {
     18 
     19 namespace {
     20 
     21 // Tests whether a field trial is active (i.e. group() has been called on it).
     22 bool IsFieldTrialActive(const std::string& trial_name) {
     23   base::FieldTrial::ActiveGroups active_groups;
     24   base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
     25   for (size_t i = 0; i < active_groups.size(); ++i) {
     26     if (active_groups[i].trial_name == trial_name)
     27       return true;
     28   }
     29   return false;
     30 }
     31 
     32 // Call FieldTrialList::FactoryGetFieldTrial() with a future expiry date.
     33 scoped_refptr<base::FieldTrial> CreateFieldTrial(
     34     const std::string& trial_name,
     35     int total_probability,
     36     const std::string& default_group_name,
     37     int* default_group_number) {
     38   return base::FieldTrialList::FactoryGetFieldTrial(
     39       trial_name, total_probability, default_group_name,
     40       base::FieldTrialList::kNoExpirationYear, 1, 1,
     41       base::FieldTrial::SESSION_RANDOMIZED, default_group_number);
     42 }
     43 
     44 }  // namespace
     45 
     46 class VariationsUtilTest : public ::testing::Test {
     47  public:
     48   VariationsUtilTest() : field_trial_list_(NULL) {
     49   }
     50 
     51   virtual ~VariationsUtilTest() {
     52     // Ensure that the maps are cleared between tests, since they are stored as
     53     // process singletons.
     54     testing::ClearAllVariationIDs();
     55   }
     56 
     57  private:
     58   base::FieldTrialList field_trial_list_;
     59 
     60   DISALLOW_COPY_AND_ASSIGN(VariationsUtilTest);
     61 };
     62 
     63 TEST_F(VariationsUtilTest, GetFieldTrialActiveGroups) {
     64   typedef std::set<ActiveGroupId, ActiveGroupIdCompare> ActiveGroupIdSet;
     65   std::string trial_one("trial one");
     66   std::string group_one("group one");
     67   std::string trial_two("trial two");
     68   std::string group_two("group two");
     69 
     70   base::FieldTrial::ActiveGroups active_groups;
     71   base::FieldTrial::ActiveGroup active_group;
     72   active_group.trial_name = trial_one;
     73   active_group.group_name = group_one;
     74   active_groups.push_back(active_group);
     75 
     76   active_group.trial_name = trial_two;
     77   active_group.group_name = group_two;
     78   active_groups.push_back(active_group);
     79 
     80   // Create our expected groups of IDs.
     81   ActiveGroupIdSet expected_groups;
     82   ActiveGroupId name_group_id;
     83   name_group_id.name = metrics::HashName(trial_one);
     84   name_group_id.group = metrics::HashName(group_one);
     85   expected_groups.insert(name_group_id);
     86   name_group_id.name = metrics::HashName(trial_two);
     87   name_group_id.group = metrics::HashName(group_two);
     88   expected_groups.insert(name_group_id);
     89 
     90   std::vector<ActiveGroupId> active_group_ids;
     91   testing::TestGetFieldTrialActiveGroupIds(active_groups, &active_group_ids);
     92   EXPECT_EQ(2U, active_group_ids.size());
     93   for (size_t i = 0; i < active_group_ids.size(); ++i) {
     94     ActiveGroupIdSet::iterator expected_group =
     95         expected_groups.find(active_group_ids[i]);
     96     EXPECT_FALSE(expected_group == expected_groups.end());
     97     expected_groups.erase(expected_group);
     98   }
     99   EXPECT_EQ(0U, expected_groups.size());
    100 }
    101 
    102 TEST_F(VariationsUtilTest, GenerateExperimentChunks) {
    103   const char* kExperimentStrings[] = {
    104       "1d3048f1-9de009d0",
    105       "cd73da34-cf196cb",
    106       "6214fa18-9e6dc24d",
    107       "4dcb0cd6-d31c4ca1",
    108       "9d5bce6-30d7d8ac",
    109   };
    110   const char* kExpectedChunks1[] = {
    111       "1d3048f1-9de009d0",
    112   };
    113   const char* kExpectedChunks2[] = {
    114       "1d3048f1-9de009d0,cd73da34-cf196cb",
    115   };
    116   const char* kExpectedChunks3[] = {
    117       "1d3048f1-9de009d0,cd73da34-cf196cb,6214fa18-9e6dc24d",
    118   };
    119   const char* kExpectedChunks4[] = {
    120       "1d3048f1-9de009d0,cd73da34-cf196cb,6214fa18-9e6dc24d",
    121       "4dcb0cd6-d31c4ca1",
    122   };
    123   const char* kExpectedChunks5[] = {
    124       "1d3048f1-9de009d0,cd73da34-cf196cb,6214fa18-9e6dc24d",
    125       "4dcb0cd6-d31c4ca1,9d5bce6-30d7d8ac",
    126   };
    127 
    128   struct {
    129     size_t strings_length;
    130     size_t expected_chunks_length;
    131     const char** expected_chunks;
    132   } cases[] = {
    133     { 0, 0, NULL },
    134     { 1, arraysize(kExpectedChunks1), kExpectedChunks1 },
    135     { 2, arraysize(kExpectedChunks2), kExpectedChunks2 },
    136     { 3, arraysize(kExpectedChunks3), kExpectedChunks3 },
    137     { 4, arraysize(kExpectedChunks4), kExpectedChunks4 },
    138     { 5, arraysize(kExpectedChunks5), kExpectedChunks5 },
    139   };
    140 
    141   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
    142     ASSERT_LE(cases[i].strings_length, arraysize(kExperimentStrings));
    143 
    144     std::vector<string16> experiments;
    145     for (size_t j = 0; j < cases[i].strings_length; ++j)
    146       experiments.push_back(UTF8ToUTF16(kExperimentStrings[j]));
    147 
    148     std::vector<string16> chunks;
    149     GenerateVariationChunks(experiments, &chunks);
    150     ASSERT_EQ(cases[i].expected_chunks_length, chunks.size());
    151     for (size_t j = 0; j < chunks.size(); ++j)
    152       EXPECT_EQ(UTF8ToUTF16(cases[i].expected_chunks[j]), chunks[j]);
    153   }
    154 }
    155 
    156 TEST_F(VariationsUtilTest, BuildGoogleUpdateExperimentLabel) {
    157   struct {
    158     const char* active_group_pairs;
    159     const char* expected_ids;
    160   } test_cases[] = {
    161     // Empty group.
    162     {"", ""},
    163     // Group of 1.
    164     {"FieldTrialA#Default", "3300200"},
    165     // Group of 1, doesn't have an associated ID.
    166     {"FieldTrialA#DoesNotExist", ""},
    167     // Group of 3.
    168     {"FieldTrialA#Default#FieldTrialB#Group1#FieldTrialC#Default",
    169      "3300200#3300201#3300202"},
    170     // Group of 3, one doesn't have an associated ID.
    171     {"FieldTrialA#Default#FieldTrialB#DoesNotExist#FieldTrialC#Default",
    172      "3300200#3300202"},
    173     // Group of 3, all three don't have an associated ID.
    174     {"FieldTrialX#Default#FieldTrialB#DoesNotExist#FieldTrialC#Default",
    175      "3300202"},
    176   };
    177 
    178   // Register a few VariationIDs.
    179   AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, "FieldTrialA", "Default",
    180                              TEST_VALUE_A);
    181   AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, "FieldTrialB", "Group1",
    182                              TEST_VALUE_B);
    183   AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, "FieldTrialC", "Default",
    184                              TEST_VALUE_C);
    185   AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, "FieldTrialD", "Default",
    186                              TEST_VALUE_D);  // Not actually used.
    187 
    188   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
    189     // Parse the input groups.
    190     base::FieldTrial::ActiveGroups groups;
    191     std::vector<std::string> group_data;
    192     base::SplitString(test_cases[i].active_group_pairs, '#', &group_data);
    193     ASSERT_EQ(0U, group_data.size() % 2);
    194     for (size_t j = 0; j < group_data.size(); j += 2) {
    195       base::FieldTrial::ActiveGroup group;
    196       group.trial_name = group_data[j];
    197       group.group_name = group_data[j + 1];
    198       groups.push_back(group);
    199     }
    200 
    201     // Parse the expected output.
    202     std::vector<std::string> expected_ids_list;
    203     base::SplitString(test_cases[i].expected_ids, '#', &expected_ids_list);
    204 
    205     std::string experiment_labels_string = UTF16ToUTF8(
    206         BuildGoogleUpdateExperimentLabel(groups));
    207 
    208     // Split the VariationIDs from the labels for verification below.
    209     std::vector<std::string> split_labels;
    210     std::set<std::string> parsed_ids;
    211     base::SplitString(experiment_labels_string, ';', &split_labels);
    212     for (std::vector<std::string>::const_iterator it = split_labels.begin();
    213          it != split_labels.end(); ++it) {
    214       // The ID is precisely between the '=' and '|' characters in each label.
    215       size_t index_of_equals = it->find('=');
    216       size_t index_of_pipe = it->find('|');
    217       ASSERT_NE(std::string::npos, index_of_equals);
    218       ASSERT_NE(std::string::npos, index_of_pipe);
    219       ASSERT_GT(index_of_pipe, index_of_equals);
    220       parsed_ids.insert(it->substr(index_of_equals + 1,
    221                                    index_of_pipe - index_of_equals - 1));
    222     }
    223 
    224     // Verify that the resulting string contains each of the expected labels,
    225     // and nothing more. Note that the date is stripped out and ignored.
    226     for (std::vector<std::string>::const_iterator it =
    227              expected_ids_list.begin(); it != expected_ids_list.end(); ++it) {
    228       std::set<std::string>::iterator it2 = parsed_ids.find(*it);
    229       EXPECT_TRUE(parsed_ids.end() != it2);
    230       parsed_ids.erase(it2);
    231     }
    232     EXPECT_TRUE(parsed_ids.empty());
    233   }  // for
    234 }
    235 
    236 TEST_F(VariationsUtilTest, CombineExperimentLabels) {
    237   struct {
    238     const char* variations_labels;
    239     const char* other_labels;
    240     const char* expected_label;
    241   } test_cases[] = {
    242     {"A=B|Tue, 21 Jan 2014 15:30:21 GMT",
    243      "C=D|Tue, 21 Jan 2014 15:30:21 GMT",
    244      "C=D|Tue, 21 Jan 2014 15:30:21 GMT;A=B|Tue, 21 Jan 2014 15:30:21 GMT"},
    245     {"A=B|Tue, 21 Jan 2014 15:30:21 GMT",
    246      "",
    247      "A=B|Tue, 21 Jan 2014 15:30:21 GMT"},
    248     {"",
    249      "A=B|Tue, 21 Jan 2014 15:30:21 GMT",
    250      "A=B|Tue, 21 Jan 2014 15:30:21 GMT"},
    251     {"A=B|Tue, 21 Jan 2014 15:30:21 GMT;C=D|Tue, 21 Jan 2014 15:30:21 GMT",
    252      "P=Q|Tue, 21 Jan 2014 15:30:21 GMT;X=Y|Tue, 21 Jan 2014 15:30:21 GMT",
    253      "P=Q|Tue, 21 Jan 2014 15:30:21 GMT;X=Y|Tue, 21 Jan 2014 15:30:21 GMT;"
    254      "A=B|Tue, 21 Jan 2014 15:30:21 GMT;C=D|Tue, 21 Jan 2014 15:30:21 GMT"},
    255     {"",
    256      "",
    257      ""},
    258   };
    259 
    260   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
    261     std::string result = UTF16ToUTF8(CombineExperimentLabels(
    262         ASCIIToUTF16(test_cases[i].variations_labels),
    263         ASCIIToUTF16(test_cases[i].other_labels)));
    264     EXPECT_EQ(test_cases[i].expected_label, result);
    265   }
    266 }
    267 
    268 TEST_F(VariationsUtilTest, ExtractNonVariationLabels) {
    269   struct {
    270     const char* input_label;
    271     const char* expected_output;
    272   } test_cases[] = {
    273     // Empty
    274     {"", ""},
    275     // One
    276     {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT",
    277      "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
    278     // Three
    279     {"CrVar1=123|Tue, 21 Jan 2014 15:30:21 GMT;"
    280      "experiment1=456|Tue, 21 Jan 2014 15:30:21 GMT;"
    281      "experiment2=789|Tue, 21 Jan 2014 15:30:21 GMT;"
    282      "CrVar1=123|Tue, 21 Jan 2014 15:30:21 GMT",
    283      "experiment1=456|Tue, 21 Jan 2014 15:30:21 GMT;"
    284      "experiment2=789|Tue, 21 Jan 2014 15:30:21 GMT"},
    285     // One and one Variation
    286     {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
    287      "CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT",
    288      "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
    289     // One and one Variation, flipped
    290     {"CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;"
    291      "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT",
    292      "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
    293     // Sandwiched
    294     {"CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;"
    295      "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
    296      "CrVar2=3310003|Tue, 21 Jan 2014 15:30:21 GMT;"
    297      "CrVar3=3310004|Tue, 21 Jan 2014 15:30:21 GMT",
    298      "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
    299     // Only Variations
    300     {"CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;"
    301      "CrVar2=3310003|Tue, 21 Jan 2014 15:30:21 GMT;"
    302      "CrVar3=3310004|Tue, 21 Jan 2014 15:30:21 GMT",
    303      ""},
    304     // Empty values
    305     {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
    306      "CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT",
    307      "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
    308     // Trailing semicolon
    309     {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
    310      "CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;",  // Note the semi here.
    311      "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
    312     // Semis
    313     {";;;;", ""},
    314   };
    315 
    316   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
    317     std::string non_variation_labels = UTF16ToUTF8(
    318         ExtractNonVariationLabels(ASCIIToUTF16(test_cases[i].input_label)));
    319     EXPECT_EQ(test_cases[i].expected_output, non_variation_labels);
    320   }
    321 }
    322 
    323 }  // namespace chrome_variations
    324