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/metrics/field_trial.h"
     11 #include "components/variations/processed_study.h"
     12 #include "components/variations/study_filtering.h"
     13 #include "components/variations/variations_associated_data.h"
     14 
     15 namespace chrome_variations {
     16 
     17 namespace {
     18 
     19 // Associates the variations params of |experiment|, if present.
     20 void RegisterExperimentParams(const Study& study,
     21                               const Study_Experiment& experiment) {
     22   std::map<std::string, std::string> params;
     23   for (int i = 0; i < experiment.param_size(); ++i) {
     24     if (experiment.param(i).has_name() && experiment.param(i).has_value())
     25       params[experiment.param(i).name()] = experiment.param(i).value();
     26   }
     27   if (!params.empty())
     28     AssociateVariationParams(study.name(), experiment.name(), params);
     29 }
     30 
     31 // If there are variation ids associated with |experiment|, register the
     32 // variation ids.
     33 void RegisterVariationIds(const Study_Experiment& experiment,
     34                           const std::string& trial_name) {
     35   if (experiment.has_google_web_experiment_id()) {
     36     const VariationID variation_id =
     37         static_cast<VariationID>(experiment.google_web_experiment_id());
     38     AssociateGoogleVariationIDForce(GOOGLE_WEB_PROPERTIES,
     39                                     trial_name,
     40                                     experiment.name(),
     41                                     variation_id);
     42   }
     43   if (experiment.has_google_web_trigger_experiment_id()) {
     44     const VariationID variation_id =
     45         static_cast<VariationID>(experiment.google_web_trigger_experiment_id());
     46     AssociateGoogleVariationIDForce(GOOGLE_WEB_PROPERTIES_TRIGGER,
     47                                     trial_name,
     48                                     experiment.name(),
     49                                     variation_id);
     50   }
     51   if (experiment.has_google_update_experiment_id()) {
     52     const VariationID variation_id =
     53         static_cast<VariationID>(experiment.google_update_experiment_id());
     54     AssociateGoogleVariationIDForce(GOOGLE_UPDATE_SERVICE,
     55                                     trial_name,
     56                                     experiment.name(),
     57                                     variation_id);
     58   }
     59 }
     60 
     61 }  // namespace
     62 
     63 VariationsSeedProcessor::VariationsSeedProcessor() {
     64 }
     65 
     66 VariationsSeedProcessor::~VariationsSeedProcessor() {
     67 }
     68 
     69 void VariationsSeedProcessor::CreateTrialsFromSeed(
     70     const VariationsSeed& seed,
     71     const std::string& locale,
     72     const base::Time& reference_date,
     73     const base::Version& version,
     74     Study_Channel channel,
     75     Study_FormFactor form_factor,
     76     const std::string& hardware_class) {
     77   std::vector<ProcessedStudy> filtered_studies;
     78   FilterAndValidateStudies(seed, locale, reference_date, version, channel,
     79                            form_factor, hardware_class, &filtered_studies);
     80 
     81   for (size_t i = 0; i < filtered_studies.size(); ++i)
     82     CreateTrialFromStudy(filtered_studies[i]);
     83 }
     84 
     85 void VariationsSeedProcessor::CreateTrialFromStudy(
     86     const ProcessedStudy& processed_study) {
     87   const Study& study = *processed_study.study();
     88 
     89   // Check if any experiments need to be forced due to a command line
     90   // flag. Force the first experiment with an existing flag.
     91   CommandLine* command_line = CommandLine::ForCurrentProcess();
     92   for (int i = 0; i < study.experiment_size(); ++i) {
     93     const Study_Experiment& experiment = study.experiment(i);
     94     if (experiment.has_forcing_flag() &&
     95         command_line->HasSwitch(experiment.forcing_flag())) {
     96       scoped_refptr<base::FieldTrial> trial(
     97           base::FieldTrialList::CreateFieldTrial(study.name(),
     98                                                  experiment.name()));
     99       RegisterExperimentParams(study, experiment);
    100       RegisterVariationIds(experiment, study.name());
    101       if (study.activation_type() == Study_ActivationType_ACTIVATION_AUTO)
    102         trial->group();
    103 
    104       DVLOG(1) << "Trial " << study.name() << " forced by flag: "
    105                << experiment.forcing_flag();
    106       return;
    107     }
    108   }
    109 
    110   uint32 randomization_seed = 0;
    111   base::FieldTrial::RandomizationType randomization_type =
    112       base::FieldTrial::SESSION_RANDOMIZED;
    113   if (study.has_consistency() &&
    114       study.consistency() == Study_Consistency_PERMANENT) {
    115     randomization_type = base::FieldTrial::ONE_TIME_RANDOMIZED;
    116     if (study.has_randomization_seed())
    117       randomization_seed = study.randomization_seed();
    118   }
    119 
    120   // The trial is created without specifying an expiration date because the
    121   // expiration check in field_trial.cc is based on the build date. Instead,
    122   // the expiration check using |reference_date| is done explicitly below.
    123   scoped_refptr<base::FieldTrial> trial(
    124       base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
    125           study.name(), processed_study.total_probability(),
    126           study.default_experiment_name(),
    127           base::FieldTrialList::kNoExpirationYear, 1, 1, randomization_type,
    128           randomization_seed, NULL));
    129 
    130   for (int i = 0; i < study.experiment_size(); ++i) {
    131     const Study_Experiment& experiment = study.experiment(i);
    132     RegisterExperimentParams(study, experiment);
    133 
    134     // Groups with forcing flags have probability 0 and will never be selected.
    135     // Therefore, there's no need to add them to the field trial.
    136     if (experiment.has_forcing_flag())
    137       continue;
    138 
    139     if (experiment.name() != study.default_experiment_name())
    140       trial->AppendGroup(experiment.name(), experiment.probability_weight());
    141 
    142     RegisterVariationIds(experiment, study.name());
    143   }
    144 
    145   trial->SetForced();
    146   if (processed_study.is_expired())
    147     trial->Disable();
    148   else if (study.activation_type() == Study_ActivationType_ACTIVATION_AUTO)
    149     trial->group();
    150 }
    151 
    152 }  // namespace chrome_variations
    153