• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "components/variations/metrics_util.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.
GetInstance()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.
AssociateID(IDCollectionKey key,const ActiveGroupId & group_identifier,const VariationID id,const bool force)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 
GetID(IDCollectionKey key,const ActiveGroupId & group_identifier)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 
ClearAllMapsForTesting()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|.
GetGroupToIDMap(IDCollectionKey key)78   GroupToIDMap* GetGroupToIDMap(IDCollectionKey key) {
79     return &group_to_id_maps_[key];
80   }
81 
GroupMapAccessor()82   GroupMapAccessor() {
83     group_to_id_maps_.resize(ID_COLLECTION_COUNT);
84   }
~GroupMapAccessor()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.
GetInstance()101   static VariationsParamAssociator* GetInstance() {
102     return Singleton<VariationsParamAssociator>::get();
103   }
104 
AssociateVariationParams(const std::string & trial_name,const std::string & group_name,const VariationParams & params)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 
GetVariationParams(const std::string & trial_name,VariationParams * params)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 
ClearAllParamsForTesting()135   void ClearAllParamsForTesting() {
136     base::AutoLock scoped_lock(lock_);
137     variation_params_.clear();
138   }
139 
140  private:
141   friend struct DefaultSingletonTraits<VariationsParamAssociator>;
142 
VariationsParamAssociator()143   VariationsParamAssociator() {}
~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.
IsFieldTrialActive(const std::string & trial_name)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 
MakeActiveGroupId(const std::string & trial_name,const std::string & group_name)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 
AssociateGoogleVariationID(IDCollectionKey key,const std::string & trial_name,const std::string & group_name,VariationID id)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 
AssociateGoogleVariationIDForce(IDCollectionKey key,const std::string & trial_name,const std::string & group_name,VariationID id)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 
GetGoogleVariationID(IDCollectionKey key,const std::string & trial_name,const std::string & group_name)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 
AssociateVariationParams(const std::string & trial_name,const std::string & group_name,const std::map<std::string,std::string> & params)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 
GetVariationParams(const std::string & trial_name,std::map<std::string,std::string> * params)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 
GetVariationParamValue(const std::string & trial_name,const std::string & param_name)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 
ClearAllVariationIDs()226 void ClearAllVariationIDs() {
227   GroupMapAccessor::GetInstance()->ClearAllMapsForTesting();
228 }
229 
ClearAllVariationParams()230 void ClearAllVariationParams() {
231   VariationsParamAssociator::GetInstance()->ClearAllParamsForTesting();
232 }
233 
234 }  // namespace testing
235 
236 }  // namespace chrome_variations
237