diff options
Diffstat (limited to 'base')
-rw-r--r-- | base/field_trial.cc | 40 | ||||
-rw-r--r-- | base/field_trial.h | 17 | ||||
-rw-r--r-- | base/field_trial_unittest.cc | 41 |
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)); +} |