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