Index: components/variations/variations_associated_data.cc |
=================================================================== |
--- components/variations/variations_associated_data.cc (revision 0) |
+++ components/variations/variations_associated_data.cc (working copy) |
@@ -0,0 +1,236 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "components/variations/variations_associated_data.h" |
+ |
+#include <map> |
+#include <utility> |
+#include <vector> |
+ |
+#include "base/memory/singleton.h" |
+#include "components/variations/metrics_util.h" |
+ |
+namespace chrome_variations { |
+ |
+namespace { |
+ |
+// The internal singleton accessor for the map, used to keep it thread-safe. |
+class GroupMapAccessor { |
+ public: |
+ typedef std::map<ActiveGroupId, VariationID, ActiveGroupIdCompare> |
+ GroupToIDMap; |
+ |
+ // Retrieve the singleton. |
+ static GroupMapAccessor* GetInstance() { |
+ return Singleton<GroupMapAccessor>::get(); |
+ } |
+ |
+ // Note that this normally only sets the ID for a group the first time, unless |
+ // |force| is set to true, in which case it will always override it. |
+ void AssociateID(IDCollectionKey key, |
+ const ActiveGroupId& group_identifier, |
+ const VariationID id, |
+ const bool force) { |
+#if !defined(NDEBUG) |
+ // Validate that all collections with this |group_identifier| have the same |
+ // associated ID. |
+ DCHECK_EQ(2, ID_COLLECTION_COUNT); |
+ IDCollectionKey other_key = GOOGLE_WEB_PROPERTIES; |
+ if (key == GOOGLE_WEB_PROPERTIES) |
+ other_key = GOOGLE_UPDATE_SERVICE; |
+ VariationID other_id = GetID(other_key, group_identifier); |
+ DCHECK(other_id == EMPTY_ID || other_id == id); |
+#endif |
+ |
+ base::AutoLock scoped_lock(lock_); |
+ |
+ GroupToIDMap* group_to_id_map = GetGroupToIDMap(key); |
+ if (force || |
+ group_to_id_map->find(group_identifier) == group_to_id_map->end()) |
+ (*group_to_id_map)[group_identifier] = id; |
+ } |
+ |
+ VariationID GetID(IDCollectionKey key, |
+ const ActiveGroupId& group_identifier) { |
+ base::AutoLock scoped_lock(lock_); |
+ GroupToIDMap* group_to_id_map = GetGroupToIDMap(key); |
+ GroupToIDMap::const_iterator it = group_to_id_map->find(group_identifier); |
+ if (it == group_to_id_map->end()) |
+ return EMPTY_ID; |
+ return it->second; |
+ } |
+ |
+ void ClearAllMapsForTesting() { |
+ base::AutoLock scoped_lock(lock_); |
+ |
+ for (int i = 0; i < ID_COLLECTION_COUNT; ++i) { |
+ GroupToIDMap* map = GetGroupToIDMap(static_cast<IDCollectionKey>(i)); |
+ DCHECK(map); |
+ map->clear(); |
+ } |
+ } |
+ |
+ private: |
+ friend struct DefaultSingletonTraits<GroupMapAccessor>; |
+ |
+ // Retrieves the GroupToIDMap for |key|. |
+ GroupToIDMap* GetGroupToIDMap(IDCollectionKey key) { |
+ return &group_to_id_maps_[key]; |
+ } |
+ |
+ GroupMapAccessor() { |
+ group_to_id_maps_.resize(ID_COLLECTION_COUNT); |
+ } |
+ ~GroupMapAccessor() {} |
+ |
+ base::Lock lock_; |
+ std::vector<GroupToIDMap> group_to_id_maps_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(GroupMapAccessor); |
+}; |
+ |
+// Singleton helper class that keeps track of the parameters of all variations |
+// and ensures access to these is thread-safe. |
+class VariationsParamAssociator { |
+ public: |
+ typedef std::pair<std::string, std::string> VariationKey; |
+ typedef std::map<std::string, std::string> VariationParams; |
+ |
+ // Retrieve the singleton. |
+ static VariationsParamAssociator* GetInstance() { |
+ return Singleton<VariationsParamAssociator>::get(); |
+ } |
+ |
+ bool AssociateVariationParams(const std::string& trial_name, |
+ const std::string& group_name, |
+ const VariationParams& params) { |
+ base::AutoLock scoped_lock(lock_); |
+ |
+ if (IsFieldTrialActive(trial_name)) |
+ return false; |
+ |
+ const VariationKey key(trial_name, group_name); |
+ if (ContainsKey(variation_params_, key)) |
+ return false; |
+ |
+ variation_params_[key] = params; |
+ return true; |
+ } |
+ |
+ bool GetVariationParams(const std::string& trial_name, |
+ VariationParams* params) { |
+ base::AutoLock scoped_lock(lock_); |
+ |
+ const std::string group_name = |
+ base::FieldTrialList::FindFullName(trial_name); |
+ const VariationKey key(trial_name, group_name); |
+ if (!ContainsKey(variation_params_, key)) |
+ return false; |
+ |
+ *params = variation_params_[key]; |
+ return true; |
+ } |
+ |
+ void ClearAllParamsForTesting() { |
+ base::AutoLock scoped_lock(lock_); |
+ variation_params_.clear(); |
+ } |
+ |
+ private: |
+ friend struct DefaultSingletonTraits<VariationsParamAssociator>; |
+ |
+ VariationsParamAssociator() {} |
+ ~VariationsParamAssociator() {} |
+ |
+ // Tests whether a field trial is active (i.e. group() has been called on it). |
+ // TODO(asvitkine): Expose this as an API on base::FieldTrial. |
+ bool IsFieldTrialActive(const std::string& trial_name) { |
+ base::FieldTrial::ActiveGroups active_groups; |
+ base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups); |
+ for (size_t i = 0; i < active_groups.size(); ++i) { |
+ if (active_groups[i].trial_name == trial_name) |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ base::Lock lock_; |
+ std::map<VariationKey, VariationParams> variation_params_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(VariationsParamAssociator); |
+}; |
+ |
+} // namespace |
+ |
+ActiveGroupId MakeActiveGroupId(const std::string& trial_name, |
+ const std::string& group_name) { |
+ ActiveGroupId id; |
+ id.name = metrics::HashName(trial_name); |
+ id.group = metrics::HashName(group_name); |
+ return id; |
+} |
+ |
+void AssociateGoogleVariationID(IDCollectionKey key, |
+ const std::string& trial_name, |
+ const std::string& group_name, |
+ VariationID id) { |
+ GroupMapAccessor::GetInstance()->AssociateID( |
+ key, MakeActiveGroupId(trial_name, group_name), id, false); |
+} |
+ |
+void AssociateGoogleVariationIDForce(IDCollectionKey key, |
+ const std::string& trial_name, |
+ const std::string& group_name, |
+ VariationID id) { |
+ GroupMapAccessor::GetInstance()->AssociateID( |
+ key, MakeActiveGroupId(trial_name, group_name), id, true); |
+} |
+ |
+VariationID GetGoogleVariationID(IDCollectionKey key, |
+ const std::string& trial_name, |
+ const std::string& group_name) { |
+ return GroupMapAccessor::GetInstance()->GetID( |
+ key, MakeActiveGroupId(trial_name, group_name)); |
+} |
+ |
+bool AssociateVariationParams( |
+ const std::string& trial_name, |
+ const std::string& group_name, |
+ const std::map<std::string, std::string>& params) { |
+ return VariationsParamAssociator::GetInstance()->AssociateVariationParams( |
+ trial_name, group_name, params); |
+} |
+ |
+bool GetVariationParams(const std::string& trial_name, |
+ std::map<std::string, std::string>* params) { |
+ return VariationsParamAssociator::GetInstance()->GetVariationParams( |
+ trial_name, params); |
+} |
+ |
+std::string GetVariationParamValue(const std::string& trial_name, |
+ const std::string& param_name) { |
+ std::map<std::string, std::string> params; |
+ if (GetVariationParams(trial_name, ¶ms)) { |
+ std::map<std::string, std::string>::iterator it = params.find(param_name); |
+ if (it != params.end()) |
+ return it->second; |
+ } |
+ return std::string(); |
+} |
+ |
+// Functions below are exposed for testing explicitly behind this namespace. |
+// They simply wrap existing functions in this file. |
+namespace testing { |
+ |
+void ClearAllVariationIDs() { |
+ GroupMapAccessor::GetInstance()->ClearAllMapsForTesting(); |
+} |
+ |
+void ClearAllVariationParams() { |
+ VariationsParamAssociator::GetInstance()->ClearAllParamsForTesting(); |
+} |
+ |
+} // namespace testing |
+ |
+} // namespace chrome_variations |