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 "chrome/common/metrics/variations/variations_associated_data.h"
      6 
      7 #include <map>
      8 #include <vector>
      9 
     10 #include "base/memory/singleton.h"
     11 #include "chrome/common/metrics/metrics_util.h"
     12 #include "chrome/common/metrics/variations/variation_ids.h"
     13 
     14 namespace chrome_variations {
     15 
     16 namespace {
     17 
     18 // The internal singleton accessor for the map, used to keep it thread-safe.
     19 class GroupMapAccessor {
     20  public:
     21   typedef std::map<ActiveGroupId, VariationID, ActiveGroupIdCompare>
     22       GroupToIDMap;
     23 
     24   // Retrieve the singleton.
     25   static GroupMapAccessor* GetInstance() {
     26     return Singleton<GroupMapAccessor>::get();
     27   }
     28 
     29   // Note that this normally only sets the ID for a group the first time, unless
     30   // |force| is set to true, in which case it will always override it.
     31   void AssociateID(IDCollectionKey key,
     32                    const ActiveGroupId& group_identifier,
     33                    const VariationID id,
     34                    const bool force) {
     35 #if !defined(NDEBUG)
     36     // Validate that all collections with this |group_identifier| have the same
     37     // associated ID.
     38     DCHECK_EQ(2, ID_COLLECTION_COUNT);
     39     IDCollectionKey other_key = GOOGLE_WEB_PROPERTIES;
     40     if (key == GOOGLE_WEB_PROPERTIES)
     41       other_key = GOOGLE_UPDATE_SERVICE;
     42     VariationID other_id = GetID(other_key, group_identifier);
     43     DCHECK(other_id == EMPTY_ID || other_id == id);
     44 #endif
     45 
     46     base::AutoLock scoped_lock(lock_);
     47 
     48     GroupToIDMap* group_to_id_map = GetGroupToIDMap(key);
     49     if (force ||
     50         group_to_id_map->find(group_identifier) == group_to_id_map->end())
     51       (*group_to_id_map)[group_identifier] = id;
     52   }
     53 
     54   VariationID GetID(IDCollectionKey key,
     55                     const ActiveGroupId& group_identifier) {
     56     base::AutoLock scoped_lock(lock_);
     57     GroupToIDMap* group_to_id_map = GetGroupToIDMap(key);
     58     GroupToIDMap::const_iterator it = group_to_id_map->find(group_identifier);
     59     if (it == group_to_id_map->end())
     60       return EMPTY_ID;
     61     return it->second;
     62   }
     63 
     64   void ClearAllMapsForTesting() {
     65     base::AutoLock scoped_lock(lock_);
     66 
     67     for (int i = 0; i < ID_COLLECTION_COUNT; ++i) {
     68       GroupToIDMap* map = GetGroupToIDMap(static_cast<IDCollectionKey>(i));
     69       DCHECK(map);
     70       map->clear();
     71     }
     72   }
     73 
     74  private:
     75   friend struct DefaultSingletonTraits<GroupMapAccessor>;
     76 
     77   // Retrieves the GroupToIDMap for |key|.
     78   GroupToIDMap* GetGroupToIDMap(IDCollectionKey key) {
     79     return &group_to_id_maps_[key];
     80   }
     81 
     82   GroupMapAccessor() {
     83     group_to_id_maps_.resize(ID_COLLECTION_COUNT);
     84   }
     85   ~GroupMapAccessor() {}
     86 
     87   base::Lock lock_;
     88   std::vector<GroupToIDMap> group_to_id_maps_;
     89 
     90   DISALLOW_COPY_AND_ASSIGN(GroupMapAccessor);
     91 };
     92 
     93 // Singleton helper class that keeps track of the parameters of all variations
     94 // and ensures access to these is thread-safe.
     95 class VariationsParamAssociator {
     96  public:
     97   typedef std::pair<std::string, std::string> VariationKey;
     98   typedef std::map<std::string, std::string> VariationParams;
     99 
    100   // Retrieve the singleton.
    101   static VariationsParamAssociator* GetInstance() {
    102     return Singleton<VariationsParamAssociator>::get();
    103   }
    104 
    105   bool AssociateVariationParams(const std::string& trial_name,
    106                                 const std::string& group_name,
    107                                 const VariationParams& params) {
    108     base::AutoLock scoped_lock(lock_);
    109 
    110     if (IsFieldTrialActive(trial_name))
    111       return false;
    112 
    113     const VariationKey key(trial_name, group_name);
    114     if (ContainsKey(variation_params_, key))
    115       return false;
    116 
    117     variation_params_[key] = params;
    118     return true;
    119   }
    120 
    121   bool GetVariationParams(const std::string& trial_name,
    122                           VariationParams* params) {
    123     base::AutoLock scoped_lock(lock_);
    124 
    125     const std::string group_name =
    126         base::FieldTrialList::FindFullName(trial_name);
    127     const VariationKey key(trial_name, group_name);
    128     if (!ContainsKey(variation_params_, key))
    129       return false;
    130 
    131     *params = variation_params_[key];
    132     return true;
    133   }
    134 
    135   void ClearAllParamsForTesting() {
    136     base::AutoLock scoped_lock(lock_);
    137     variation_params_.clear();
    138   }
    139 
    140  private:
    141   friend struct DefaultSingletonTraits<VariationsParamAssociator>;
    142 
    143   VariationsParamAssociator() {}
    144   ~VariationsParamAssociator() {}
    145 
    146   // Tests whether a field trial is active (i.e. group() has been called on it).
    147   // TODO(asvitkine): Expose this as an API on base::FieldTrial.
    148   bool IsFieldTrialActive(const std::string& trial_name) {
    149     base::FieldTrial::ActiveGroups active_groups;
    150     base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
    151     for (size_t i = 0; i < active_groups.size(); ++i) {
    152       if (active_groups[i].trial_name == trial_name)
    153         return true;
    154     }
    155     return false;
    156   }
    157 
    158   base::Lock lock_;
    159   std::map<VariationKey, VariationParams> variation_params_;
    160 
    161   DISALLOW_COPY_AND_ASSIGN(VariationsParamAssociator);
    162 };
    163 
    164 }  // namespace
    165 
    166 ActiveGroupId MakeActiveGroupId(const std::string& trial_name,
    167                                 const std::string& group_name) {
    168   ActiveGroupId id;
    169   id.name = metrics::HashName(trial_name);
    170   id.group = metrics::HashName(group_name);
    171   return id;
    172 }
    173 
    174 void AssociateGoogleVariationID(IDCollectionKey key,
    175                                 const std::string& trial_name,
    176                                 const std::string& group_name,
    177                                 VariationID id) {
    178   GroupMapAccessor::GetInstance()->AssociateID(
    179       key, MakeActiveGroupId(trial_name, group_name), id, false);
    180 }
    181 
    182 void AssociateGoogleVariationIDForce(IDCollectionKey key,
    183                                      const std::string& trial_name,
    184                                      const std::string& group_name,
    185                                      VariationID id) {
    186   GroupMapAccessor::GetInstance()->AssociateID(
    187       key, MakeActiveGroupId(trial_name, group_name), id, true);
    188 }
    189 
    190 VariationID GetGoogleVariationID(IDCollectionKey key,
    191                                  const std::string& trial_name,
    192                                  const std::string& group_name) {
    193   return GroupMapAccessor::GetInstance()->GetID(
    194       key, MakeActiveGroupId(trial_name, group_name));
    195 }
    196 
    197 bool AssociateVariationParams(
    198     const std::string& trial_name,
    199     const std::string& group_name,
    200     const std::map<std::string, std::string>& params) {
    201   return VariationsParamAssociator::GetInstance()->AssociateVariationParams(
    202       trial_name, group_name, params);
    203 }
    204 
    205 bool GetVariationParams(const std::string& trial_name,
    206                         std::map<std::string, std::string>* params) {
    207   return VariationsParamAssociator::GetInstance()->GetVariationParams(
    208       trial_name, params);
    209 }
    210 
    211 std::string GetVariationParamValue(const std::string& trial_name,
    212                                    const std::string& param_name) {
    213   std::map<std::string, std::string> params;
    214   if (GetVariationParams(trial_name, &params)) {
    215     std::map<std::string, std::string>::iterator it = params.find(param_name);
    216     if (it != params.end())
    217       return it->second;
    218   }
    219   return std::string();
    220 }
    221 
    222 // Functions below are exposed for testing explicitly behind this namespace.
    223 // They simply wrap existing functions in this file.
    224 namespace testing {
    225 
    226 void ClearAllVariationIDs() {
    227   GroupMapAccessor::GetInstance()->ClearAllMapsForTesting();
    228 }
    229 
    230 void ClearAllVariationParams() {
    231   VariationsParamAssociator::GetInstance()->ClearAllParamsForTesting();
    232 }
    233 
    234 }  // namespace testing
    235 
    236 }  // namespace chrome_variations
    237