Commit f58f2b25 authored by Steven Holte's avatar Steven Holte Committed by Commit Bot

Extract SyntheticTrialRegistry from MetricsService

Bug: 507665
Change-Id: If67ff3f6596cc9ca99cd99b0dc2b69ee442161c2
Reviewed-on: https://chromium-review.googlesource.com/571438Reviewed-by: default avatarJochen Eisinger <jochen@chromium.org>
Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Commit-Queue: Steven Holte <holte@chromium.org>
Cr-Commit-Position: refs/heads/master@{#487173}
parent fc4cbe38
...@@ -770,9 +770,9 @@ void ChromeBrowserMainParts::SetupFieldTrials() { ...@@ -770,9 +770,9 @@ void ChromeBrowserMainParts::SetupFieldTrials() {
void ChromeBrowserMainParts::SetupMetrics() { void ChromeBrowserMainParts::SetupMetrics() {
TRACE_EVENT0("startup", "ChromeBrowserMainParts::SetupMetrics"); TRACE_EVENT0("startup", "ChromeBrowserMainParts::SetupMetrics");
metrics::MetricsService* metrics = browser_process_->metrics_service(); metrics::MetricsService* metrics = browser_process_->metrics_service();
metrics->AddSyntheticTrialObserver( metrics->synthetic_trial_registry()->AddSyntheticTrialObserver(
variations::VariationsHttpHeaderProvider::GetInstance()); variations::VariationsHttpHeaderProvider::GetInstance());
metrics->AddSyntheticTrialObserver( metrics->synthetic_trial_registry()->AddSyntheticTrialObserver(
variations::SyntheticTrialsActiveGroupIdProvider::GetInstance()); variations::SyntheticTrialsActiveGroupIdProvider::GetInstance());
// Now that field trials have been created, initializes metrics recording. // Now that field trials have been created, initializes metrics recording.
metrics->InitializeMetricsRecordingState(); metrics->InitializeMetricsRecordingState();
......
...@@ -845,69 +845,6 @@ bool MetricsService::UmaMetricsProperlyShutdown() { ...@@ -845,69 +845,6 @@ bool MetricsService::UmaMetricsProperlyShutdown() {
return clean_shutdown_status_ == CLEANLY_SHUTDOWN; return clean_shutdown_status_ == CLEANLY_SHUTDOWN;
} }
void MetricsService::AddSyntheticTrialObserver(
variations::SyntheticTrialObserver* observer) {
synthetic_trial_observer_list_.AddObserver(observer);
if (!synthetic_trial_groups_.empty())
observer->OnSyntheticTrialsChanged(synthetic_trial_groups_);
}
void MetricsService::RemoveSyntheticTrialObserver(
variations::SyntheticTrialObserver* observer) {
synthetic_trial_observer_list_.RemoveObserver(observer);
}
void MetricsService::RegisterSyntheticFieldTrial(
const variations::SyntheticTrialGroup& trial) {
for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) {
if (synthetic_trial_groups_[i].id.name == trial.id.name) {
if (synthetic_trial_groups_[i].id.group != trial.id.group) {
synthetic_trial_groups_[i].id.group = trial.id.group;
synthetic_trial_groups_[i].start_time = base::TimeTicks::Now();
NotifySyntheticTrialObservers();
}
return;
}
}
variations::SyntheticTrialGroup trial_group = trial;
trial_group.start_time = base::TimeTicks::Now();
synthetic_trial_groups_.push_back(trial_group);
NotifySyntheticTrialObservers();
}
void MetricsService::RegisterSyntheticMultiGroupFieldTrial(
uint32_t trial_name_hash,
const std::vector<uint32_t>& group_name_hashes) {
auto has_same_trial_name =
[trial_name_hash](const variations::SyntheticTrialGroup& x) {
return x.id.name == trial_name_hash;
};
synthetic_trial_groups_.erase(
std::remove_if(synthetic_trial_groups_.begin(),
synthetic_trial_groups_.end(), has_same_trial_name),
synthetic_trial_groups_.end());
if (group_name_hashes.empty())
return;
variations::SyntheticTrialGroup trial_group(trial_name_hash,
group_name_hashes[0]);
trial_group.start_time = base::TimeTicks::Now();
for (uint32_t group_name_hash : group_name_hashes) {
// Note: Adding the trial group will copy it, so this re-uses the same
// |trial_group| struct for convenience (e.g. so start_time's all match).
trial_group.id.group = group_name_hash;
synthetic_trial_groups_.push_back(trial_group);
}
NotifySyntheticTrialObservers();
}
void MetricsService::GetCurrentSyntheticFieldTrialsForTesting(
std::vector<variations::ActiveGroupId>* synthetic_trials) {
GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(), synthetic_trials);
}
void MetricsService::RegisterMetricsProvider( void MetricsService::RegisterMetricsProvider(
std::unique_ptr<MetricsProvider> provider) { std::unique_ptr<MetricsProvider> provider) {
DCHECK_EQ(INITIALIZED, state_); DCHECK_EQ(INITIALIZED, state_);
...@@ -918,24 +855,6 @@ void MetricsService::CheckForClonedInstall() { ...@@ -918,24 +855,6 @@ void MetricsService::CheckForClonedInstall() {
state_manager_->CheckForClonedInstall(); state_manager_->CheckForClonedInstall();
} }
void MetricsService::NotifySyntheticTrialObservers() {
for (variations::SyntheticTrialObserver& observer :
synthetic_trial_observer_list_) {
observer.OnSyntheticTrialsChanged(synthetic_trial_groups_);
}
}
void MetricsService::GetSyntheticFieldTrialsOlderThan(
base::TimeTicks time,
std::vector<variations::ActiveGroupId>* synthetic_trials) {
DCHECK(synthetic_trials);
synthetic_trials->clear();
for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) {
if (synthetic_trial_groups_[i].start_time <= time)
synthetic_trials->push_back(synthetic_trial_groups_[i].id);
}
}
std::unique_ptr<MetricsLog> MetricsService::CreateLog( std::unique_ptr<MetricsLog> MetricsService::CreateLog(
MetricsLog::LogType log_type) { MetricsLog::LogType log_type) {
return base::MakeUnique<MetricsLog>(state_manager_->client_id(), session_id_, return base::MakeUnique<MetricsLog>(state_manager_->client_id(), session_id_,
...@@ -945,7 +864,8 @@ std::unique_ptr<MetricsLog> MetricsService::CreateLog( ...@@ -945,7 +864,8 @@ std::unique_ptr<MetricsLog> MetricsService::CreateLog(
void MetricsService::RecordCurrentEnvironment(MetricsLog* log) { void MetricsService::RecordCurrentEnvironment(MetricsLog* log) {
DCHECK(client_); DCHECK(client_);
std::vector<variations::ActiveGroupId> synthetic_trials; std::vector<variations::ActiveGroupId> synthetic_trials;
GetSyntheticFieldTrialsOlderThan(log->creation_time(), &synthetic_trials); synthetic_trial_registry_.GetSyntheticFieldTrialsOlderThan(
log->creation_time(), &synthetic_trials);
std::string serialized_environment = log->RecordEnvironment( std::string serialized_environment = log->RecordEnvironment(
metrics_providers_, synthetic_trials, GetInstallDate(), metrics_providers_, synthetic_trials, GetInstallDate(),
GetMetricsReportingEnabledDate()); GetMetricsReportingEnabledDate());
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include "base/metrics/histogram_flattener.h" #include "base/metrics/histogram_flattener.h"
#include "base/metrics/histogram_snapshot_manager.h" #include "base/metrics/histogram_snapshot_manager.h"
#include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics.h"
#include "base/observer_list.h"
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "build/build_config.h" #include "build/build_config.h"
...@@ -34,7 +33,7 @@ ...@@ -34,7 +33,7 @@
#include "components/metrics/metrics_provider.h" #include "components/metrics/metrics_provider.h"
#include "components/metrics/metrics_reporting_service.h" #include "components/metrics/metrics_reporting_service.h"
#include "components/metrics/net/network_metrics_provider.h" #include "components/metrics/net/network_metrics_provider.h"
#include "components/variations/synthetic_trials.h" #include "components/variations/synthetic_trial_registry.h"
class PrefService; class PrefService;
class PrefRegistrySimple; class PrefRegistrySimple;
...@@ -44,14 +43,9 @@ class HistogramSamples; ...@@ -44,14 +43,9 @@ class HistogramSamples;
class PrefService; class PrefService;
} }
namespace variations {
struct ActiveGroupId;
}
namespace metrics { namespace metrics {
class MetricsRotationScheduler; class MetricsRotationScheduler;
class MetricsServiceAccessor;
class MetricsServiceClient; class MetricsServiceClient;
class MetricsStateManager; class MetricsStateManager;
...@@ -162,18 +156,6 @@ class MetricsService : public base::HistogramFlattener { ...@@ -162,18 +156,6 @@ class MetricsService : public base::HistogramFlattener {
// This value should be true when process has completed shutdown. // This value should be true when process has completed shutdown.
static bool UmaMetricsProperlyShutdown(); static bool UmaMetricsProperlyShutdown();
// Public accessor that returns the list of synthetic field trials. It must
// only be used for testing.
void GetCurrentSyntheticFieldTrialsForTesting(
std::vector<variations::ActiveGroupId>* synthetic_trials);
// Adds an observer to be notified when the synthetic trials list changes.
void AddSyntheticTrialObserver(variations::SyntheticTrialObserver* observer);
// Removes an existing observer of synthetic trials list changes.
void RemoveSyntheticTrialObserver(
variations::SyntheticTrialObserver* observer);
// Register the specified |provider| to provide additional metrics into the // Register the specified |provider| to provide additional metrics into the
// UMA log. Should be called during MetricsService initialization only. // UMA log. Should be called during MetricsService initialization only.
void RegisterMetricsProvider(std::unique_ptr<MetricsProvider> provider); void RegisterMetricsProvider(std::unique_ptr<MetricsProvider> provider);
...@@ -194,6 +176,10 @@ class MetricsService : public base::HistogramFlattener { ...@@ -194,6 +176,10 @@ class MetricsService : public base::HistogramFlattener {
int message_size, int message_size,
bool is_cellular); bool is_cellular);
variations::SyntheticTrialRegistry* synthetic_trial_registry() {
return &synthetic_trial_registry_;
}
protected: protected:
// Exposed for testing. // Exposed for testing.
MetricsLogManager* log_manager() { return &log_manager_; } MetricsLogManager* log_manager() { return &log_manager_; }
...@@ -202,8 +188,6 @@ class MetricsService : public base::HistogramFlattener { ...@@ -202,8 +188,6 @@ class MetricsService : public base::HistogramFlattener {
} }
private: private:
friend class MetricsServiceAccessor;
// The MetricsService has a lifecycle that is stored as a state. // The MetricsService has a lifecycle that is stored as a state.
// See metrics_service.cc for description of this lifecycle. // See metrics_service.cc for description of this lifecycle.
enum State { enum State {
...@@ -227,27 +211,6 @@ class MetricsService : public base::HistogramFlattener { ...@@ -227,27 +211,6 @@ class MetricsService : public base::HistogramFlattener {
UNSET UNSET
}; };
typedef std::vector<variations::SyntheticTrialGroup> SyntheticTrialGroups;
// Registers a field trial name and group to be used to annotate a UMA report
// with a particular Chrome configuration state. A UMA report will be
// annotated with this trial group if and only if all events in the report
// were created after the trial is registered. Only one group name may be
// registered at a time for a given trial_name. Only the last group name that
// is registered for a given trial name will be recorded. The values passed
// in must not correspond to any real field trial in the code.
// Note: Should not be used to replace trials that were registered with
// RegisterMultiGroupSyntheticFieldTrial().
void RegisterSyntheticFieldTrial(
const variations::SyntheticTrialGroup& trial_group);
// Similar to RegisterSyntheticFieldTrial(), but registers a synthetic trial
// that has multiple active groups for a given trial name hash. Any previous
// groups registered for |trial_name_hash| will be replaced.
void RegisterSyntheticMultiGroupFieldTrial(
uint32_t trial_name_hash,
const std::vector<uint32_t>& group_name_hashes);
// Calls into the client to initialize some system profile metrics. // Calls into the client to initialize some system profile metrics.
void StartInitTask(); void StartInitTask();
...@@ -329,14 +292,6 @@ class MetricsService : public base::HistogramFlattener { ...@@ -329,14 +292,6 @@ class MetricsService : public base::HistogramFlattener {
// Records that the browser was shut down cleanly. // Records that the browser was shut down cleanly.
void LogCleanShutdown(bool end_completed); void LogCleanShutdown(bool end_completed);
// Notifies observers on a synthetic trial list change.
void NotifySyntheticTrialObservers();
// Returns a list of synthetic field trials that are older than |time|.
void GetSyntheticFieldTrialsOlderThan(
base::TimeTicks time,
std::vector<variations::ActiveGroupId>* synthetic_trials);
// Creates a new MetricsLog instance with the given |log_type|. // Creates a new MetricsLog instance with the given |log_type|.
std::unique_ptr<MetricsLog> CreateLog(MetricsLog::LogType log_type); std::unique_ptr<MetricsLog> CreateLog(MetricsLog::LogType log_type);
...@@ -418,12 +373,7 @@ class MetricsService : public base::HistogramFlattener { ...@@ -418,12 +373,7 @@ class MetricsService : public base::HistogramFlattener {
// Stores the time of the last call to |GetUptimes()|. // Stores the time of the last call to |GetUptimes()|.
base::TimeTicks last_updated_time_; base::TimeTicks last_updated_time_;
// Field trial groups that map to Chrome configuration states. variations::SyntheticTrialRegistry synthetic_trial_registry_;
SyntheticTrialGroups synthetic_trial_groups_;
// List of observers of |synthetic_trial_groups_| changes.
base::ObserverList<variations::SyntheticTrialObserver>
synthetic_trial_observer_list_;
// Redundant marker to check that we completed our shutdown, and set the // Redundant marker to check that we completed our shutdown, and set the
// exited-cleanly bit in the prefs. // exited-cleanly bit in the prefs.
...@@ -432,11 +382,6 @@ class MetricsService : public base::HistogramFlattener { ...@@ -432,11 +382,6 @@ class MetricsService : public base::HistogramFlattener {
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, IsPluginProcess); FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, IsPluginProcess);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest,
PermutedEntropyCacheClearedWhenLowEntropyReset); PermutedEntropyCacheClearedWhenLowEntropyReset);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, RegisterSyntheticTrial);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest,
RegisterSyntheticMultiGroupFieldTrial);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest,
GetSyntheticFieldTrialActiveGroups);
base::ThreadChecker thread_checker_; base::ThreadChecker thread_checker_;
......
...@@ -46,8 +46,9 @@ bool MetricsServiceAccessor::RegisterSyntheticMultiGroupFieldTrial( ...@@ -46,8 +46,9 @@ bool MetricsServiceAccessor::RegisterSyntheticMultiGroupFieldTrial(
if (!metrics_service) if (!metrics_service)
return false; return false;
metrics_service->RegisterSyntheticMultiGroupFieldTrial(HashName(trial_name), metrics_service->synthetic_trial_registry()
group_name_hashes); ->RegisterSyntheticMultiGroupFieldTrial(HashName(trial_name),
group_name_hashes);
return true; return true;
} }
...@@ -69,7 +70,8 @@ bool MetricsServiceAccessor::RegisterSyntheticFieldTrialWithNameAndGroupHash( ...@@ -69,7 +70,8 @@ bool MetricsServiceAccessor::RegisterSyntheticFieldTrialWithNameAndGroupHash(
return false; return false;
variations::SyntheticTrialGroup trial_group(trial_name_hash, group_name_hash); variations::SyntheticTrialGroup trial_group(trial_name_hash, group_name_hash);
metrics_service->RegisterSyntheticFieldTrial(trial_group); metrics_service->synthetic_trial_registry()->RegisterSyntheticFieldTrial(
trial_group);
return true; return true;
} }
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "base/test/test_simple_task_runner.h" #include "base/test/test_simple_task_runner.h"
#include "base/threading/platform_thread.h" #include "base/threading/platform_thread.h"
...@@ -33,9 +32,6 @@ ...@@ -33,9 +32,6 @@
#include "components/metrics/test_metrics_provider.h" #include "components/metrics/test_metrics_provider.h"
#include "components/metrics/test_metrics_service_client.h" #include "components/metrics/test_metrics_service_client.h"
#include "components/prefs/testing_pref_service.h" #include "components/prefs/testing_pref_service.h"
#include "components/variations/active_field_trials.h"
#include "components/variations/metrics_util.h"
#include "components/variations/synthetic_trials_active_group_id_provider.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/zlib/google/compression_utils.h" #include "third_party/zlib/google/compression_utils.h"
...@@ -117,29 +113,6 @@ class MetricsServiceTest : public testing::Test { ...@@ -117,29 +113,6 @@ class MetricsServiceTest : public testing::Test {
enabled_state_provider_->set_enabled(true); enabled_state_provider_->set_enabled(true);
} }
// 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));
}
}
// 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<variations::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 variations::ActiveGroupId& trial : synthetic_trials) {
if (trial.name == trial_name_hash && trial.group == trial_group_hash)
return true;
}
return false;
}
// Finds a histogram with the specified |name_hash| in |histograms|. // Finds a histogram with the specified |name_hash| in |histograms|.
const base::HistogramBase* FindHistogram( const base::HistogramBase* FindHistogram(
const base::StatisticsRecorder::Histograms& histograms, const base::StatisticsRecorder::Histograms& histograms,
...@@ -345,119 +318,6 @@ TEST_F(MetricsServiceTest, InitialStabilityLogAfterCrash) { ...@@ -345,119 +318,6 @@ TEST_F(MetricsServiceTest, InitialStabilityLogAfterCrash) {
EXPECT_EQ(1, uma_log.system_profile().stability().crash_count()); EXPECT_EQ(1, uma_log.system_profile().stability().crash_count());
} }
TEST_F(MetricsServiceTest, RegisterSyntheticTrial) {
TestMetricsServiceClient client;
MetricsService service(GetMetricsStateManager(), &client, GetLocalState());
// Add two synthetic trials and confirm that they show up in the list.
variations::SyntheticTrialGroup trial1(HashName("TestTrial1"),
HashName("Group1"));
service.RegisterSyntheticFieldTrial(trial1);
variations::SyntheticTrialGroup trial2(HashName("TestTrial2"),
HashName("Group2"));
service.RegisterSyntheticFieldTrial(trial2);
// Ensure that time has advanced by at least a tick before proceeding.
WaitUntilTimeChanges(base::TimeTicks::Now());
service.log_manager_.BeginLoggingWithLog(std::unique_ptr<MetricsLog>(
new MetricsLog("clientID", 1, MetricsLog::INITIAL_STABILITY_LOG, &client,
GetLocalState())));
// 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<variations::ActiveGroupId> synthetic_trials;
service.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.
variations::SyntheticTrialGroup trial3(HashName("TestTrial1"),
HashName("Group2"));
service.RegisterSyntheticFieldTrial(trial3);
service.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.
variations::SyntheticTrialGroup trial4(HashName("TestTrial3"),
HashName("Group3"));
service.RegisterSyntheticFieldTrial(trial4);
service.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.
service.log_manager_.FinishCurrentLog(service.log_store());
service.log_manager_.BeginLoggingWithLog(
std::unique_ptr<MetricsLog>(new MetricsLog(
"clientID", 1, MetricsLog::ONGOING_LOG, &client, GetLocalState())));
service.GetSyntheticFieldTrialsOlderThan(
service.log_manager_.current_log()->creation_time(), &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"));
service.log_manager_.FinishCurrentLog(service.log_store());
}
TEST_F(MetricsServiceTest, RegisterSyntheticMultiGroupFieldTrial) {
TestMetricsServiceClient client;
MetricsService service(GetMetricsStateManager(), &client, GetLocalState());
// 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")};
service.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
group_name_hashes);
// Ensure that time has advanced by at least a tick before proceeding.
WaitUntilTimeChanges(base::TimeTicks::Now());
service.log_manager_.BeginLoggingWithLog(std::unique_ptr<MetricsLog>(
new MetricsLog("clientID", 1, MetricsLog::INITIAL_STABILITY_LOG, &client,
GetLocalState())));
std::vector<variations::ActiveGroupId> synthetic_trials;
service.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")};
service.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
group_name_hashes);
// Ensure that time has advanced by at least a tick before proceeding.
WaitUntilTimeChanges(base::TimeTicks::Now());
service.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();
service.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
group_name_hashes);
// Ensure that time has advanced by at least a tick before proceeding.
WaitUntilTimeChanges(base::TimeTicks::Now());
service.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
&synthetic_trials);
service.log_manager_.FinishCurrentLog(service.log_store());
}
TEST_F(MetricsServiceTest, TEST_F(MetricsServiceTest,
MetricsProviderOnRecordingDisabledCalledOnInitialStop) { MetricsProviderOnRecordingDisabledCalledOnInitialStop) {
TestMetricsServiceClient client; TestMetricsServiceClient client;
...@@ -538,39 +398,4 @@ TEST_F(MetricsServiceTest, SplitRotation) { ...@@ -538,39 +398,4 @@ TEST_F(MetricsServiceTest, SplitRotation) {
EXPECT_EQ(1U, task_runner_->NumPendingTasks()); EXPECT_EQ(1U, task_runner_->NumPendingTasks());
} }
TEST_F(MetricsServiceTest, GetSyntheticFieldTrialActiveGroups) {
TestMetricsServiceClient client;
MetricsService service(GetMetricsStateManager(), &client, GetLocalState());
// Instantiate and setup the corresponding singleton observer which tracks the
// creation of all SyntheticTrialGroups.
service.AddSyntheticTrialObserver(
variations::SyntheticTrialsActiveGroupIdProvider::GetInstance());
// Add two synthetic trials and confirm that they show up in the list.
variations::SyntheticTrialGroup trial1(HashName("TestTrial1"),
HashName("Group1"));
service.RegisterSyntheticFieldTrial(trial1);
variations::SyntheticTrialGroup trial2(HashName("TestTrial2"),
HashName("Group2"));
service.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;
variations::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 metrics } // namespace metrics
...@@ -40,6 +40,8 @@ static_library("variations") { ...@@ -40,6 +40,8 @@ static_library("variations") {
"proto/variations_seed.proto", "proto/variations_seed.proto",
"study_filtering.cc", "study_filtering.cc",
"study_filtering.h", "study_filtering.h",
"synthetic_trial_registry.cc",
"synthetic_trial_registry.h",
"synthetic_trials.cc", "synthetic_trials.cc",
"synthetic_trials.h", "synthetic_trials.h",
"synthetic_trials_active_group_id_provider.cc", "synthetic_trials_active_group_id_provider.cc",
...@@ -127,6 +129,7 @@ source_set("unit_tests") { ...@@ -127,6 +129,7 @@ source_set("unit_tests") {
"metrics_util_unittest.cc", "metrics_util_unittest.cc",
"net/variations_http_headers_unittest.cc", "net/variations_http_headers_unittest.cc",
"study_filtering_unittest.cc", "study_filtering_unittest.cc",
"synthetic_trial_registry_unittest.cc",
"variations_associated_data_unittest.cc", "variations_associated_data_unittest.cc",
"variations_http_header_provider_unittest.cc", "variations_http_header_provider_unittest.cc",
"variations_request_scheduler_unittest.cc", "variations_request_scheduler_unittest.cc",
......
// 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"
namespace variations {
SyntheticTrialRegistry::SyntheticTrialRegistry() = default;
SyntheticTrialRegistry::~SyntheticTrialRegistry() = default;
void SyntheticTrialRegistry::AddSyntheticTrialObserver(
SyntheticTrialObserver* observer) {
synthetic_trial_observer_list_.AddObserver(observer);
if (!synthetic_trial_groups_.empty())
observer->OnSyntheticTrialsChanged(synthetic_trial_groups_);
}
void SyntheticTrialRegistry::RemoveSyntheticTrialObserver(
SyntheticTrialObserver* observer) {
synthetic_trial_observer_list_.RemoveObserver(observer);
}
void SyntheticTrialRegistry::RegisterSyntheticFieldTrial(
const SyntheticTrialGroup& trial) {
for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) {
if (synthetic_trial_groups_[i].id.name == trial.id.name) {
if (synthetic_trial_groups_[i].id.group != trial.id.group) {
synthetic_trial_groups_[i].id.group = trial.id.group;
synthetic_trial_groups_[i].start_time = base::TimeTicks::Now();
NotifySyntheticTrialObservers();
}
return;
}
}
SyntheticTrialGroup trial_group = trial;
trial_group.start_time = base::TimeTicks::Now();
synthetic_trial_groups_.push_back(trial_group);
NotifySyntheticTrialObservers();
}
void SyntheticTrialRegistry::RegisterSyntheticMultiGroupFieldTrial(
uint32_t trial_name_hash,
const std::vector<uint32_t>& group_name_hashes) {
auto has_same_trial_name = [trial_name_hash](const SyntheticTrialGroup& x) {
return x.id.name == trial_name_hash;
};
synthetic_trial_groups_.erase(
std::remove_if(synthetic_trial_groups_.begin(),
synthetic_trial_groups_.end(), has_same_trial_name),
synthetic_trial_groups_.end());
if (group_name_hashes.empty())
return;
SyntheticTrialGroup trial_group(trial_name_hash, group_name_hashes[0]);
trial_group.start_time = base::TimeTicks::Now();
for (uint32_t group_name_hash : group_name_hashes) {
// Note: Adding the trial group will copy it, so this re-uses the same
// |trial_group| struct for convenience (e.g. so start_time's all match).
trial_group.id.group = group_name_hash;
synthetic_trial_groups_.push_back(trial_group);
}
NotifySyntheticTrialObservers();
}
void SyntheticTrialRegistry::NotifySyntheticTrialObservers() {
for (SyntheticTrialObserver& observer : synthetic_trial_observer_list_) {
observer.OnSyntheticTrialsChanged(synthetic_trial_groups_);
}
}
void SyntheticTrialRegistry::GetSyntheticFieldTrialsOlderThan(
base::TimeTicks time,
std::vector<ActiveGroupId>* synthetic_trials) {
DCHECK(synthetic_trials);
synthetic_trials->clear();
for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) {
if (synthetic_trial_groups_[i].start_time <= time)
synthetic_trials->push_back(synthetic_trial_groups_[i].id);
}
}
} // namespace variations
// 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.
#ifndef COMPONENTS_VARIATIONS_SYNTHETIC_TRIAL_REGISTRY_H_
#define COMPONENTS_VARIATIONS_SYNTHETIC_TRIAL_REGISTRY_H_
#include <vector>
#include "base/observer_list.h"
#include "components/variations/synthetic_trials.h"
namespace metrics {
class MetricsServiceAccessor;
class MetricsService;
} // namespace metrics
namespace variations {
struct ActiveGroupId;
class SyntheticTrialRegistry {
public:
SyntheticTrialRegistry();
~SyntheticTrialRegistry();
// Adds an observer to be notified when the synthetic trials list changes.
void AddSyntheticTrialObserver(SyntheticTrialObserver* observer);
// Removes an existing observer of synthetic trials list changes.
void RemoveSyntheticTrialObserver(SyntheticTrialObserver* observer);
private:
friend metrics::MetricsServiceAccessor;
friend metrics::MetricsService;
FRIEND_TEST_ALL_PREFIXES(SyntheticTrialRegistryTest, RegisterSyntheticTrial);
FRIEND_TEST_ALL_PREFIXES(SyntheticTrialRegistryTest,
RegisterSyntheticMultiGroupFieldTrial);
FRIEND_TEST_ALL_PREFIXES(SyntheticTrialRegistryTest,
GetSyntheticFieldTrialActiveGroups);
// Registers a field trial name and group to be used to annotate a UMA report
// with a particular Chrome configuration state. A UMA report will be
// annotated with this trial group if and only if all events in the report
// were created after the trial is registered. Only one group name may be
// registered at a time for a given trial_name. Only the last group name that
// is registered for a given trial name will be recorded. The values passed
// in must not correspond to any real field trial in the code.
// Note: Should not be used to replace trials that were registered with
// RegisterMultiGroupSyntheticFieldTrial().
void RegisterSyntheticFieldTrial(const SyntheticTrialGroup& trial_group);
// Similar to RegisterSyntheticFieldTrial(), but registers a synthetic trial
// that has multiple active groups for a given trial name hash. Any previous
// groups registered for |trial_name_hash| will be replaced.
void RegisterSyntheticMultiGroupFieldTrial(
uint32_t trial_name_hash,
const std::vector<uint32_t>& group_name_hashes);
// Returns a list of synthetic field trials that are older than |time|.
void GetSyntheticFieldTrialsOlderThan(
base::TimeTicks time,
std::vector<ActiveGroupId>* synthetic_trials);
// Notifies observers on a synthetic trial list change.
void NotifySyntheticTrialObservers();
// Field trial groups that map to Chrome configuration states.
std::vector<SyntheticTrialGroup> synthetic_trial_groups_;
// List of observers of |synthetic_trial_groups_| changes.
base::ObserverList<SyntheticTrialObserver> synthetic_trial_observer_list_;
};
} // namespace variations
#endif // COMPONENTS_VARIATIONS_SYNTHETIC_TRIAL_REGISTRY_H_
// 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 "base/strings/stringprintf.h"
#include "components/variations/active_field_trials.h"
#include "components/variations/metrics_util.h"
#include "components/variations/synthetic_trials_active_group_id_provider.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace variations {
namespace {
class SyntheticTrialRegistryTest : public ::testing::Test {
public:
// 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 = metrics::HashName(trial_name);
uint32_t trial_group_hash = metrics::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));
}
}
};
} // namespace
TEST_F(SyntheticTrialRegistryTest, RegisterSyntheticTrial) {
SyntheticTrialRegistry registry;
// Add two synthetic trials and confirm that they show up in the list.
SyntheticTrialGroup trial1(metrics::HashName("TestTrial1"),
metrics::HashName("Group1"));
registry.RegisterSyntheticFieldTrial(trial1);
SyntheticTrialGroup trial2(metrics::HashName("TestTrial2"),
metrics::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(metrics::HashName("TestTrial1"),
metrics::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(metrics::HashName("TestTrial3"),
metrics::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 = metrics::HashName("TestTrial1");
std::vector<uint32_t> group_name_hashes = {metrics::HashName("A"),
metrics::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 = {metrics::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(metrics::HashName("TestTrial1"),
metrics::HashName("Group1"));
registry.RegisterSyntheticFieldTrial(trial1);
SyntheticTrialGroup trial2(metrics::HashName("TestTrial2"),
metrics::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
...@@ -275,7 +275,7 @@ void IOSChromeMainParts::SetupFieldTrials() { ...@@ -275,7 +275,7 @@ void IOSChromeMainParts::SetupFieldTrials() {
void IOSChromeMainParts::SetupMetrics() { void IOSChromeMainParts::SetupMetrics() {
metrics::MetricsService* metrics = application_context_->GetMetricsService(); metrics::MetricsService* metrics = application_context_->GetMetricsService();
metrics->AddSyntheticTrialObserver( metrics->synthetic_trial_registry()->AddSyntheticTrialObserver(
variations::VariationsHttpHeaderProvider::GetInstance()); variations::VariationsHttpHeaderProvider::GetInstance());
// Now that field trials have been created, initializes metrics recording. // Now that field trials have been created, initializes metrics recording.
metrics->InitializeMetricsRecordingState(); metrics->InitializeMetricsRecordingState();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment