blob: 403c13423641b4615032807a0680266081e92b76 [file] [log] [blame]
// Copyright 2017 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/synthetic_trial_registry.h"
#include <string>
#include "base/message_loop/message_loop.h"
#include "base/metrics/field_trial.h"
#include "base/strings/stringprintf.h"
#include "components/variations/active_field_trials.h"
#include "components/variations/hashing.h"
#include "components/variations/synthetic_trials_active_group_id_provider.h"
#include "components/variations/variations_crash_keys.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace variations {
namespace {
class SyntheticTrialRegistryTest : public ::testing::Test {
public:
SyntheticTrialRegistryTest() : field_trial_list_(nullptr) { InitCrashKeys(); }
~SyntheticTrialRegistryTest() override { ClearCrashKeysInstanceForTesting(); }
// Returns true if there is a synthetic trial in the given vector that matches
// the given trial name and trial group; returns false otherwise.
bool HasSyntheticTrial(const std::vector<ActiveGroupId>& synthetic_trials,
const std::string& trial_name,
const std::string& trial_group) {
uint32_t trial_name_hash = HashName(trial_name);
uint32_t trial_group_hash = HashName(trial_group);
for (const ActiveGroupId& trial : synthetic_trials) {
if (trial.name == trial_name_hash && trial.group == trial_group_hash)
return true;
}
return false;
}
// Waits until base::TimeTicks::Now() no longer equals |value|. This should
// take between 1-15ms per the documented resolution of base::TimeTicks.
void WaitUntilTimeChanges(const base::TimeTicks& value) {
while (base::TimeTicks::Now() == value) {
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
}
}
private:
base::MessageLoop loop_;
base::FieldTrialList field_trial_list_;
DISALLOW_COPY_AND_ASSIGN(SyntheticTrialRegistryTest);
};
} // namespace
TEST_F(SyntheticTrialRegistryTest, RegisterSyntheticTrial) {
SyntheticTrialRegistry registry;
// Add two synthetic trials and confirm that they show up in the list.
SyntheticTrialGroup trial1(HashName("TestTrial1"), HashName("Group1"));
registry.RegisterSyntheticFieldTrial(trial1);
SyntheticTrialGroup trial2(HashName("TestTrial2"), HashName("Group2"));
registry.RegisterSyntheticFieldTrial(trial2);
// Ensure that time has advanced by at least a tick before proceeding.
WaitUntilTimeChanges(base::TimeTicks::Now());
// Save the time when the log was started (it's okay for this to be greater
// than the time recorded by the above call since it's used to ensure the
// value changes).
const base::TimeTicks begin_log_time = base::TimeTicks::Now();
std::vector<ActiveGroupId> synthetic_trials;
registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
&synthetic_trials);
EXPECT_EQ(2U, synthetic_trials.size());
EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "Group1"));
EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
// Ensure that time has advanced by at least a tick before proceeding.
WaitUntilTimeChanges(begin_log_time);
// Change the group for the first trial after the log started.
SyntheticTrialGroup trial3(HashName("TestTrial1"), HashName("Group2"));
registry.RegisterSyntheticFieldTrial(trial3);
registry.GetSyntheticFieldTrialsOlderThan(begin_log_time, &synthetic_trials);
EXPECT_EQ(1U, synthetic_trials.size());
EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
// Add a new trial after the log started and confirm that it doesn't show up.
SyntheticTrialGroup trial4(HashName("TestTrial3"), HashName("Group3"));
registry.RegisterSyntheticFieldTrial(trial4);
registry.GetSyntheticFieldTrialsOlderThan(begin_log_time, &synthetic_trials);
EXPECT_EQ(1U, synthetic_trials.size());
EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
// Ensure that time has advanced by at least a tick before proceeding.
WaitUntilTimeChanges(base::TimeTicks::Now());
// Start a new log and ensure all three trials appear in it.
registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
&synthetic_trials);
EXPECT_EQ(3U, synthetic_trials.size());
EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "Group2"));
EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial3", "Group3"));
}
TEST_F(SyntheticTrialRegistryTest, RegisterSyntheticMultiGroupFieldTrial) {
SyntheticTrialRegistry registry;
// Register a synthetic trial TestTrial1 with groups A and B.
uint32_t trial_name_hash = HashName("TestTrial1");
std::vector<uint32_t> group_name_hashes = {HashName("A"), HashName("B")};
registry.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
group_name_hashes);
// Ensure that time has advanced by at least a tick before proceeding.
WaitUntilTimeChanges(base::TimeTicks::Now());
std::vector<ActiveGroupId> synthetic_trials;
registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
&synthetic_trials);
EXPECT_EQ(2U, synthetic_trials.size());
EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "A"));
EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "B"));
// Change the group for the trial to a single group.
group_name_hashes = {HashName("X")};
registry.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
group_name_hashes);
// Ensure that time has advanced by at least a tick before proceeding.
WaitUntilTimeChanges(base::TimeTicks::Now());
registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
&synthetic_trials);
EXPECT_EQ(1U, synthetic_trials.size());
EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "X"));
// Register a trial with no groups, which should effectively remove the trial.
group_name_hashes.clear();
registry.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
group_name_hashes);
// Ensure that time has advanced by at least a tick before proceeding.
WaitUntilTimeChanges(base::TimeTicks::Now());
registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
&synthetic_trials);
}
TEST_F(SyntheticTrialRegistryTest, GetSyntheticFieldTrialActiveGroups) {
SyntheticTrialRegistry registry;
// Instantiate and setup the corresponding singleton observer which tracks the
// creation of all SyntheticTrialGroups.
registry.AddSyntheticTrialObserver(
SyntheticTrialsActiveGroupIdProvider::GetInstance());
// Add two synthetic trials and confirm that they show up in the list.
SyntheticTrialGroup trial1(HashName("TestTrial1"), HashName("Group1"));
registry.RegisterSyntheticFieldTrial(trial1);
SyntheticTrialGroup trial2(HashName("TestTrial2"), HashName("Group2"));
registry.RegisterSyntheticFieldTrial(trial2);
// Ensure that time has advanced by at least a tick before proceeding.
WaitUntilTimeChanges(base::TimeTicks::Now());
// Now get the list of currently active groups.
std::vector<std::string> output;
GetSyntheticTrialGroupIdsAsString(&output);
EXPECT_EQ(2U, output.size());
std::string trial1_hash =
base::StringPrintf("%x-%x", trial1.id.name, trial1.id.group);
EXPECT_TRUE(base::ContainsValue(output, trial1_hash));
std::string trial2_hash =
base::StringPrintf("%x-%x", trial2.id.name, trial2.id.group);
EXPECT_TRUE(base::ContainsValue(output, trial2_hash));
}
} // namespace variations