summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-29 05:42:22 +0000
committerrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-29 05:42:22 +0000
commit63a8ba1e1fb71156beff23b4a26828dbc387c734 (patch)
treee69b6e1f24c5d7d1446bc6d5596518f44399bc67 /base
parentdbf00c96e956033c1ea9b7542fa0d9c5ab1e66ac (diff)
downloadchromium_src-63a8ba1e1fb71156beff23b4a26828dbc387c734.zip
chromium_src-63a8ba1e1fb71156beff23b4a26828dbc387c734.tar.gz
chromium_src-63a8ba1e1fb71156beff23b4a26828dbc387c734.tar.bz2
Field trials are currently implemented (commonly) using a static variable that is set
once, the first time it is necessary to decide if there is an experiment by a given name active. With this change the field-test system can "push" a group that is selected for the given field trial (field test) if/when an experiment does arrive. This change implements a simple IPC notification of the result of a FieldTrial setting being sent to any previously started renderers. BUG=16494 TEST=field trial tests R=jar Review URL: http://codereview.chromium.org/6883029 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@83488 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r--base/metrics/field_trial.cc88
-rw-r--r--base/metrics/field_trial.h47
-rw-r--r--base/metrics/field_trial_unittest.cc28
3 files changed, 140 insertions, 23 deletions
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index ad2e5a0..daeb369 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -25,6 +25,10 @@ const char FieldTrialList::kPersistentStringSeparator('/');
static const char kHistogramFieldTrialSeparator('_');
+// static
+int FieldTrialList::kExpirationYearInFuture = 0;
+
+
//------------------------------------------------------------------------------
// FieldTrial methods and members.
@@ -83,6 +87,7 @@ int FieldTrial::AppendGroup(const std::string& name,
base::StringAppendF(&group_name_, "%d", group_);
else
group_name_ = name;
+ FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_);
}
return next_group_number_++;
}
@@ -92,6 +97,7 @@ int FieldTrial::group() {
accumulated_group_probability_ = divisor_;
group_ = kDefaultGroupNumber;
group_name_ = default_group_name_;
+ FieldTrialList::NotifyFieldTrialGroupSelection(name_, group_name_);
}
return group_;
}
@@ -136,10 +142,18 @@ FieldTrialList* FieldTrialList::global_ = NULL;
// static
bool FieldTrialList::register_without_global_ = false;
-FieldTrialList::FieldTrialList() : application_start_time_(TimeTicks::Now()) {
+FieldTrialList::FieldTrialList()
+ : application_start_time_(TimeTicks::Now()),
+ observer_list_(ObserverList<Observer>::NOTIFY_EXISTING_ONLY) {
DCHECK(!global_);
DCHECK(!register_without_global_);
global_ = this;
+
+ Time::Exploded exploded;
+ Time two_years_from_now =
+ Time::NowFromSystemTime() + TimeDelta::FromDays(730);
+ two_years_from_now.LocalExplode(&exploded);
+ kExpirationYearInFuture = exploded.year;
}
FieldTrialList::~FieldTrialList() {
@@ -219,12 +233,6 @@ bool FieldTrialList::CreateTrialsInChildProcess(
if (parent_trials.empty() || !global_)
return true;
- Time::Exploded exploded;
- Time two_years_from_now =
- Time::NowFromSystemTime() + TimeDelta::FromDays(730);
- two_years_from_now.LocalExplode(&exploded);
- const int kTwoYearsFromNow = exploded.year;
-
size_t next_item = 0;
while (next_item < parent_trials.length()) {
size_t name_end = parent_trials.find(kPersistentStringSeparator, next_item);
@@ -239,23 +247,65 @@ bool FieldTrialList::CreateTrialsInChildProcess(
group_name_end - name_end - 1);
next_item = group_name_end + 1;
- FieldTrial *field_trial(FieldTrialList::Find(name));
- if (field_trial) {
- // In single process mode, we may have already created the field trial.
- if ((field_trial->group_name_internal() != group_name) &&
- (field_trial->default_group_name() != group_name))
- return false;
- continue;
- }
- const int kTotalProbability = 100;
- field_trial = new FieldTrial(name, kTotalProbability, group_name,
- kTwoYearsFromNow, 1, 1);
- field_trial->AppendGroup(group_name, kTotalProbability);
+ if (!CreateFieldTrial(name, group_name))
+ return false;
}
return true;
}
// static
+FieldTrial* FieldTrialList::CreateFieldTrial(
+ const std::string& name,
+ const std::string& group_name) {
+ DCHECK(global_);
+ DCHECK_GE(name.size(), 0u);
+ DCHECK_GE(group_name.size(), 0u);
+ if (name.empty() || group_name.empty() || !global_)
+ return NULL;
+
+ FieldTrial *field_trial(FieldTrialList::Find(name));
+ if (field_trial) {
+ // In single process mode, we may have already created the field trial.
+ if (field_trial->group_name_internal() != group_name)
+ return NULL;
+ return field_trial;
+ }
+ const int kTotalProbability = 100;
+ field_trial = new FieldTrial(name, kTotalProbability, group_name,
+ kExpirationYearInFuture, 1, 1);
+ field_trial->AppendGroup(group_name, kTotalProbability);
+ return field_trial;
+}
+
+// static
+void FieldTrialList::AddObserver(Observer* observer) {
+ if (!global_)
+ return;
+ DCHECK(global_);
+ global_->observer_list_.AddObserver(observer);
+}
+
+// static
+void FieldTrialList::RemoveObserver(Observer* observer) {
+ if (!global_)
+ return;
+ DCHECK(global_);
+ global_->observer_list_.RemoveObserver(observer);
+}
+
+// static
+void FieldTrialList::NotifyFieldTrialGroupSelection(
+ const std::string& name,
+ const std::string& group_name) {
+ if (!global_)
+ return;
+ DCHECK(global_);
+ FOR_EACH_OBSERVER(Observer,
+ global_->observer_list_,
+ OnFieldTrialGroupFinalized(name, group_name));
+}
+
+// static
size_t FieldTrialList::GetFieldTrialCount() {
if (!global_)
return 0;
diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h
index dcfd391..b8ab0c5 100644
--- a/base/metrics/field_trial.h
+++ b/base/metrics/field_trial.h
@@ -81,6 +81,7 @@
#include "base/base_api.h"
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
#include "base/synchronization/lock.h"
#include "base/time.h"
@@ -218,6 +219,20 @@ class BASE_API FieldTrialList {
// second process to mimic our state (i.e., provide the same group name).
static const char kPersistentStringSeparator; // Currently a slash.
+ // Define expiration year in future. It is initialized to two years from Now.
+ static int kExpirationYearInFuture;
+
+ // Observer is notified when a FieldTrial's group is selected.
+ class Observer {
+ public:
+ // Notify observers when FieldTrials's group is selected.
+ virtual void OnFieldTrialGroupFinalized(const std::string& trial_name,
+ const std::string& group_name) = 0;
+
+ protected:
+ virtual ~Observer() {}
+ };
+
// This singleton holds the global list of registered FieldTrials.
FieldTrialList();
// Destructor Release()'s references to all registered FieldTrial instances.
@@ -245,11 +260,34 @@ class BASE_API FieldTrialList {
// Use a previously generated state string (re: StatesToString()) augment the
// current list of field tests to include the supplied tests, and using a 100%
// probability for each test, force them to have the same group string. This
- // is commonly used in a sub-process, to carry randomly selected state in a
- // parent process into this sub-process.
- // Currently only the group_name_ and name_ are restored.
+ // is commonly used in a non-browser process, to carry randomly selected state
+ // in a browser process into this non-browser process. This method calls
+ // CreateFieldTrial to create the FieldTrial in the non-browser process.
+ // Currently only the group_name_ and name_ are restored.
static bool CreateTrialsInChildProcess(const std::string& prior_trials);
+ // Create a FieldTrial with the given |name| and using 100% probability for
+ // the FieldTrial, force FieldTrial to have the same group string as
+ // |group_name|. This is commonly used in a non-browser process, to carry
+ // randomly selected state in a browser process into this non-browser process.
+ // Currently only the group_name_ and name_ are set in the FieldTrial. It
+ // returns NULL if there is a FieldTrial that is already registered with the
+ // same |name| but has different finalized group string (|group_name|).
+ static FieldTrial* CreateFieldTrial(const std::string& name,
+ const std::string& group_name);
+
+ // Add an observer to be notified when a field trial is irrevocably committed
+ // to being part of some specific field_group (and hence the group_name is
+ // also finalized for that field_trial).
+ static void AddObserver(Observer* observer);
+
+ // Remove an observer.
+ static void RemoveObserver(Observer* observer);
+
+ // Notify all observers that a group is finalized for the named Trial.
+ static void NotifyFieldTrialGroupSelection(const std::string& name,
+ const std::string& group_name);
+
// The time of construction of the global map is recorded in a static variable
// and is commonly used by experiments to identify the time since the start
// of the application. In some experiments it may be useful to discount
@@ -288,6 +326,9 @@ class BASE_API FieldTrialList {
base::Lock lock_;
RegistrationList registered_;
+ // List of observers to be notified when a group is selected for a FieldTrial.
+ ObserverList<Observer> observer_list_;
+
DISALLOW_COPY_AND_ASSIGN(FieldTrialList);
};
diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc
index 0af6d60..3357085 100644
--- a/base/metrics/field_trial_unittest.cc
+++ b/base/metrics/field_trial_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -287,6 +287,32 @@ TEST_F(FieldTrialTest, DuplicateRestore) {
EXPECT_FALSE(FieldTrialList::CreateTrialsInChildProcess("Some name/Loser/"));
}
+TEST_F(FieldTrialTest, CreateFieldTrial) {
+ EXPECT_TRUE(FieldTrialList::Find("Some_name") == NULL);
+
+ FieldTrialList::CreateFieldTrial("Some_name", "Winner");
+
+ FieldTrial* trial = FieldTrialList::Find("Some_name");
+ ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
+ EXPECT_EQ("Winner", trial->group_name());
+ EXPECT_EQ("Some_name", trial->name());
+}
+
+TEST_F(FieldTrialTest, DuplicateFieldTrial) {
+ FieldTrial* trial =
+ new FieldTrial(
+ "Some_name", 10, "Default some name", next_year_, 12, 31);
+ trial->AppendGroup("Winner", 10);
+
+ // It is OK if we redundantly specify a winner.
+ FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("Some_name", "Winner");
+ EXPECT_TRUE(trial1 != NULL);
+
+ // But it is an error to try to change to a different winner.
+ FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("Some_name", "Loser");
+ EXPECT_TRUE(trial2 == NULL);
+}
+
TEST_F(FieldTrialTest, MakeName) {
FieldTrial* trial =
new FieldTrial("Field Trial", 10, "Winner", next_year_, 12, 31);