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