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