summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
Diffstat (limited to 'base')
-rw-r--r--base/field_trial.cc40
-rw-r--r--base/field_trial.h17
-rw-r--r--base/field_trial_unittest.cc41
3 files changed, 98 insertions, 0 deletions
diff --git a/base/field_trial.cc b/base/field_trial.cc
index d0085e6..fc12f31 100644
--- a/base/field_trial.cc
+++ b/base/field_trial.cc
@@ -13,6 +13,9 @@ using base::Time;
// static
const int FieldTrial::kNotParticipating = -1;
+// static
+const char FieldTrial::kPersistentStringSeparator('/');
+
//------------------------------------------------------------------------------
// FieldTrial methods and members.
@@ -50,6 +53,40 @@ std::string FieldTrial::MakeName(const std::string& name_prefix,
return big_string.append(FieldTrialList::FindFullName(trial_name));
}
+std::string FieldTrial::MakePersistentString() const {
+ DCHECK_EQ(name_.find(kPersistentStringSeparator), std::string::npos);
+ DCHECK_EQ(group_name_.find(kPersistentStringSeparator), std::string::npos);
+
+ std::string persistent(name_);
+ persistent = persistent.append(1, kPersistentStringSeparator);
+ persistent = persistent.append(group_name_);
+ return persistent;
+}
+
+// static
+FieldTrial* FieldTrial::RestorePersistentString(const std::string &persistent) {
+ size_t split_point = persistent.find(kPersistentStringSeparator);
+ if (std::string::npos == split_point)
+ return NULL; // Bogus string.
+ std::string new_name(persistent, 0, split_point);
+ std::string new_group_name(persistent, split_point + 1);
+ if (new_name.empty() || new_group_name.empty())
+ return NULL; // Incomplete string.
+
+ FieldTrial *field_trial;
+ field_trial = FieldTrialList::Find(new_name);
+ if (field_trial) {
+ // In single process mode, we may have already created the field trial.
+ if (field_trial->group_name_ != new_group_name)
+ return NULL; // Conflicting group name :-(.
+ } else {
+ const int kTotalProbability = 100;
+ field_trial = new FieldTrial(new_name, kTotalProbability);
+ field_trial->AppendGroup(new_group_name, kTotalProbability);
+ }
+ return field_trial;
+}
+
//------------------------------------------------------------------------------
// FieldTrialList methods and members.
@@ -75,6 +112,9 @@ FieldTrialList::~FieldTrialList() {
// static
void FieldTrialList::Register(FieldTrial* trial) {
+ DCHECK(global_);
+ if (!global_)
+ return;
AutoLock auto_lock(global_->lock_);
DCHECK(!global_->PreLockedFind(trial->name()));
trial->AddRef();
diff --git a/base/field_trial.h b/base/field_trial.h
index 4d04d30..83cbcf6 100644
--- a/base/field_trial.h
+++ b/base/field_trial.h
@@ -75,6 +75,11 @@ class FieldTrial : public base::RefCounted<FieldTrial> {
public:
static const int kNotParticipating;
+ // Define a separator charactor to use when creating a persistent form of an
+ // instance. This is intended for use as a command line argument, passed to a
+ // second process to mimic our state (i.e., provide the same group name).
+ static const char kPersistentStringSeparator; // Currently a slash.
+
typedef int Probability; // Use scaled up probability.
// The name is used to register the instance with the FieldTrialList class,
@@ -107,6 +112,18 @@ class FieldTrial : public base::RefCounted<FieldTrial> {
static std::string MakeName(const std::string& name_prefix,
const std::string& trial_name);
+ // Create a persistent representation of the instance that could be resurected
+ // in another process. This allows randomization to be done in one process,
+ // and secondary processes can by synchronized on the result.
+ // The resulting string contains only the name, the trial name, and a "/"
+ // separator.
+ std::string MakePersistentString() const;
+
+ // Using a string created by MakePersistentString(), construct a new instance
+ // that has the same state as the original instance. Currently only the
+ // group_name_ and name_ are restored.
+ static FieldTrial* RestorePersistentString(const std::string &persistent);
+
private:
// The name of the field trial, as can be found via the FieldTrialList.
// This is empty of the trial is not in the experiment.
diff --git a/base/field_trial_unittest.cc b/base/field_trial_unittest.cc
index 6c818e5..f678b0b 100644
--- a/base/field_trial_unittest.cc
+++ b/base/field_trial_unittest.cc
@@ -114,3 +114,44 @@ TEST_F(FieldTrialTest, OneWinner) {
EXPECT_EQ(trial->group(), winner_index);
EXPECT_EQ(winner_name, trial->group_name());
}
+
+TEST_F(FieldTrialTest, Save) {
+ FieldTrial* trial = new FieldTrial("Some name", 10);
+ // There is no winner yet, so no textual group name is associated with trial.
+ EXPECT_EQ(trial->group_name(), "");
+ EXPECT_EQ(trial->MakePersistentString(), "Some name/");
+
+ // Create a winning group.
+ trial->AppendGroup("Winner", 10);
+ EXPECT_EQ(trial->MakePersistentString(), "Some name/Winner");
+}
+
+TEST_F(FieldTrialTest, Restore) {
+ FieldTrial* trial = FieldTrial::RestorePersistentString("Some name/winner");
+ EXPECT_EQ(trial->group_name(), "winner");
+ EXPECT_EQ(trial->name(), "Some name");
+}
+
+TEST_F(FieldTrialTest, BogusRestore) {
+ const FieldTrial *trial = FieldTrial::RestorePersistentString("MissingSlash");
+ EXPECT_EQ(trial, static_cast<FieldTrial *>(NULL));
+
+ trial = FieldTrial::RestorePersistentString("MissingGroupName/");
+ EXPECT_EQ(trial, static_cast<FieldTrial *>(NULL));
+
+ trial = FieldTrial::RestorePersistentString("/MissingName");
+ EXPECT_EQ(trial, static_cast<FieldTrial *>(NULL));
+}
+
+TEST_F(FieldTrialTest, DuplicateRestore) {
+ FieldTrial* trial = new FieldTrial("Some name", 10);
+ trial->AppendGroup("Winner", 10);
+ EXPECT_EQ(trial->MakePersistentString(), "Some name/Winner");
+
+ // It is OK if we redundantly specify a winner.
+ EXPECT_EQ(trial, FieldTrial::RestorePersistentString("Some name/Winner"));
+
+ // But it is an error to try to change to a different winner.
+ EXPECT_EQ(FieldTrial::RestorePersistentString("Some name/Loser"),
+ static_cast<FieldTrial *>(NULL));
+}