diff options
author | asvitkine@chromium.org <asvitkine@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-05 18:24:40 +0000 |
---|---|---|
committer | asvitkine@chromium.org <asvitkine@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-05 18:24:40 +0000 |
commit | 7e797793d31525002837e3a078be62a7c9e94c4e (patch) | |
tree | 2b4315fc721b36cd97306af4cd76720d9ce70cdd | |
parent | 37fad6b194217278d700f40fd6ea2b09d6a86535 (diff) | |
download | chromium_src-7e797793d31525002837e3a078be62a7c9e94c4e.zip chromium_src-7e797793d31525002837e3a078be62a7c9e94c4e.tar.gz chromium_src-7e797793d31525002837e3a078be62a7c9e94c4e.tar.bz2 |
Split variations_util.cc into two separate files.
Moves id and param association to variations_associated_data.cc,
while keeping the rest of the functionality in variations_util.cc.
No functional changes.
BUG=266007
TEST=Existing unit tests.
Review URL: https://chromiumcodereview.appspot.com/21401002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@215644 0039d316-1c4b-4281-b951-d872f2087c98
11 files changed, 710 insertions, 615 deletions
diff --git a/chrome/browser/metrics/variations/variations_http_header_provider.cc b/chrome/browser/metrics/variations/variations_http_header_provider.cc index 8142240..f4ed100 100644 --- a/chrome/browser/metrics/variations/variations_http_header_provider.cc +++ b/chrome/browser/metrics/variations/variations_http_header_provider.cc @@ -10,7 +10,7 @@ #include "base/strings/string_util.h" #include "chrome/browser/google/google_util.h" #include "chrome/common/metrics/proto/chrome_experiments.pb.h" -#include "chrome/common/metrics/variations/variations_util.h" +#include "chrome/common/metrics/variations/variations_associated_data.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/http/http_request_headers.h" #include "url/gurl.h" diff --git a/chrome/browser/metrics/variations/variations_seed_processor.cc b/chrome/browser/metrics/variations/variations_seed_processor.cc index ef98d85..13f34f9 100644 --- a/chrome/browser/metrics/variations/variations_seed_processor.cc +++ b/chrome/browser/metrics/variations/variations_seed_processor.cc @@ -12,7 +12,7 @@ #include "base/version.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/metrics/proto/trials_seed.pb.h" -#include "chrome/common/metrics/variations/variations_util.h" +#include "chrome/common/metrics/variations/variations_associated_data.h" namespace chrome_variations { diff --git a/chrome/browser/ui/bookmarks/bookmark_prompt_controller.cc b/chrome/browser/ui/bookmarks/bookmark_prompt_controller.cc index 6013a1b..386669d 100644 --- a/chrome/browser/ui/bookmarks/bookmark_prompt_controller.cc +++ b/chrome/browser/ui/bookmarks/bookmark_prompt_controller.cc @@ -22,7 +22,7 @@ #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_version_info.h" #include "chrome/common/metrics/variations/variation_ids.h" -#include "chrome/common/metrics/variations/variations_util.h" +#include "chrome/common/metrics/variations/variations_associated_data.h" #include "chrome/common/pref_names.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index 4647180..d510065 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -400,6 +400,8 @@ 'common/metrics/metrics_util.h', 'common/metrics/variations/uniformity_field_trials.cc', 'common/metrics/variations/uniformity_field_trials.h', + 'common/metrics/variations/variations_associated_data.cc', + 'common/metrics/variations/variations_associated_data.h', 'common/metrics/variations/variations_util.cc', 'common/metrics/variations/variations_util.h', 'common/multi_process_lock.h', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 6e8a5fa..b7e3d06 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1741,6 +1741,7 @@ 'common/metrics/metrics_log_base_unittest.cc', 'common/metrics/metrics_log_manager_unittest.cc', 'common/metrics/metrics_util_unittest.cc', + 'common/metrics/variations/variations_associated_data_unittest.cc', 'common/metrics/variations/variations_util_unittest.cc', 'common/multi_process_lock_unittest.cc', 'common/net/url_fixer_upper_unittest.cc', diff --git a/chrome/common/metrics/variations/variations_associated_data.cc b/chrome/common/metrics/variations/variations_associated_data.cc new file mode 100644 index 0000000..f74bc85 --- /dev/null +++ b/chrome/common/metrics/variations/variations_associated_data.cc @@ -0,0 +1,227 @@ +// 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 "chrome/common/metrics/variations/variations_associated_data.h" + +#include <map> +#include <vector> + +#include "base/memory/singleton.h" +#include "chrome/common/metrics/metrics_util.h" +#include "chrome/common/metrics/variations/variation_ids.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; + } + + 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(); +} + +} // namespace testing + +} // namespace chrome_variations diff --git a/chrome/common/metrics/variations/variations_associated_data.h b/chrome/common/metrics/variations/variations_associated_data.h new file mode 100644 index 0000000..182b66b --- /dev/null +++ b/chrome/common/metrics/variations/variations_associated_data.h @@ -0,0 +1,151 @@ +// 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. + +#ifndef CHROME_COMMON_METRICS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_ +#define CHROME_COMMON_METRICS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_ + +#include <map> +#include <string> + +#include "base/metrics/field_trial.h" +#include "base/strings/string16.h" +#include "chrome/common/metrics/variations/variation_ids.h" + +// This file provides various helpers that extend the functionality around +// base::FieldTrial. +// +// This includes several simple APIs to handle getting and setting additional +// data related to Chrome variations, such as parameters and Google variation +// IDs. These APIs are meant to extend the base::FieldTrial APIs to offer extra +// functionality that is not offered by the simpler base::FieldTrial APIs. +// +// The AssociateGoogleVariationID and AssociateVariationParams functions are +// generally meant to be called by the VariationsService based on server-side +// variation configs, but may also be used for client-only field trials by +// invoking them directly after appending all the groups to a FieldTrial. +// +// Experiment code can then use the getter APIs to retrieve variation parameters +// or IDs: +// +// std::map<std::string, std::string> params; +// if (GetVariationParams("trial", ¶ms)) { +// // use |params| +// } +// +// std::string value = GetVariationParamValue("trial", "param_x"); +// // use |value|, which will be "" if it does not exist +// +// VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", +// "group1"); +// if (id != chrome_variations::kEmptyID) { +// // use |id| +// } + +namespace chrome_variations { + +// The Unique ID of a trial and its active group, where the name and group +// identifiers are hashes of the trial and group name strings. +struct ActiveGroupId { + uint32 name; + uint32 group; +}; + +// Returns an ActiveGroupId struct for the given trial and group names. +ActiveGroupId MakeActiveGroupId(const std::string& trial_name, + const std::string& group_name); + +// We need to supply a Compare class for templates since ActiveGroupId is a +// user-defined type. +struct ActiveGroupIdCompare { + bool operator() (const ActiveGroupId& lhs, const ActiveGroupId& rhs) const { + // The group and name fields are just SHA-1 Hashes, so we just need to treat + // them as IDs and do a less-than comparison. We test group first, since + // name is more likely to collide. + if (lhs.group != rhs.group) + return lhs.group < rhs.group; + return lhs.name < rhs.name; + } +}; + +// A key into the Associate/Get methods for VariationIDs. This is used to create +// separate ID associations for separate parties interested in VariationIDs. +enum IDCollectionKey { + // This collection is used by Google web properties, transmitted through the + // X-Chrome-Variations header. + GOOGLE_WEB_PROPERTIES, + // This collection is used by Google update services, transmitted through the + // Google Update experiment labels. + GOOGLE_UPDATE_SERVICE, + // The total count of collections. + ID_COLLECTION_COUNT, +}; + +// Associate a chrome_variations::VariationID value with a FieldTrial group for +// collection |key|. If an id was previously set for |trial_name| and +// |group_name|, this does nothing. The group is denoted by |trial_name| and +// |group_name|. This must be called whenever a FieldTrial is prepared (create +// the trial and append groups) and needs to have a +// chrome_variations::VariationID associated with it so Google servers can +// recognize the FieldTrial. Thread safe. +void AssociateGoogleVariationID(IDCollectionKey key, + const std::string& trial_name, + const std::string& group_name, + VariationID id); + +// As above, but overwrites any previously set id. Thread safe. +void AssociateGoogleVariationIDForce(IDCollectionKey key, + const std::string& trial_name, + const std::string& group_name, + VariationID id); + +// Retrieve the chrome_variations::VariationID associated with a FieldTrial +// group for collection |key|. The group is denoted by |trial_name| and +// |group_name|. This will return chrome_variations::kEmptyID if there is +// currently no associated ID for the named group. This API can be nicely +// combined with FieldTrial::GetActiveFieldTrialGroups() to enumerate the +// variation IDs for all active FieldTrial groups. Thread safe. +VariationID GetGoogleVariationID(IDCollectionKey key, + const std::string& trial_name, + const std::string& group_name); + +// Associates the specified set of key-value |params| with the variation +// specified by |trial_name| and |group_name|. Fails and returns false if the +// specified variation already has params associated with it or the field trial +// is already active (group() has been called on it). Thread safe. +bool AssociateVariationParams(const std::string& trial_name, + const std::string& group_name, + const std::map<std::string, std::string>& params); + +// Retrieves the set of key-value |params| for the variation associated with +// the specified field trial, based on its selected group. If the field trial +// does not exist or its selected group does not have any parameters associated +// with it, returns false and does not modify |params|. Calling this function +// will result in the field trial being marked as active if found (i.e. group() +// will be called on it), if it wasn't already. Currently, this information is +// only available from the browser process. Thread safe. +bool GetVariationParams(const std::string& trial_name, + std::map<std::string, std::string>* params); + +// Retrieves a specific parameter value corresponding to |param_name| for the +// variation associated with the specified field trial, based on its selected +// group. If the field trial does not exist or the specified parameter does not +// exist, returns an empty string. Calling this function will result in the +// field trial being marked as active if found (i.e. group() will be called on +// it), if it wasn't already. Currently, this information is only available from +// the browser process. Thread safe. +std::string GetVariationParamValue(const std::string& trial_name, + const std::string& param_name); + +// Expose some functions for testing. These functions just wrap functionality +// that is implemented above. +namespace testing { + +// Clears all of the mapped associations. +void ClearAllVariationIDs(); + +} // namespace testing + +} // namespace chrome_variations + +#endif // CHROME_COMMON_METRICS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_ diff --git a/chrome/common/metrics/variations/variations_associated_data_unittest.cc b/chrome/common/metrics/variations/variations_associated_data_unittest.cc new file mode 100644 index 0000000..2a194af --- /dev/null +++ b/chrome/common/metrics/variations/variations_associated_data_unittest.cc @@ -0,0 +1,307 @@ +// 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 "chrome/common/metrics/variations/variations_associated_data.h" + +#include "base/metrics/field_trial.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chrome_variations { + +namespace { + +// Convenience helper to retrieve the chrome_variations::VariationID for a +// FieldTrial. Note that this will do the group assignment in |trial| if not +// already done. +VariationID GetIDForTrial(IDCollectionKey key, base::FieldTrial* trial) { + return GetGoogleVariationID(key, trial->trial_name(), trial->group_name()); +} + +// Tests whether a field trial is active (i.e. group() has been called on it). +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; +} + +// Call FieldTrialList::FactoryGetFieldTrial() with a future expiry date. +scoped_refptr<base::FieldTrial> CreateFieldTrial( + const std::string& trial_name, + int total_probability, + const std::string& default_group_name, + int* default_group_number) { + return base::FieldTrialList::FactoryGetFieldTrial( + trial_name, total_probability, default_group_name, + base::FieldTrialList::kNoExpirationYear, 1, 1, + base::FieldTrial::SESSION_RANDOMIZED, default_group_number); +} + +} // namespace + +class VariationsAssociatedDataTest : public ::testing::Test { + public: + VariationsAssociatedDataTest() : field_trial_list_(NULL) { + } + + virtual ~VariationsAssociatedDataTest() { + // Ensure that the maps are cleared between tests, since they are stored as + // process singletons. + testing::ClearAllVariationIDs(); + } + + private: + base::FieldTrialList field_trial_list_; + + DISALLOW_COPY_AND_ASSIGN(VariationsAssociatedDataTest); +}; + +// Test that if the trial is immediately disabled, GetGoogleVariationID just +// returns the empty ID. +TEST_F(VariationsAssociatedDataTest, DisableImmediately) { + int default_group_number = -1; + scoped_refptr<base::FieldTrial> trial( + CreateFieldTrial("trial", 100, "default", &default_group_number)); + + ASSERT_EQ(default_group_number, trial->group()); + ASSERT_EQ(EMPTY_ID, GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial.get())); +} + +// Test that successfully associating the FieldTrial with some ID, and then +// disabling the FieldTrial actually makes GetGoogleVariationID correctly +// return the empty ID. +TEST_F(VariationsAssociatedDataTest, DisableAfterInitialization) { + const std::string default_name = "default"; + const std::string non_default_name = "non_default"; + + scoped_refptr<base::FieldTrial> trial( + CreateFieldTrial("trial", 100, default_name, NULL)); + + trial->AppendGroup(non_default_name, 100); + AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial->trial_name(), + default_name, TEST_VALUE_A); + AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial->trial_name(), + non_default_name, TEST_VALUE_B); + trial->Disable(); + ASSERT_EQ(default_name, trial->group_name()); + ASSERT_EQ(TEST_VALUE_A, GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial.get())); +} + +// Test various successful association cases. +TEST_F(VariationsAssociatedDataTest, AssociateGoogleVariationID) { + const std::string default_name1 = "default"; + scoped_refptr<base::FieldTrial> trial_true( + CreateFieldTrial("d1", 10, default_name1, NULL)); + const std::string winner = "TheWinner"; + int winner_group = trial_true->AppendGroup(winner, 10); + + // Set GoogleVariationIDs so we can verify that they were chosen correctly. + AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(), + default_name1, TEST_VALUE_A); + AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(), + winner, TEST_VALUE_B); + + EXPECT_EQ(winner_group, trial_true->group()); + EXPECT_EQ(winner, trial_true->group_name()); + EXPECT_EQ(TEST_VALUE_B, + GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get())); + + const std::string default_name2 = "default2"; + scoped_refptr<base::FieldTrial> trial_false( + CreateFieldTrial("d2", 10, default_name2, NULL)); + const std::string loser = "ALoser"; + const int loser_group = trial_false->AppendGroup(loser, 0); + + AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_false->trial_name(), + default_name2, TEST_VALUE_A); + AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_false->trial_name(), + loser, TEST_VALUE_B); + + EXPECT_NE(loser_group, trial_false->group()); + EXPECT_EQ(TEST_VALUE_A, + GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_false.get())); +} + +// Test that not associating a FieldTrial with any IDs ensure that the empty ID +// will be returned. +TEST_F(VariationsAssociatedDataTest, NoAssociation) { + const std::string default_name = "default"; + scoped_refptr<base::FieldTrial> no_id_trial( + CreateFieldTrial("d3", 10, default_name, NULL)); + + const std::string winner = "TheWinner"; + const int winner_group = no_id_trial->AppendGroup(winner, 10); + + // Ensure that despite the fact that a normal winner is elected, it does not + // have a valid VariationID associated with it. + EXPECT_EQ(winner_group, no_id_trial->group()); + EXPECT_EQ(winner, no_id_trial->group_name()); + EXPECT_EQ(EMPTY_ID, GetIDForTrial(GOOGLE_WEB_PROPERTIES, no_id_trial.get())); +} + +// Ensure that the AssociateGoogleVariationIDForce works as expected. +TEST_F(VariationsAssociatedDataTest, ForceAssociation) { + EXPECT_EQ(EMPTY_ID, + GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group")); + AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group", + TEST_VALUE_A); + EXPECT_EQ(TEST_VALUE_A, + GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group")); + AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group", + TEST_VALUE_B); + EXPECT_EQ(TEST_VALUE_A, + GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group")); + AssociateGoogleVariationIDForce(GOOGLE_WEB_PROPERTIES, "trial", "group", + TEST_VALUE_B); + EXPECT_EQ(TEST_VALUE_B, + GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group")); +} + +// Ensure that two collections can coexist without affecting each other. +TEST_F(VariationsAssociatedDataTest, CollectionsCoexist) { + const std::string default_name = "default"; + int default_group_number = -1; + scoped_refptr<base::FieldTrial> trial_true( + CreateFieldTrial("d1", 10, default_name, &default_group_number)); + ASSERT_EQ(default_group_number, trial_true->group()); + ASSERT_EQ(default_name, trial_true->group_name()); + + EXPECT_EQ(EMPTY_ID, + GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get())); + EXPECT_EQ(EMPTY_ID, + GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get())); + + AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(), + default_name, TEST_VALUE_A); + EXPECT_EQ(TEST_VALUE_A, + GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get())); + EXPECT_EQ(EMPTY_ID, + GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get())); + + AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, trial_true->trial_name(), + default_name, TEST_VALUE_A); + EXPECT_EQ(TEST_VALUE_A, + GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get())); + EXPECT_EQ(TEST_VALUE_A, + GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get())); +} + +TEST_F(VariationsAssociatedDataTest, AssociateVariationParams) { + const std::string kTrialName = "AssociateVariationParams"; + + { + std::map<std::string, std::string> params; + params["a"] = "10"; + params["b"] = "test"; + ASSERT_TRUE(AssociateVariationParams(kTrialName, "A", params)); + } + { + std::map<std::string, std::string> params; + params["a"] = "5"; + ASSERT_TRUE(AssociateVariationParams(kTrialName, "B", params)); + } + + base::FieldTrialList::CreateFieldTrial(kTrialName, "B"); + EXPECT_EQ("5", GetVariationParamValue(kTrialName, "a")); + EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b")); + EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x")); + + std::map<std::string, std::string> params; + EXPECT_TRUE(GetVariationParams(kTrialName, ¶ms)); + EXPECT_EQ(1U, params.size()); + EXPECT_EQ("5", params["a"]); +} + +TEST_F(VariationsAssociatedDataTest, AssociateVariationParams_Fail) { + const std::string kTrialName = "AssociateVariationParams_Fail"; + const std::string kGroupName = "A"; + + std::map<std::string, std::string> params; + params["a"] = "10"; + ASSERT_TRUE(AssociateVariationParams(kTrialName, kGroupName, params)); + params["a"] = "1"; + params["b"] = "2"; + ASSERT_FALSE(AssociateVariationParams(kTrialName, kGroupName, params)); + + base::FieldTrialList::CreateFieldTrial(kTrialName, kGroupName); + EXPECT_EQ("10", GetVariationParamValue(kTrialName, "a")); + EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b")); +} + +TEST_F(VariationsAssociatedDataTest, AssociateVariationParams_TrialActiveFail) { + const std::string kTrialName = "AssociateVariationParams_TrialActiveFail"; + base::FieldTrialList::CreateFieldTrial(kTrialName, "A"); + ASSERT_EQ("A", base::FieldTrialList::FindFullName(kTrialName)); + + std::map<std::string, std::string> params; + params["a"] = "10"; + EXPECT_FALSE(AssociateVariationParams(kTrialName, "B", params)); + EXPECT_FALSE(AssociateVariationParams(kTrialName, "A", params)); +} + +TEST_F(VariationsAssociatedDataTest, + AssociateVariationParams_DoesntActivateTrial) { + const std::string kTrialName = "AssociateVariationParams_DoesntActivateTrial"; + + ASSERT_FALSE(IsFieldTrialActive(kTrialName)); + scoped_refptr<base::FieldTrial> trial( + CreateFieldTrial(kTrialName, 100, "A", NULL)); + ASSERT_FALSE(IsFieldTrialActive(kTrialName)); + + std::map<std::string, std::string> params; + params["a"] = "10"; + EXPECT_TRUE(AssociateVariationParams(kTrialName, "A", params)); + ASSERT_FALSE(IsFieldTrialActive(kTrialName)); +} + +TEST_F(VariationsAssociatedDataTest, GetVariationParams_NoTrial) { + const std::string kTrialName = "GetVariationParams_NoParams"; + + std::map<std::string, std::string> params; + EXPECT_FALSE(GetVariationParams(kTrialName, ¶ms)); + EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x")); + EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "y")); +} + +TEST_F(VariationsAssociatedDataTest, GetVariationParams_NoParams) { + const std::string kTrialName = "GetVariationParams_NoParams"; + + base::FieldTrialList::CreateFieldTrial(kTrialName, "A"); + + std::map<std::string, std::string> params; + EXPECT_FALSE(GetVariationParams(kTrialName, ¶ms)); + EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x")); + EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "y")); +} + +TEST_F(VariationsAssociatedDataTest, GetVariationParams_ActivatesTrial) { + const std::string kTrialName = "GetVariationParams_ActivatesTrial"; + + ASSERT_FALSE(IsFieldTrialActive(kTrialName)); + scoped_refptr<base::FieldTrial> trial( + CreateFieldTrial(kTrialName, 100, "A", NULL)); + ASSERT_FALSE(IsFieldTrialActive(kTrialName)); + + std::map<std::string, std::string> params; + EXPECT_FALSE(GetVariationParams(kTrialName, ¶ms)); + ASSERT_TRUE(IsFieldTrialActive(kTrialName)); +} + +TEST_F(VariationsAssociatedDataTest, GetVariationParamValue_ActivatesTrial) { + const std::string kTrialName = "GetVariationParamValue_ActivatesTrial"; + + ASSERT_FALSE(IsFieldTrialActive(kTrialName)); + scoped_refptr<base::FieldTrial> trial( + CreateFieldTrial(kTrialName, 100, "A", NULL)); + ASSERT_FALSE(IsFieldTrialActive(kTrialName)); + + std::map<std::string, std::string> params; + EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x")); + ASSERT_TRUE(IsFieldTrialActive(kTrialName)); +} + +} // namespace chrome_variations diff --git a/chrome/common/metrics/variations/variations_util.cc b/chrome/common/metrics/variations/variations_util.cc index 2f64c92..5ef0bf8 100644 --- a/chrome/common/metrics/variations/variations_util.cc +++ b/chrome/common/metrics/variations/variations_util.cc @@ -4,21 +4,15 @@ #include "chrome/common/metrics/variations/variations_util.h" -#include <map> #include <vector> -#include "base/memory/singleton.h" -#include "base/sha1.h" #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "base/sys_byteorder.h" #include "chrome/common/child_process_logging.h" -#include "chrome/common/metrics/metrics_util.h" -#include "chrome/common/metrics/variations/variation_ids.h" #include "chrome/installer/util/google_update_experiment_util.h" namespace chrome_variations { @@ -28,155 +22,6 @@ namespace { const char kVariationPrefix[] = "CrVar"; const char kExperimentLabelSep[] = ";"; -// 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 = VariationKey(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 = VariationKey(trial_name, group_name); - if (!ContainsKey(variation_params_, key)) - return false; - - *params = variation_params_[key]; - return true; - } - - 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); -}; - -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; -} - // Populates |name_group_ids| based on |active_groups|. void GetFieldTrialActiveGroupIdsForActiveGroups( const base::FieldTrial::ActiveGroups& active_groups, @@ -193,8 +38,7 @@ void GetFieldTrialActiveGroupIdsForActiveGroups( // including a timestamp that is a year in the future from now. Since multiple // headers can be transmitted, |count| is a number that is appended after the // label key to differentiate the labels. -string16 CreateSingleExperimentLabel(int count, - chrome_variations::VariationID id) { +string16 CreateSingleExperimentLabel(int count, VariationID id) { // Build the parts separately so they can be validated. const string16 key = ASCIIToUTF16(kVariationPrefix) + base::IntToString16(count); @@ -234,55 +78,6 @@ void GetFieldTrialActiveGroupIdsAsStrings( } } -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(); -} - void GenerateVariationChunks(const std::vector<string16>& experiments, std::vector<string16>* chunks) { string16 current_chunk; @@ -316,12 +111,10 @@ string16 BuildGoogleUpdateExperimentLabel( // Find all currently active VariationIDs associated with Google Update. for (base::FieldTrial::ActiveGroups::const_iterator it = active_groups.begin(); it != active_groups.end(); ++it) { - const chrome_variations::VariationID id = - chrome_variations::GetGoogleVariationID( - chrome_variations::GOOGLE_UPDATE_SERVICE, - it->trial_name, it->group_name); + const VariationID id = GetGoogleVariationID(GOOGLE_UPDATE_SERVICE, + it->trial_name, it->group_name); - if (id == chrome_variations::EMPTY_ID) + if (id == EMPTY_ID) continue; if (!experiment_labels.empty()) @@ -374,10 +167,6 @@ string16 CombineExperimentLabels(const string16& variation_labels, // They simply wrap existing functions in this file. namespace testing { -void ClearAllVariationIDs() { - GroupMapAccessor::GetInstance()->ClearAllMapsForTesting(); -} - void TestGetFieldTrialActiveGroupIds( const base::FieldTrial::ActiveGroups& active_groups, std::vector<ActiveGroupId>* name_group_ids) { @@ -388,4 +177,3 @@ void TestGetFieldTrialActiveGroupIds( } // namespace testing } // namespace chrome_variations - diff --git a/chrome/common/metrics/variations/variations_util.h b/chrome/common/metrics/variations/variations_util.h index 61147b5..f6e29ed 100644 --- a/chrome/common/metrics/variations/variations_util.h +++ b/chrome/common/metrics/variations/variations_util.h @@ -5,80 +5,14 @@ #ifndef CHROME_COMMON_METRICS_VARIATIONS_VARIATIONS_UTIL_H_ #define CHROME_COMMON_METRICS_VARIATIONS_VARIATIONS_UTIL_H_ -#include <map> -#include <string> #include <vector> #include "base/metrics/field_trial.h" #include "base/strings/string16.h" -#include "chrome/common/metrics/variations/variation_ids.h" - -// This file provides various helpers that extend the functionality around -// base::FieldTrial. -// -// This includes several simple APIs to handle getting and setting additional -// data related to Chrome variations, such as parameters and Google variation -// IDs. These APIs are meant to extend the base::FieldTrial APIs to offer extra -// functionality that is not offered by the simpler base::FieldTrial APIs. -// -// The AssociateGoogleVariationID and AssociateVariationParams functions are -// generally meant to be called by the VariationsService based on server-side -// variation configs, but may also be used for client-only field trials by -// invoking them directly after appending all the groups to a FieldTrial. -// -// Experiment code can then use the getter APIs to retrieve variation parameters -// or IDs: -// -// std::map<std::string, std::string> params; -// if (GetVariationParams("trial", ¶ms)) { -// // use |params| -// } -// -// std::string value = GetVariationParamValue("trial", "param_x"); -// // use |value|, which will be "" if it does not exist -// -// VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", -// "group1"); -// if (id != chrome_variations::kEmptyID) { -// // use |id| -// } +#include "chrome/common/metrics/variations/variations_associated_data.h" namespace chrome_variations { -// A key into the Associate/Get methods for VariationIDs. This is used to create -// separate ID associations for separate parties interested in VariationIDs. -enum IDCollectionKey { - // This collection is used by Google web properties, transmitted through the - // X-Chrome-Variations header. - GOOGLE_WEB_PROPERTIES, - // This collection is used by Google update services, transmitted through the - // Google Update experiment labels. - GOOGLE_UPDATE_SERVICE, - // The total count of collections. - ID_COLLECTION_COUNT, -}; - -// The Unique ID of a trial and its active group, where the name and group -// identifiers are hashes of the trial and group name strings. -struct ActiveGroupId { - uint32 name; - uint32 group; -}; - -// We need to supply a Compare class for templates since ActiveGroupId is a -// user-defined type. -struct ActiveGroupIdCompare { - bool operator() (const ActiveGroupId& lhs, - const ActiveGroupId& rhs) const { - // The group and name fields are just SHA-1 Hashes, so we just need to treat - // them as IDs and do a less-than comparison. We test group first, since - // name is more likely to collide. - if (lhs.group != rhs.group) - return lhs.group < rhs.group; - return lhs.name < rhs.name; - } -}; - // Fills the supplied vector |name_group_ids| (which must be empty when called) // with unique ActiveGroupIds for each Field Trial that has a chosen group. // Field Trials for which a group has not been chosen yet are NOT returned in @@ -92,62 +26,6 @@ void GetFieldTrialActiveGroupIds(std::vector<ActiveGroupId>* name_group_ids); // chosen yet are NOT returned in this list. void GetFieldTrialActiveGroupIdsAsStrings(std::vector<string16>* output); -// Associate a chrome_variations::VariationID value with a FieldTrial group for -// collection |key|. If an id was previously set for |trial_name| and -// |group_name|, this does nothing. The group is denoted by |trial_name| and -// |group_name|. This must be called whenever a FieldTrial is prepared (create -// the trial and append groups) and needs to have a -// chrome_variations::VariationID associated with it so Google servers can -// recognize the FieldTrial. Thread safe. -void AssociateGoogleVariationID(IDCollectionKey key, - const std::string& trial_name, - const std::string& group_name, - VariationID id); - -// As above, but overwrites any previously set id. Thread safe. -void AssociateGoogleVariationIDForce(IDCollectionKey key, - const std::string& trial_name, - const std::string& group_name, - VariationID id); - -// Retrieve the chrome_variations::VariationID associated with a FieldTrial -// group for collection |key|. The group is denoted by |trial_name| and -// |group_name|. This will return chrome_variations::kEmptyID if there is -// currently no associated ID for the named group. This API can be nicely -// combined with FieldTrial::GetActiveFieldTrialGroups() to enumerate the -// variation IDs for all active FieldTrial groups. Thread safe. -VariationID GetGoogleVariationID(IDCollectionKey key, - const std::string& trial_name, - const std::string& group_name); - -// Associates the specified set of key-value |params| with the variation -// specified by |trial_name| and |group_name|. Fails and returns false if the -// specified variation already has params associated with it or the field trial -// is already active (group() has been called on it). Thread safe. -bool AssociateVariationParams(const std::string& trial_name, - const std::string& group_name, - const std::map<std::string, std::string>& params); - -// Retrieves the set of key-value |params| for the variation associated with -// the specified field trial, based on its selected group. If the field trial -// does not exist or its selected group does not have any parameters associated -// with it, returns false and does not modify |params|. Calling this function -// will result in the field trial being marked as active if found (i.e. group() -// will be called on it), if it wasn't already. Currently, this information is -// only available from the browser process. Thread safe. -bool GetVariationParams(const std::string& trial_name, - std::map<std::string, std::string>* params); - -// Retrieves a specific parameter value corresponding to |param_name| for the -// variation associated with the specified field trial, based on its selected -// group. If the field trial does not exist or the specified parameter does not -// exist, returns an empty string. Calling this function will result in the -// field trial being marked as active if found (i.e. group() will be called on -// it), if it wasn't already. Currently, this information is only available from -// the browser process. Thread safe. -std::string GetVariationParamValue(const std::string& trial_name, - const std::string& param_name); - // Generates variation chunks from |variation_strings| that are suitable for // crash reporting. void GenerateVariationChunks(const std::vector<string16>& variation_strings, diff --git a/chrome/common/metrics/variations/variations_util_unittest.cc b/chrome/common/metrics/variations/variations_util_unittest.cc index 173133a..c92da5b 100644 --- a/chrome/common/metrics/variations/variations_util_unittest.cc +++ b/chrome/common/metrics/variations/variations_util_unittest.cc @@ -2,32 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Tests for the Variations Helpers. +#include "chrome/common/metrics/variations/variations_util.h" #include <set> +#include <string> -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" #include "base/metrics/field_trial.h" #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "chrome/common/metrics/metrics_util.h" -#include "chrome/common/metrics/variations/variations_util.h" -#include "content/public/test/test_browser_thread.h" #include "testing/gtest/include/gtest/gtest.h" namespace chrome_variations { namespace { -// Convenience helper to retrieve the chrome_variations::VariationID for a -// FieldTrial. Note that this will do the group assignment in |trial| if not -// already done. -VariationID GetIDForTrial(IDCollectionKey key, base::FieldTrial* trial) { - return GetGoogleVariationID(key, trial->trial_name(), trial->group_name()); -} - // Tests whether a field trial is active (i.e. group() has been called on it). bool IsFieldTrialActive(const std::string& trial_name) { base::FieldTrial::ActiveGroups active_groups; @@ -56,11 +46,9 @@ scoped_refptr<base::FieldTrial> CreateFieldTrial( class VariationsUtilTest : public ::testing::Test { public: VariationsUtilTest() : field_trial_list_(NULL) { - // Since the API can only be called on the UI thread, we have to fake that - // we're on it. - ui_thread_.reset(new content::TestBrowserThread( - content::BrowserThread::UI, &message_loop_)); + } + virtual ~VariationsUtilTest() { // Ensure that the maps are cleared between tests, since they are stored as // process singletons. testing::ClearAllVariationIDs(); @@ -68,8 +56,8 @@ class VariationsUtilTest : public ::testing::Test { private: base::FieldTrialList field_trial_list_; - base::MessageLoop message_loop_; - scoped_ptr<content::TestBrowserThread> ui_thread_; + + DISALLOW_COPY_AND_ASSIGN(VariationsUtilTest); }; TEST_F(VariationsUtilTest, GetFieldTrialActiveGroups) { @@ -111,220 +99,6 @@ TEST_F(VariationsUtilTest, GetFieldTrialActiveGroups) { EXPECT_EQ(0U, expected_groups.size()); } -// Test that if the trial is immediately disabled, GetGoogleVariationID just -// returns the empty ID. -TEST_F(VariationsUtilTest, DisableImmediately) { - int default_group_number = -1; - scoped_refptr<base::FieldTrial> trial( - CreateFieldTrial("trial", 100, "default", &default_group_number)); - - ASSERT_EQ(default_group_number, trial->group()); - ASSERT_EQ(EMPTY_ID, GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial.get())); -} - -// Test that successfully associating the FieldTrial with some ID, and then -// disabling the FieldTrial actually makes GetGoogleVariationID correctly -// return the empty ID. -TEST_F(VariationsUtilTest, DisableAfterInitialization) { - const std::string default_name = "default"; - const std::string non_default_name = "non_default"; - - scoped_refptr<base::FieldTrial> trial( - CreateFieldTrial("trial", 100, default_name, NULL)); - - trial->AppendGroup(non_default_name, 100); - AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial->trial_name(), - default_name, TEST_VALUE_A); - AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial->trial_name(), - non_default_name, TEST_VALUE_B); - trial->Disable(); - ASSERT_EQ(default_name, trial->group_name()); - ASSERT_EQ(TEST_VALUE_A, GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial.get())); -} - -// Test various successful association cases. -TEST_F(VariationsUtilTest, AssociateGoogleVariationID) { - const std::string default_name1 = "default"; - scoped_refptr<base::FieldTrial> trial_true( - CreateFieldTrial("d1", 10, default_name1, NULL)); - const std::string winner = "TheWinner"; - int winner_group = trial_true->AppendGroup(winner, 10); - - // Set GoogleVariationIDs so we can verify that they were chosen correctly. - AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(), - default_name1, TEST_VALUE_A); - AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(), - winner, TEST_VALUE_B); - - EXPECT_EQ(winner_group, trial_true->group()); - EXPECT_EQ(winner, trial_true->group_name()); - EXPECT_EQ(TEST_VALUE_B, - GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get())); - - const std::string default_name2 = "default2"; - scoped_refptr<base::FieldTrial> trial_false( - CreateFieldTrial("d2", 10, default_name2, NULL)); - const std::string loser = "ALoser"; - const int loser_group = trial_false->AppendGroup(loser, 0); - - AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_false->trial_name(), - default_name2, TEST_VALUE_A); - AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_false->trial_name(), - loser, TEST_VALUE_B); - - EXPECT_NE(loser_group, trial_false->group()); - EXPECT_EQ(TEST_VALUE_A, - GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_false.get())); -} - -// Test that not associating a FieldTrial with any IDs ensure that the empty ID -// will be returned. -TEST_F(VariationsUtilTest, NoAssociation) { - const std::string default_name = "default"; - scoped_refptr<base::FieldTrial> no_id_trial( - CreateFieldTrial("d3", 10, default_name, NULL)); - - const std::string winner = "TheWinner"; - const int winner_group = no_id_trial->AppendGroup(winner, 10); - - // Ensure that despite the fact that a normal winner is elected, it does not - // have a valid VariationID associated with it. - EXPECT_EQ(winner_group, no_id_trial->group()); - EXPECT_EQ(winner, no_id_trial->group_name()); - EXPECT_EQ(EMPTY_ID, GetIDForTrial(GOOGLE_WEB_PROPERTIES, no_id_trial.get())); -} - -// Ensure that the AssociateGoogleVariationIDForce works as expected. -TEST_F(VariationsUtilTest, ForceAssociation) { - EXPECT_EQ(EMPTY_ID, - GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group")); - AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group", - TEST_VALUE_A); - EXPECT_EQ(TEST_VALUE_A, - GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group")); - AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group", - TEST_VALUE_B); - EXPECT_EQ(TEST_VALUE_A, - GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group")); - AssociateGoogleVariationIDForce(GOOGLE_WEB_PROPERTIES, "trial", "group", - TEST_VALUE_B); - EXPECT_EQ(TEST_VALUE_B, - GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group")); -} - -TEST_F(VariationsUtilTest, AssociateVariationParams) { - const std::string kTrialName = "AssociateVariationParams"; - - { - std::map<std::string, std::string> params; - params["a"] = "10"; - params["b"] = "test"; - ASSERT_TRUE(AssociateVariationParams(kTrialName, "A", params)); - } - { - std::map<std::string, std::string> params; - params["a"] = "5"; - ASSERT_TRUE(AssociateVariationParams(kTrialName, "B", params)); - } - - base::FieldTrialList::CreateFieldTrial(kTrialName, "B"); - EXPECT_EQ("5", GetVariationParamValue(kTrialName, "a")); - EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b")); - EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x")); - - std::map<std::string, std::string> params; - EXPECT_TRUE(GetVariationParams(kTrialName, ¶ms)); - EXPECT_EQ(1U, params.size()); - EXPECT_EQ("5", params["a"]); -} - -TEST_F(VariationsUtilTest, AssociateVariationParams_Fail) { - const std::string kTrialName = "AssociateVariationParams_Fail"; - const std::string kGroupName = "A"; - - std::map<std::string, std::string> params; - params["a"] = "10"; - ASSERT_TRUE(AssociateVariationParams(kTrialName, kGroupName, params)); - params["a"] = "1"; - params["b"] = "2"; - ASSERT_FALSE(AssociateVariationParams(kTrialName, kGroupName, params)); - - base::FieldTrialList::CreateFieldTrial(kTrialName, kGroupName); - EXPECT_EQ("10", GetVariationParamValue(kTrialName, "a")); - EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b")); -} - -TEST_F(VariationsUtilTest, AssociateVariationParams_TrialActiveFail) { - const std::string kTrialName = "AssociateVariationParams_TrialActiveFail"; - base::FieldTrialList::CreateFieldTrial(kTrialName, "A"); - ASSERT_EQ("A", base::FieldTrialList::FindFullName(kTrialName)); - - std::map<std::string, std::string> params; - params["a"] = "10"; - EXPECT_FALSE(AssociateVariationParams(kTrialName, "B", params)); - EXPECT_FALSE(AssociateVariationParams(kTrialName, "A", params)); -} - -TEST_F(VariationsUtilTest, AssociateVariationParams_DoesntActivateTrial) { - const std::string kTrialName = "AssociateVariationParams_DoesntActivateTrial"; - - ASSERT_FALSE(IsFieldTrialActive(kTrialName)); - scoped_refptr<base::FieldTrial> trial( - CreateFieldTrial(kTrialName, 100, "A", NULL)); - ASSERT_FALSE(IsFieldTrialActive(kTrialName)); - - std::map<std::string, std::string> params; - params["a"] = "10"; - EXPECT_TRUE(AssociateVariationParams(kTrialName, "A", params)); - ASSERT_FALSE(IsFieldTrialActive(kTrialName)); -} - -TEST_F(VariationsUtilTest, GetVariationParams_NoTrial) { - const std::string kTrialName = "GetVariationParams_NoParams"; - - std::map<std::string, std::string> params; - EXPECT_FALSE(GetVariationParams(kTrialName, ¶ms)); - EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x")); - EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "y")); -} - -TEST_F(VariationsUtilTest, GetVariationParams_NoParams) { - const std::string kTrialName = "GetVariationParams_NoParams"; - - base::FieldTrialList::CreateFieldTrial(kTrialName, "A"); - - std::map<std::string, std::string> params; - EXPECT_FALSE(GetVariationParams(kTrialName, ¶ms)); - EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x")); - EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "y")); -} - -TEST_F(VariationsUtilTest, GetVariationParams_ActivatesTrial) { - const std::string kTrialName = "GetVariationParams_ActivatesTrial"; - - ASSERT_FALSE(IsFieldTrialActive(kTrialName)); - scoped_refptr<base::FieldTrial> trial( - CreateFieldTrial(kTrialName, 100, "A", NULL)); - ASSERT_FALSE(IsFieldTrialActive(kTrialName)); - - std::map<std::string, std::string> params; - EXPECT_FALSE(GetVariationParams(kTrialName, ¶ms)); - ASSERT_TRUE(IsFieldTrialActive(kTrialName)); -} - -TEST_F(VariationsUtilTest, GetVariationParamValue_ActivatesTrial) { - const std::string kTrialName = "GetVariationParamValue_ActivatesTrial"; - - ASSERT_FALSE(IsFieldTrialActive(kTrialName)); - scoped_refptr<base::FieldTrial> trial( - CreateFieldTrial(kTrialName, 100, "A", NULL)); - ASSERT_FALSE(IsFieldTrialActive(kTrialName)); - - std::map<std::string, std::string> params; - EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x")); - ASSERT_TRUE(IsFieldTrialActive(kTrialName)); -} - TEST_F(VariationsUtilTest, GenerateExperimentChunks) { const char* kExperimentStrings[] = { "1d3048f1-9de009d0", @@ -379,35 +153,6 @@ TEST_F(VariationsUtilTest, GenerateExperimentChunks) { } } -// Ensure that two collections can coexist without affecting each other. -TEST_F(VariationsUtilTest, CollectionsCoexist) { - const std::string default_name = "default"; - int default_group_number = -1; - scoped_refptr<base::FieldTrial> trial_true( - CreateFieldTrial("d1", 10, default_name, &default_group_number)); - ASSERT_EQ(default_group_number, trial_true->group()); - ASSERT_EQ(default_name, trial_true->group_name()); - - EXPECT_EQ(EMPTY_ID, - GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get())); - EXPECT_EQ(EMPTY_ID, - GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get())); - - AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(), - default_name, TEST_VALUE_A); - EXPECT_EQ(TEST_VALUE_A, - GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get())); - EXPECT_EQ(EMPTY_ID, - GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get())); - - AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, trial_true->trial_name(), - default_name, TEST_VALUE_A); - EXPECT_EQ(TEST_VALUE_A, - GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get())); - EXPECT_EQ(TEST_VALUE_A, - GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get())); -} - TEST_F(VariationsUtilTest, BuildGoogleUpdateExperimentLabel) { struct { const char* active_group_pairs; @@ -431,18 +176,14 @@ TEST_F(VariationsUtilTest, BuildGoogleUpdateExperimentLabel) { }; // Register a few VariationIDs. - chrome_variations::AssociateGoogleVariationID( - chrome_variations::GOOGLE_UPDATE_SERVICE, "FieldTrialA", "Default", - chrome_variations::TEST_VALUE_A); - chrome_variations::AssociateGoogleVariationID( - chrome_variations::GOOGLE_UPDATE_SERVICE, "FieldTrialB", "Group1", - chrome_variations::TEST_VALUE_B); - chrome_variations::AssociateGoogleVariationID( - chrome_variations::GOOGLE_UPDATE_SERVICE, "FieldTrialC", "Default", - chrome_variations::TEST_VALUE_C); - chrome_variations::AssociateGoogleVariationID( - chrome_variations::GOOGLE_UPDATE_SERVICE, "FieldTrialD", "Default", - chrome_variations::TEST_VALUE_D); // Not actually used. + AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, "FieldTrialA", "Default", + TEST_VALUE_A); + AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, "FieldTrialB", "Group1", + TEST_VALUE_B); + AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, "FieldTrialC", "Default", + TEST_VALUE_C); + AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, "FieldTrialD", "Default", + TEST_VALUE_D); // Not actually used. for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { // Parse the input groups. |