summaryrefslogtreecommitdiff
path: root/chromium/components/variations/service
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-09-18 14:34:04 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-10-04 11:15:27 +0000
commite6430e577f105ad8813c92e75c54660c4985026e (patch)
tree88115e5d1fb471fea807111924dcccbeadbf9e4f /chromium/components/variations/service
parent53d399fe6415a96ea6986ec0d402a9c07da72453 (diff)
downloadqtwebengine-chromium-e6430e577f105ad8813c92e75c54660c4985026e.tar.gz
BASELINE: Update Chromium to 61.0.3163.99
Change-Id: I8452f34574d88ca2b27af9bd56fc9ff3f16b1367 Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/components/variations/service')
-rw-r--r--chromium/components/variations/service/BUILD.gn5
-rw-r--r--chromium/components/variations/service/ui_string_overrider_unittest.cc7
-rw-r--r--chromium/components/variations/service/variations_field_trial_creator.cc328
-rw-r--r--chromium/components/variations/service/variations_field_trial_creator.h123
-rw-r--r--chromium/components/variations/service/variations_field_trial_creator_unittest.cc276
-rw-r--r--chromium/components/variations/service/variations_service.cc451
-rw-r--r--chromium/components/variations/service/variations_service.h43
-rw-r--r--chromium/components/variations/service/variations_service_client.h14
-rw-r--r--chromium/components/variations/service/variations_service_unittest.cc498
9 files changed, 1206 insertions, 539 deletions
diff --git a/chromium/components/variations/service/BUILD.gn b/chromium/components/variations/service/BUILD.gn
index d2ea94f1029..f0e3cc719cc 100644
--- a/chromium/components/variations/service/BUILD.gn
+++ b/chromium/components/variations/service/BUILD.gn
@@ -6,6 +6,8 @@ static_library("service") {
sources = [
"ui_string_overrider.cc",
"ui_string_overrider.h",
+ "variations_field_trial_creator.cc",
+ "variations_field_trial_creator.h",
"variations_service.cc",
"variations_service.h",
"variations_service_client.h",
@@ -31,6 +33,7 @@ source_set("unit_tests") {
testonly = true
sources = [
"ui_string_overrider_unittest.cc",
+ "variations_field_trial_creator_unittest.cc",
"variations_service_unittest.cc",
]
@@ -38,6 +41,8 @@ source_set("unit_tests") {
":service",
"//base",
"//base/test:test_support",
+ "//components/metrics",
+ "//components/metrics:test_support",
"//components/prefs:test_support",
"//components/variations",
"//components/variations/proto",
diff --git a/chromium/components/variations/service/ui_string_overrider_unittest.cc b/chromium/components/variations/service/ui_string_overrider_unittest.cc
index 0f4ab527528..3ceef785d31 100644
--- a/chromium/components/variations/service/ui_string_overrider_unittest.cc
+++ b/chromium/components/variations/service/ui_string_overrider_unittest.cc
@@ -10,8 +10,7 @@
#include "base/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
-namespace chrome_variations {
-
+namespace variations {
namespace {
const size_t kNumResources = 4;
@@ -42,7 +41,7 @@ class UIStringOverriderTest : public ::testing::Test {
}
private:
- variations::UIStringOverrider provider_;
+ UIStringOverrider provider_;
DISALLOW_COPY_AND_ASSIGN(UIStringOverriderTest);
};
@@ -61,4 +60,4 @@ TEST_F(UIStringOverriderTest, LookupFound) {
EXPECT_EQ(kResourceIndices[i], GetResourceIndex(kResourceHashes[i]));
}
-} // namespace chrome_variations
+} // namespace variations
diff --git a/chromium/components/variations/service/variations_field_trial_creator.cc b/chromium/components/variations/service/variations_field_trial_creator.cc
new file mode 100644
index 00000000000..d01415249b3
--- /dev/null
+++ b/chromium/components/variations/service/variations_field_trial_creator.cc
@@ -0,0 +1,328 @@
+// Copyright (c) 2012 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/service/variations_field_trial_creator.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/build_time.h"
+#include "base/command_line.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/sys_info.h"
+#include "base/version.h"
+#include "build/build_config.h"
+#include "components/prefs/pref_service.h"
+#include "components/variations/pref_names.h"
+#include "components/variations/proto/variations_seed.pb.h"
+#include "components/variations/service/variations_service_client.h"
+#include "components/variations/variations_seed_processor.h"
+#include "components/variations/variations_switches.h"
+#include "ui/base/device_form_factor.h"
+
+namespace variations {
+
+// Maximum age permitted for a variations seed, in days.
+const int kMaxVariationsSeedAgeDays = 30;
+
+enum VariationsSeedExpiry {
+ VARIATIONS_SEED_EXPIRY_NOT_EXPIRED,
+ VARIATIONS_SEED_EXPIRY_FETCH_TIME_MISSING,
+ VARIATIONS_SEED_EXPIRY_EXPIRED,
+ VARIATIONS_SEED_EXPIRY_ENUM_SIZE,
+};
+
+// Gets current form factor and converts it from enum DeviceFormFactor to enum
+// Study_FormFactor.
+Study::FormFactor GetCurrentFormFactor() {
+ switch (ui::GetDeviceFormFactor()) {
+ case ui::DEVICE_FORM_FACTOR_PHONE:
+ return Study::PHONE;
+ case ui::DEVICE_FORM_FACTOR_TABLET:
+ return Study::TABLET;
+ case ui::DEVICE_FORM_FACTOR_DESKTOP:
+ return Study::DESKTOP;
+ }
+ NOTREACHED();
+ return Study::DESKTOP;
+}
+
+// Gets the hardware class and returns it as a string. This returns an empty
+// string if the client is not ChromeOS.
+std::string GetHardwareClass() {
+#if defined(OS_CHROMEOS)
+ return base::SysInfo::GetLsbReleaseBoard();
+#endif // OS_CHROMEOS
+ return std::string();
+}
+
+// Returns the date that should be used by the VariationsSeedProcessor to do
+// expiry and start date checks.
+base::Time GetReferenceDateForExpiryChecks(PrefService* local_state) {
+ const int64_t date_value = local_state->GetInt64(prefs::kVariationsSeedDate);
+ const base::Time seed_date = base::Time::FromInternalValue(date_value);
+ const base::Time build_time = base::GetBuildTime();
+ // Use the build time for date checks if either the seed date is invalid or
+ // the build time is newer than the seed date.
+ base::Time reference_date = seed_date;
+ if (seed_date.is_null() || seed_date < build_time)
+ reference_date = build_time;
+ return reference_date;
+}
+
+// Wrapper around channel checking, used to enable channel mocking for
+// testing. If the current browser channel is not UNKNOWN, this will return
+// that channel value. Otherwise, if the fake channel flag is provided, this
+// will return the fake channel. Failing that, this will return the UNKNOWN
+// channel.
+Study::Channel GetChannelForVariations(version_info::Channel product_channel) {
+ switch (product_channel) {
+ case version_info::Channel::CANARY:
+ return Study::CANARY;
+ case version_info::Channel::DEV:
+ return Study::DEV;
+ case version_info::Channel::BETA:
+ return Study::BETA;
+ case version_info::Channel::STABLE:
+ return Study::STABLE;
+ case version_info::Channel::UNKNOWN:
+ break;
+ }
+ const std::string forced_channel =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kFakeVariationsChannel);
+ if (forced_channel == "stable")
+ return Study::STABLE;
+ if (forced_channel == "beta")
+ return Study::BETA;
+ if (forced_channel == "dev")
+ return Study::DEV;
+ if (forced_channel == "canary")
+ return Study::CANARY;
+ DVLOG(1) << "Invalid channel provided: " << forced_channel;
+ return Study::UNKNOWN;
+}
+
+// Records UMA histogram with the result of the variations seed expiry check.
+void RecordCreateTrialsSeedExpiry(VariationsSeedExpiry expiry_check_result) {
+ UMA_HISTOGRAM_ENUMERATION("Variations.CreateTrials.SeedExpiry",
+ expiry_check_result,
+ VARIATIONS_SEED_EXPIRY_ENUM_SIZE);
+}
+
+VariationsFieldTrialCreator::VariationsFieldTrialCreator(
+ PrefService* local_state,
+ VariationsServiceClient* client,
+ const UIStringOverrider& ui_string_overrider)
+ : client_(client),
+ ui_string_overrider_(ui_string_overrider),
+ seed_store_(local_state),
+ create_trials_from_seed_called_(false) {}
+
+VariationsFieldTrialCreator::~VariationsFieldTrialCreator() {}
+
+std::string VariationsFieldTrialCreator::GetLatestCountry() const {
+ const std::string override_country =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kVariationsOverrideCountry);
+ return !override_country.empty()
+ ? override_country
+ : local_state()->GetString(prefs::kVariationsCountry);
+}
+
+bool VariationsFieldTrialCreator::CreateTrialsFromSeed(
+ std::unique_ptr<const base::FieldTrial::EntropyProvider>
+ low_entropy_provider,
+ base::FeatureList* feature_list) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ CHECK(!create_trials_from_seed_called_);
+
+ create_trials_from_seed_called_ = true;
+
+ VariationsSeed seed;
+ if (!LoadSeed(&seed))
+ return false;
+
+ const int64_t last_fetch_time_internal =
+ local_state()->GetInt64(prefs::kVariationsLastFetchTime);
+ const base::Time last_fetch_time =
+ base::Time::FromInternalValue(last_fetch_time_internal);
+ if (last_fetch_time.is_null()) {
+ // If the last fetch time is missing and we have a seed, then this must be
+ // the first run of Chrome. Store the current time as the last fetch time.
+ RecordLastFetchTime();
+ RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_FETCH_TIME_MISSING);
+ } else {
+ // Reject the seed if it is more than 30 days old.
+ const base::TimeDelta seed_age = base::Time::Now() - last_fetch_time;
+ if (seed_age.InDays() > kMaxVariationsSeedAgeDays) {
+ RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_EXPIRED);
+ return false;
+ }
+ RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_NOT_EXPIRED);
+ }
+
+ const base::Version current_version(version_info::GetVersionNumber());
+ if (!current_version.IsValid())
+ return false;
+
+ std::unique_ptr<ClientFilterableState> client_state =
+ GetClientFilterableStateForVersion(current_version);
+
+ // Note that passing |&ui_string_overrider_| via base::Unretained below is
+ // safe because the callback is executed synchronously. It is not possible
+ // to pass UIStringOverrider itself to VariationSeedProcessor as variations
+ // components should not depends on //ui/base.
+ VariationsSeedProcessor().CreateTrialsFromSeed(
+ seed, *client_state,
+ base::Bind(&UIStringOverrider::OverrideUIString,
+ base::Unretained(&ui_string_overrider_)),
+ low_entropy_provider.get(), feature_list);
+
+ const base::Time now = base::Time::Now();
+
+ // Log the "freshness" of the seed that was just used. The freshness is the
+ // time between the last successful seed download and now.
+ if (!last_fetch_time.is_null()) {
+ const base::TimeDelta delta = now - last_fetch_time;
+ // Log the value in number of minutes.
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Variations.SeedFreshness", delta.InMinutes(),
+ 1, base::TimeDelta::FromDays(30).InMinutes(),
+ 50);
+ }
+
+ return true;
+}
+
+void VariationsFieldTrialCreator::SetCreateTrialsFromSeedCalledForTesting(
+ bool called) {
+ create_trials_from_seed_called_ = called;
+}
+
+std::unique_ptr<ClientFilterableState>
+VariationsFieldTrialCreator::GetClientFilterableStateForVersion(
+ const base::Version& version) {
+ std::unique_ptr<ClientFilterableState> state =
+ base::MakeUnique<ClientFilterableState>();
+ state->locale = client_->GetApplicationLocale();
+ state->reference_date = GetReferenceDateForExpiryChecks(local_state());
+ state->version = version;
+ state->channel = GetChannelForVariations(client_->GetChannel());
+ state->form_factor = GetCurrentFormFactor();
+ state->platform = ClientFilterableState::GetCurrentPlatform();
+ state->hardware_class = GetHardwareClass();
+#if defined(OS_ANDROID)
+ // This is set on Android only currently, because the IsLowEndDevice() API
+ // on other platforms has no intrinsic meaning outside of a field trial that
+ // controls its value. Since this is before server-side field trials are
+ // evaluated, that field trial would not be able to apply for this case.
+ state->is_low_end_device = base::SysInfo::IsLowEndDevice();
+#endif
+ state->session_consistency_country = GetLatestCountry();
+ state->permanent_consistency_country = LoadPermanentConsistencyCountry(
+ version, state->session_consistency_country);
+ return state;
+}
+
+std::string VariationsFieldTrialCreator::LoadPermanentConsistencyCountry(
+ const base::Version& version,
+ const std::string& latest_country) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(version.IsValid());
+
+ const std::string override_country =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kVariationsOverrideCountry);
+ if (!override_country.empty())
+ return override_country;
+
+ const base::ListValue* list_value =
+ local_state()->GetList(prefs::kVariationsPermanentConsistencyCountry);
+ std::string stored_version_string;
+ std::string stored_country;
+
+ // Determine if the saved pref value is present and valid.
+ const bool is_pref_empty = list_value->empty();
+ const bool is_pref_valid = list_value->GetSize() == 2 &&
+ list_value->GetString(0, &stored_version_string) &&
+ list_value->GetString(1, &stored_country) &&
+ base::Version(stored_version_string).IsValid();
+
+ // Determine if the version from the saved pref matches |version|.
+ const bool does_version_match =
+ is_pref_valid && version == base::Version(stored_version_string);
+
+ // Determine if the country in the saved pref matches the country in
+ // |latest_country|.
+ const bool does_country_match = is_pref_valid && !latest_country.empty() &&
+ stored_country == latest_country;
+
+ // Record a histogram for how the saved pref value compares to the current
+ // version and the country code in the variations seed.
+ LoadPermanentConsistencyCountryResult result;
+ if (is_pref_empty) {
+ result = !latest_country.empty() ? LOAD_COUNTRY_NO_PREF_HAS_SEED
+ : LOAD_COUNTRY_NO_PREF_NO_SEED;
+ } else if (!is_pref_valid) {
+ result = !latest_country.empty() ? LOAD_COUNTRY_INVALID_PREF_HAS_SEED
+ : LOAD_COUNTRY_INVALID_PREF_NO_SEED;
+ } else if (latest_country.empty()) {
+ result = does_version_match ? LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_EQ
+ : LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_NEQ;
+ } else if (does_version_match) {
+ result = does_country_match ? LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_EQ
+ : LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_NEQ;
+ } else {
+ result = does_country_match ? LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_EQ
+ : LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_NEQ;
+ }
+ UMA_HISTOGRAM_ENUMERATION("Variations.LoadPermanentConsistencyCountryResult",
+ result, LOAD_COUNTRY_MAX);
+
+ // Use the stored country if one is available and was fetched since the last
+ // time Chrome was updated.
+ if (does_version_match)
+ return stored_country;
+
+ if (latest_country.empty()) {
+ if (!is_pref_valid)
+ local_state()->ClearPref(prefs::kVariationsPermanentConsistencyCountry);
+ // If we've never received a country code from the server, use an empty
+ // country so that it won't pass any filters that specifically include
+ // countries, but so that it will pass any filters that specifically exclude
+ // countries.
+ return std::string();
+ }
+
+ // Otherwise, update the pref with the current Chrome version and country.
+ StorePermanentCountry(version, latest_country);
+ return latest_country;
+}
+
+void VariationsFieldTrialCreator::StorePermanentCountry(
+ const base::Version& version,
+ const std::string& country) {
+ base::ListValue new_list_value;
+ new_list_value.AppendString(version.GetString());
+ new_list_value.AppendString(country);
+ local_state()->Set(prefs::kVariationsPermanentConsistencyCountry,
+ new_list_value);
+}
+
+void VariationsFieldTrialCreator::RecordLastFetchTime() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ local_state()->SetInt64(prefs::kVariationsLastFetchTime,
+ base::Time::Now().ToInternalValue());
+}
+
+bool VariationsFieldTrialCreator::LoadSeed(VariationsSeed* seed) {
+ return seed_store_.LoadSeed(seed);
+}
+
+} // namespace variations
diff --git a/chromium/components/variations/service/variations_field_trial_creator.h b/chromium/components/variations/service/variations_field_trial_creator.h
new file mode 100644
index 00000000000..8e32f8168d1
--- /dev/null
+++ b/chromium/components/variations/service/variations_field_trial_creator.h
@@ -0,0 +1,123 @@
+// 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_FIELD_TRIAL_CREATOR_H_
+#define COMPONENTS_VARIATIONS_FIELD_TRIAL_CREATOR_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/metrics/field_trial.h"
+#include "components/variations/client_filterable_state.h"
+#include "components/variations/service/ui_string_overrider.h"
+#include "components/variations/variations_seed_store.h"
+
+namespace variations {
+class VariationsServiceClient;
+}
+
+namespace variations {
+
+// Used to setup field trials based on stored variations seed data.
+class VariationsFieldTrialCreator {
+ public:
+ // Caller is responsible for ensuring that objects passed to the constructor
+ // stay valid for the lifetime of this object.
+ VariationsFieldTrialCreator(PrefService* local_state,
+ VariationsServiceClient* client,
+ const UIStringOverrider& ui_string_overrider);
+ ~VariationsFieldTrialCreator();
+
+ // Returns what variations will consider to be the latest country. Returns
+ // empty if it is not available.
+ std::string GetLatestCountry() const;
+
+ // Creates field trials based on the variations seed loaded from local state.
+ // If there is a problem loading the seed data, all trials specified by the
+ // seed may not be created. Some field trials are configured to override or
+ // associate with (for reporting) specific features. These associations are
+ // registered with |feature_list|.
+ bool CreateTrialsFromSeed(
+ std::unique_ptr<const base::FieldTrial::EntropyProvider>
+ low_entropy_provider,
+ base::FeatureList* feature_list);
+
+ VariationsSeedStore& seed_store() { return seed_store_; }
+
+ const VariationsSeedStore& seed_store() const { return seed_store_; }
+
+ bool create_trials_from_seed_called() const {
+ return create_trials_from_seed_called_;
+ }
+
+ // Exposed for testing.
+ void SetCreateTrialsFromSeedCalledForTesting(bool called);
+
+ // Returns all of the client state used for filtering studies.
+ // As a side-effect, may update the stored permanent consistency country.
+ std::unique_ptr<ClientFilterableState> GetClientFilterableStateForVersion(
+ const base::Version& version);
+
+ // Loads the country code to use for filtering permanent consistency studies,
+ // updating the stored country code if the stored value was for a different
+ // Chrome version. The country used for permanent consistency studies is kept
+ // consistent between Chrome upgrades in order to avoid annoying the user due
+ // to experiment churn while traveling.
+ std::string LoadPermanentConsistencyCountry(
+ const base::Version& version,
+ const std::string& latest_country);
+
+ // Sets the stored permanent country pref for this client.
+ void StorePermanentCountry(const base::Version& version,
+ const std::string& country);
+
+ // Records the time of the most recent successful fetch.
+ void RecordLastFetchTime();
+
+ // Loads the seed from the variations store into |seed|. If successfull,
+ // |seed| will contain the loaded data and true is returned. Set as virtual
+ // so that it can be overridden by tests.
+ virtual bool LoadSeed(VariationsSeed* seed);
+
+ private:
+ PrefService* local_state() { return seed_store_.local_state(); }
+
+ const PrefService* local_state() const { return seed_store_.local_state(); }
+
+ // Set of different possible values to report for the
+ // Variations.LoadPermanentConsistencyCountryResult histogram. This enum must
+ // be kept consistent with its counterpart in histograms.xml.
+ enum LoadPermanentConsistencyCountryResult {
+ LOAD_COUNTRY_NO_PREF_NO_SEED = 0,
+ LOAD_COUNTRY_NO_PREF_HAS_SEED,
+ LOAD_COUNTRY_INVALID_PREF_NO_SEED,
+ LOAD_COUNTRY_INVALID_PREF_HAS_SEED,
+ LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_EQ,
+ LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_NEQ,
+ LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_EQ,
+ LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_NEQ,
+ LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_EQ,
+ LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_NEQ,
+ LOAD_COUNTRY_MAX,
+ };
+
+ VariationsServiceClient* client_;
+
+ UIStringOverrider ui_string_overrider_;
+
+ VariationsSeedStore seed_store_;
+
+ // Tracks whether |CreateTrialsFromSeed| has been called, to ensure that
+ // it gets called prior to |StartRepeatedVariationsSeedFetch|.
+ bool create_trials_from_seed_called_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(VariationsFieldTrialCreator);
+};
+
+} // namespace variations
+
+#endif // COMPONENTS_VARIATIONS_FIELD_TRIAL_CREATOR_H_
diff --git a/chromium/components/variations/service/variations_field_trial_creator_unittest.cc b/chromium/components/variations/service/variations_field_trial_creator_unittest.cc
new file mode 100644
index 00000000000..47fefb3a0a1
--- /dev/null
+++ b/chromium/components/variations/service/variations_field_trial_creator_unittest.cc
@@ -0,0 +1,276 @@
+// 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/service/variations_field_trial_creator.h"
+
+#include <stddef.h>
+
+#include "base/feature_list.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/version.h"
+#include "components/metrics/clean_exit_beacon.h"
+#include "components/metrics/client_info.h"
+#include "components/metrics/metrics_state_manager.h"
+#include "components/metrics/test_enabled_state_provider.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/variations/pref_names.h"
+#include "components/variations/service/variations_service.h"
+#include "components/web_resource/resource_request_allowed_notifier_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace variations {
+namespace {
+
+// A stub for the metrics state manager.
+void StubStoreClientInfo(const metrics::ClientInfo& /* client_info */) {}
+
+// A stub for the metrics state manager.
+std::unique_ptr<metrics::ClientInfo> StubLoadClientInfo() {
+ return std::unique_ptr<metrics::ClientInfo>();
+}
+
+class TestVariationsServiceClient : public VariationsServiceClient {
+ public:
+ TestVariationsServiceClient() {}
+ ~TestVariationsServiceClient() override {}
+
+ // VariationsServiceClient:
+ std::string GetApplicationLocale() override { return std::string(); }
+ base::Callback<base::Version(void)> GetVersionForSimulationCallback()
+ override {
+ return base::Callback<base::Version(void)>();
+ }
+ net::URLRequestContextGetter* GetURLRequestContext() override {
+ return nullptr;
+ }
+ network_time::NetworkTimeTracker* GetNetworkTimeTracker() override {
+ return nullptr;
+ }
+ version_info::Channel GetChannel() override {
+ return version_info::Channel::UNKNOWN;
+ }
+ bool OverridesRestrictParameter(std::string* parameter) override {
+ if (restrict_parameter_.empty())
+ return false;
+ *parameter = restrict_parameter_;
+ return true;
+ }
+
+ void set_restrict_parameter(const std::string& value) {
+ restrict_parameter_ = value;
+ }
+
+ private:
+ std::string restrict_parameter_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestVariationsServiceClient);
+};
+
+class TestVariationsFieldTrialCreator : public VariationsFieldTrialCreator {
+ public:
+ TestVariationsFieldTrialCreator(
+ std::unique_ptr<web_resource::TestRequestAllowedNotifier> test_notifier,
+ PrefService* local_state,
+ TestVariationsServiceClient* client)
+ : VariationsFieldTrialCreator(local_state, client, UIStringOverrider()),
+ client_(client) {
+ SetCreateTrialsFromSeedCalledForTesting(true);
+ }
+
+ ~TestVariationsFieldTrialCreator() {
+ delete client_;
+ client_ = 0;
+ }
+
+ bool StoreSeed(const std::string& seed_data,
+ const std::string& seed_signature,
+ const std::string& country_code,
+ const base::Time& date_fetched,
+ bool is_delta_compressed,
+ bool is_gzip_compressed) {
+ seed_stored_ = true;
+ stored_seed_data_ = seed_data;
+ stored_country_ = country_code;
+ delta_compressed_seed_ = is_delta_compressed;
+ gzip_compressed_seed_ = is_gzip_compressed;
+ return true;
+ }
+
+ private:
+ bool LoadSeed(VariationsSeed* seed) override {
+ if (!seed_stored_)
+ return false;
+ return seed->ParseFromString(stored_seed_data_);
+ }
+
+ bool seed_stored_;
+ std::string stored_seed_data_;
+ std::string stored_country_;
+ bool delta_compressed_seed_;
+ bool gzip_compressed_seed_;
+
+ TestVariationsServiceClient* client_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestVariationsFieldTrialCreator);
+};
+
+// A test class used to validate expected functionality in VariationsService.
+class TestVariationsService : public VariationsService {
+ public:
+ TestVariationsService(
+ std::unique_ptr<web_resource::TestRequestAllowedNotifier> test_notifier,
+ PrefService* local_state,
+ metrics::MetricsStateManager* state_manager)
+ : VariationsService(base::WrapUnique(new TestVariationsServiceClient()),
+ std::move(test_notifier),
+ local_state,
+ state_manager,
+ UIStringOverrider()) {}
+
+ ~TestVariationsService() override {}
+};
+
+// Constants used to create the test seed.
+const char kTestSeedStudyName[] = "test";
+const char kTestSeedExperimentName[] = "abc";
+const int kTestSeedExperimentProbability = 100;
+const char kTestSeedSerialNumber[] = "123";
+
+// Populates |seed| with simple test data. The resulting seed will contain one
+// study called "test", which contains one experiment called "abc" with
+// probability weight 100. |seed|'s study field will be cleared before adding
+// the new study.
+VariationsSeed CreateTestSeed() {
+ VariationsSeed seed;
+ Study* study = seed.add_study();
+ study->set_name(kTestSeedStudyName);
+ study->set_default_experiment_name(kTestSeedExperimentName);
+ Study_Experiment* experiment = study->add_experiment();
+ experiment->set_name(kTestSeedExperimentName);
+ experiment->set_probability_weight(kTestSeedExperimentProbability);
+ seed.set_serial_number(kTestSeedSerialNumber);
+ return seed;
+}
+
+// Serializes |seed| to protobuf binary format.
+std::string SerializeSeed(const VariationsSeed& seed) {
+ std::string serialized_seed;
+ seed.SerializeToString(&serialized_seed);
+ return serialized_seed;
+}
+
+} // namespace
+
+class FieldTrialCreatorTest : public ::testing::Test {
+ protected:
+ FieldTrialCreatorTest()
+ : enabled_state_provider_(
+ new metrics::TestEnabledStateProvider(false, false)) {
+ VariationsService::RegisterPrefs(prefs_.registry());
+ metrics::CleanExitBeacon::RegisterPrefs(prefs_.registry());
+ metrics::MetricsStateManager::RegisterPrefs(prefs_.registry());
+ }
+
+ metrics::MetricsStateManager* GetMetricsStateManager() {
+ // Lazy-initialize the metrics_state_manager so that it correctly reads the
+ // stability state from prefs after tests have a chance to initialize it.
+ if (!metrics_state_manager_) {
+ metrics_state_manager_ = metrics::MetricsStateManager::Create(
+ &prefs_, enabled_state_provider_.get(), base::string16(),
+ base::Bind(&StubStoreClientInfo), base::Bind(&StubLoadClientInfo));
+ }
+ return metrics_state_manager_.get();
+ }
+
+ protected:
+ TestingPrefServiceSimple prefs_;
+
+ private:
+ base::MessageLoop message_loop_;
+ std::unique_ptr<metrics::TestEnabledStateProvider> enabled_state_provider_;
+ std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(FieldTrialCreatorTest);
+};
+
+TEST_F(FieldTrialCreatorTest, CreateTrialsFromSeed) {
+ // Create a local base::FieldTrialList, to hold the field trials created in
+ // this test.
+ base::FieldTrialList field_trial_list(nullptr);
+
+ // Create a variations service.
+ TestVariationsFieldTrialCreator field_trial_creator(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, new TestVariationsServiceClient());
+ field_trial_creator.SetCreateTrialsFromSeedCalledForTesting(false);
+
+ // Store a seed.
+ field_trial_creator.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
+ std::string(), base::Time::Now(), false, false);
+ prefs_.SetInt64(prefs::kVariationsLastFetchTime,
+ base::Time::Now().ToInternalValue());
+
+ // Check that field trials are created from the seed. Since the test study has
+ // only 1 experiment with 100% probability weight, we must be part of it.
+ EXPECT_TRUE(field_trial_creator.CreateTrialsFromSeed(
+ std::unique_ptr<const base::FieldTrial::EntropyProvider>(nullptr),
+ base::FeatureList::GetInstance()));
+ EXPECT_EQ(kTestSeedExperimentName,
+ base::FieldTrialList::FindFullName(kTestSeedStudyName));
+}
+
+TEST_F(FieldTrialCreatorTest, CreateTrialsFromSeedNoLastFetchTime) {
+ // Create a local base::FieldTrialList, to hold the field trials created in
+ // this test.
+ base::FieldTrialList field_trial_list(nullptr);
+
+ // Create a variations service
+ TestVariationsFieldTrialCreator field_trial_creator(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, new TestVariationsServiceClient());
+ field_trial_creator.SetCreateTrialsFromSeedCalledForTesting(false);
+
+ // Store a seed. To simulate a first run, |prefs::kVariationsLastFetchTime|
+ // is left empty.
+ field_trial_creator.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
+ std::string(), base::Time::Now(), false, false);
+ EXPECT_EQ(0, prefs_.GetInt64(prefs::kVariationsLastFetchTime));
+
+ // Check that field trials are created from the seed. Since the test study has
+ // only 1 experiment with 100% probability weight, we must be part of it.
+ EXPECT_TRUE(field_trial_creator.CreateTrialsFromSeed(
+ std::unique_ptr<const base::FieldTrial::EntropyProvider>(nullptr),
+ base::FeatureList::GetInstance()));
+ EXPECT_EQ(base::FieldTrialList::FindFullName(kTestSeedStudyName),
+ kTestSeedExperimentName);
+}
+
+TEST_F(FieldTrialCreatorTest, CreateTrialsFromOutdatedSeed) {
+ // Create a local base::FieldTrialList, to hold the field trials created in
+ // this test.
+ base::FieldTrialList field_trial_list(nullptr);
+
+ // Create a variations service.
+ TestVariationsFieldTrialCreator field_trial_creator(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, new TestVariationsServiceClient());
+ field_trial_creator.SetCreateTrialsFromSeedCalledForTesting(false);
+
+ // Store a seed, with a fetch time 31 days in the past.
+ const base::Time seed_date =
+ base::Time::Now() - base::TimeDelta::FromDays(31);
+ field_trial_creator.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
+ std::string(), seed_date, false, false);
+ prefs_.SetInt64(prefs::kVariationsLastFetchTime, seed_date.ToInternalValue());
+
+ // Check that field trials are not created from the seed.
+ EXPECT_FALSE(field_trial_creator.CreateTrialsFromSeed(
+ std::unique_ptr<const base::FieldTrial::EntropyProvider>(nullptr),
+ base::FeatureList::GetInstance()));
+ EXPECT_TRUE(base::FieldTrialList::FindFullName(kTestSeedStudyName).empty());
+}
+
+} // namespace variations
diff --git a/chromium/components/variations/service/variations_service.cc b/chromium/components/variations/service/variations_service.cc
index e669699c2cd..11c418e4faf 100644
--- a/chromium/components/variations/service/variations_service.cc
+++ b/chromium/components/variations/service/variations_service.cc
@@ -10,6 +10,7 @@
#include <utility>
#include <vector>
+#include "base/base_switches.h"
#include "base/build_time.h"
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
@@ -18,6 +19,7 @@
#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/timer/elapsed_timer.h"
#include "base/values.h"
@@ -31,6 +33,7 @@
#include "components/prefs/pref_service.h"
#include "components/variations/pref_names.h"
#include "components/variations/proto/variations_seed.pb.h"
+#include "components/variations/service/variations_field_trial_creator.h"
#include "components/variations/variations_seed_processor.h"
#include "components/variations/variations_seed_simulator.h"
#include "components/variations/variations_switches.h"
@@ -49,49 +52,15 @@
#include "url/gurl.h"
namespace variations {
-
namespace {
// TODO(mad): To be removed when we stop updating the NetworkTimeTracker.
// For the HTTP date headers, the resolution of the server time is 1 second.
const int64_t kServerTimeResolutionMs = 1000;
-// Maximum age permitted for a variations seed, in days.
-const int kMaxVariationsSeedAgeDays = 30;
-
-// Wrapper around channel checking, used to enable channel mocking for
-// testing. If the current browser channel is not UNKNOWN, this will return
-// that channel value. Otherwise, if the fake channel flag is provided, this
-// will return the fake channel. Failing that, this will return the UNKNOWN
-// channel.
-variations::Study_Channel GetChannelForVariations(
- version_info::Channel product_channel) {
- switch (product_channel) {
- case version_info::Channel::CANARY:
- return variations::Study_Channel_CANARY;
- case version_info::Channel::DEV:
- return variations::Study_Channel_DEV;
- case version_info::Channel::BETA:
- return variations::Study_Channel_BETA;
- case version_info::Channel::STABLE:
- return variations::Study_Channel_STABLE;
- case version_info::Channel::UNKNOWN:
- break;
- }
- const std::string forced_channel =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kFakeVariationsChannel);
- if (forced_channel == "stable")
- return variations::Study_Channel_STABLE;
- if (forced_channel == "beta")
- return variations::Study_Channel_BETA;
- if (forced_channel == "dev")
- return variations::Study_Channel_DEV;
- if (forced_channel == "canary")
- return variations::Study_Channel_CANARY;
- DVLOG(1) << "Invalid channel provided: " << forced_channel;
- return variations::Study_Channel_UNKNOWN;
-}
+// Whether the VariationsService should always be created, even in Chromium
+// builds.
+bool g_enabled_for_testing = false;
// Returns a string that will be used for the value of the 'osname' URL param
// to the variations server.
@@ -148,13 +117,6 @@ enum VariationsSeedExpiry {
VARIATIONS_SEED_EXPIRY_ENUM_SIZE,
};
-// Records UMA histogram with the result of the variations seed expiry check.
-void RecordCreateTrialsSeedExpiry(VariationsSeedExpiry expiry_check_result) {
- UMA_HISTOGRAM_ENUMERATION("Variations.CreateTrials.SeedExpiry",
- expiry_check_result,
- VARIATIONS_SEED_EXPIRY_ENUM_SIZE);
-}
-
// Converts ResourceRequestAllowedNotifier::State to the corresponding
// ResourceRequestsAllowedState value.
ResourceRequestsAllowedState ResourceRequestStateToHistogramValue(
@@ -174,45 +136,6 @@ ResourceRequestsAllowedState ResourceRequestStateToHistogramValue(
return RESOURCE_REQUESTS_NOT_ALLOWED;
}
-
-// Gets current form factor and converts it from enum DeviceFormFactor to enum
-// Study_FormFactor.
-variations::Study_FormFactor GetCurrentFormFactor() {
- switch (ui::GetDeviceFormFactor()) {
- case ui::DEVICE_FORM_FACTOR_PHONE:
- return variations::Study_FormFactor_PHONE;
- case ui::DEVICE_FORM_FACTOR_TABLET:
- return variations::Study_FormFactor_TABLET;
- case ui::DEVICE_FORM_FACTOR_DESKTOP:
- return variations::Study_FormFactor_DESKTOP;
- }
- NOTREACHED();
- return variations::Study_FormFactor_DESKTOP;
-}
-
-// Gets the hardware class and returns it as a string. This returns an empty
-// string if the client is not ChromeOS.
-std::string GetHardwareClass() {
-#if defined(OS_CHROMEOS)
- return base::SysInfo::GetLsbReleaseBoard();
-#endif // OS_CHROMEOS
- return std::string();
-}
-
-// Returns the date that should be used by the VariationsSeedProcessor to do
-// expiry and start date checks.
-base::Time GetReferenceDateForExpiryChecks(PrefService* local_state) {
- const int64_t date_value = local_state->GetInt64(prefs::kVariationsSeedDate);
- const base::Time seed_date = base::Time::FromInternalValue(date_value);
- const base::Time build_time = base::GetBuildTime();
- // Use the build time for date checks if either the seed date is invalid or
- // the build time is newer than the seed date.
- base::Time reference_date = seed_date;
- if (seed_date.is_null() || seed_date < build_time)
- reference_date = build_time;
- return reference_date;
-}
-
// Returns the header value for |name| from |headers| or an empty string if not
// set.
std::string GetHeaderValue(const net::HttpResponseHeaders* headers,
@@ -280,103 +203,64 @@ VariationsService::VariationsService(
metrics::MetricsStateManager* state_manager,
const UIStringOverrider& ui_string_overrider)
: client_(std::move(client)),
- ui_string_overrider_(ui_string_overrider),
local_state_(local_state),
state_manager_(state_manager),
policy_pref_service_(local_state),
- seed_store_(local_state),
- create_trials_from_seed_called_(false),
initial_request_completed_(false),
disable_deltas_for_next_request_(false),
resource_request_allowed_notifier_(std::move(notifier)),
request_count_(0),
+ field_trial_creator_(local_state, client_.get(), ui_string_overrider),
weak_ptr_factory_(this) {
DCHECK(client_.get());
DCHECK(resource_request_allowed_notifier_.get());
resource_request_allowed_notifier_->Init(this);
-}
-
-VariationsService::~VariationsService() {
-}
-
-bool VariationsService::CreateTrialsFromSeed(base::FeatureList* feature_list) {
- DCHECK(thread_checker_.CalledOnValidThread());
- CHECK(!create_trials_from_seed_called_);
- create_trials_from_seed_called_ = true;
-
- variations::VariationsSeed seed;
- if (!LoadSeed(&seed))
- return false;
-
- const int64_t last_fetch_time_internal =
- local_state_->GetInt64(prefs::kVariationsLastFetchTime);
- const base::Time last_fetch_time =
- base::Time::FromInternalValue(last_fetch_time_internal);
- if (last_fetch_time.is_null()) {
- // If the last fetch time is missing and we have a seed, then this must be
- // the first run of Chrome. Store the current time as the last fetch time.
- RecordLastFetchTime();
- RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_FETCH_TIME_MISSING);
- } else {
- // Reject the seed if it is more than 30 days old.
- const base::TimeDelta seed_age = base::Time::Now() - last_fetch_time;
- if (seed_age.InDays() > kMaxVariationsSeedAgeDays) {
- RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_EXPIRED);
- return false;
- }
- RecordCreateTrialsSeedExpiry(VARIATIONS_SEED_EXPIRY_NOT_EXPIRED);
+ // Increment the crash streak if the previous session crashed.
+ // Note that the streak is not cleared if the previous run didn’t crash.
+ // Instead, it’s incremented on each crash until Chrome is able to
+ // successfully fetch a new seed. This way, a seed update that mostly
+ // destabilizes Chrome will still result in a fallback to safe mode.
+ int num_crashes = local_state->GetInteger(prefs::kVariationsCrashStreak);
+ if (!state_manager_->clean_exit_beacon()->exited_cleanly()) {
+ ++num_crashes;
+ local_state->SetInteger(prefs::kVariationsCrashStreak, num_crashes);
}
- const base::Version current_version(version_info::GetVersionNumber());
- if (!current_version.IsValid())
- return false;
-
- variations::Study_Channel channel =
- GetChannelForVariations(client_->GetChannel());
- UMA_HISTOGRAM_SPARSE_SLOWLY("Variations.UserChannel", channel);
-
- const std::string latest_country = GetLatestCountry();
- std::unique_ptr<const base::FieldTrial::EntropyProvider> low_entropy_provider(
- CreateLowEntropyProvider());
- // Note that passing |&ui_string_overrider_| via base::Unretained below is
- // safe because the callback is executed synchronously. It is not possible
- // to pass UIStringOverrider itself to VariationSeedProcesor as variations
- // components should not depends on //ui/base.
- variations::VariationsSeedProcessor().CreateTrialsFromSeed(
- seed, client_->GetApplicationLocale(),
- GetReferenceDateForExpiryChecks(local_state_), current_version, channel,
- GetCurrentFormFactor(), GetHardwareClass(), latest_country,
- LoadPermanentConsistencyCountry(current_version, latest_country),
- base::Bind(&UIStringOverrider::OverrideUIString,
- base::Unretained(&ui_string_overrider_)),
- low_entropy_provider.get(), feature_list);
-
- const base::Time now = base::Time::Now();
-
- // Log the "freshness" of the seed that was just used. The freshness is the
- // time between the last successful seed download and now.
- if (last_fetch_time_internal) {
- const base::TimeDelta delta =
- now - base::Time::FromInternalValue(last_fetch_time_internal);
- // Log the value in number of minutes.
- UMA_HISTOGRAM_CUSTOM_COUNTS("Variations.SeedFreshness", delta.InMinutes(),
- 1, base::TimeDelta::FromDays(30).InMinutes(), 50);
- }
+ // After three failures in a row -- either consistent crashes or consistent
+ // failures to fetch the seed -- assume that the current seed is bad, and fall
+ // back to the safe seed. However, ignore any number of failures if the
+ // --force-fieldtrials flag is set, as this flag is only used by developers,
+ // and there's no need to make the development process flakier.
+ const int kMaxFailuresBeforeRevertingToSafeSeed = 3;
+ int num_failures_to_fetch =
+ local_state->GetInteger(prefs::kVariationsFailedToFetchSeedStreak);
+ bool fall_back_to_safe_mode =
+ (num_crashes >= kMaxFailuresBeforeRevertingToSafeSeed ||
+ num_failures_to_fetch >= kMaxFailuresBeforeRevertingToSafeSeed) &&
+ !base::CommandLine::ForCurrentProcess()->HasSwitch(
+ ::switches::kForceFieldTrials);
+ UMA_HISTOGRAM_BOOLEAN("Variations.SafeMode.FellBackToSafeMode",
+ fall_back_to_safe_mode);
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Variations.SafeMode.Streak.Crashes",
+ std::min(std::max(num_crashes, 0), 100));
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "Variations.SafeMode.Streak.FetchFailures",
+ std::min(std::max(num_failures_to_fetch, 0), 100));
+}
- return true;
+VariationsService::~VariationsService() {
}
void VariationsService::PerformPreMainMessageLoopStartup() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
StartRepeatedVariationsSeedFetch();
- client_->OnInitialStartup();
}
void VariationsService::StartRepeatedVariationsSeedFetch() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Initialize the Variations server URL.
variations_server_url_ =
@@ -384,30 +268,37 @@ void VariationsService::StartRepeatedVariationsSeedFetch() {
// Check that |CreateTrialsFromSeed| was called, which is necessary to
// retrieve the serial number that will be sent to the server.
- DCHECK(create_trials_from_seed_called_);
+ DCHECK(field_trial_creator_.create_trials_from_seed_called());
DCHECK(!request_scheduler_.get());
- // Note that the act of instantiating the scheduler will start the fetch, if
- // the scheduler deems appropriate.
request_scheduler_.reset(VariationsRequestScheduler::Create(
base::Bind(&VariationsService::FetchVariationsSeed,
weak_ptr_factory_.GetWeakPtr()),
local_state_));
+ // Note that the act of starting the scheduler will start the fetch, if the
+ // scheduler deems appropriate.
request_scheduler_->Start();
}
+std::string VariationsService::LoadPermanentConsistencyCountry(
+ const base::Version& version,
+ const std::string& latest_country) {
+ return field_trial_creator_.LoadPermanentConsistencyCountry(version,
+ latest_country);
+}
+
void VariationsService::AddObserver(Observer* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_list_.AddObserver(observer);
}
void VariationsService::RemoveObserver(Observer* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_list_.RemoveObserver(observer);
}
void VariationsService::OnAppEnterForeground() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// On mobile platforms, initialize the fetch scheduler when we receive the
// first app foreground notification.
@@ -417,7 +308,7 @@ void VariationsService::OnAppEnterForeground() {
}
void VariationsService::SetRestrictMode(const std::string& restrict_mode) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// This should be called before the server URL has been computed.
DCHECK(variations_server_url_.is_empty());
@@ -425,8 +316,8 @@ void VariationsService::SetRestrictMode(const std::string& restrict_mode) {
}
void VariationsService::SetCreateTrialsFromSeedCalledForTesting(bool called) {
- DCHECK(thread_checker_.CalledOnValidThread());
- create_trials_from_seed_called_ = called;
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ field_trial_creator_.SetCreateTrialsFromSeedCalledForTesting(called);
}
GURL VariationsService::GetVariationsServerURL(
@@ -472,6 +363,11 @@ void VariationsService::RegisterPrefs(PrefRegistrySimple* registry) {
// This preference keeps track of the country code used to filter
// permanent-consistency studies.
registry->RegisterListPref(prefs::kVariationsPermanentConsistencyCountry);
+
+ // Prefs tracking failures along the way to fetching a seed, used to implement
+ // Safe Mode.
+ registry->RegisterIntegerPref(prefs::kVariationsCrashStreak, 0);
+ registry->RegisterIntegerPref(prefs::kVariationsFailedToFetchSeedStreak, 0);
}
// static
@@ -495,7 +391,8 @@ std::unique_ptr<VariationsService> VariationsService::Create(
// Unless the URL was provided, unsupported builds should return NULL to
// indicate that the service should not be used.
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kVariationsServerURL)) {
+ switches::kVariationsServerURL) &&
+ !g_enabled_for_testing) {
DVLOG(1) << "Not creating VariationsService in unofficial build without --"
<< switches::kVariationsServerURL << " specified.";
return result;
@@ -510,18 +407,19 @@ std::unique_ptr<VariationsService> VariationsService::Create(
}
// static
-std::unique_ptr<VariationsService> VariationsService::CreateForTesting(
- std::unique_ptr<VariationsServiceClient> client,
- PrefService* local_state) {
- return base::WrapUnique(new VariationsService(
- std::move(client),
- base::MakeUnique<web_resource::ResourceRequestAllowedNotifier>(
- local_state, nullptr),
- local_state, nullptr, UIStringOverrider()));
+void VariationsService::EnableForTesting() {
+ g_enabled_for_testing = true;
}
void VariationsService::DoActualFetch() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // Pessimistically assume the fetch will fail. The failure streak will be
+ // reset upon success.
+ int num_failures_to_fetch =
+ local_state_->GetInteger(prefs::kVariationsFailedToFetchSeedStreak);
+ local_state_->SetInteger(prefs::kVariationsFailedToFetchSeedStreak,
+ num_failures_to_fetch + 1);
// Normally, there shouldn't be a |pending_request_| when this fires. However
// it's not impossible - for example if Chrome was paused (e.g. in a debugger
@@ -560,21 +458,15 @@ void VariationsService::DoActualFetch() {
net::LOAD_DO_NOT_SAVE_COOKIES);
pending_seed_request_->SetRequestContext(client_->GetURLRequestContext());
bool enable_deltas = false;
- if (!seed_store_.variations_serial_number().empty() &&
+ if (!field_trial_creator_.seed_store().variations_serial_number().empty() &&
!disable_deltas_for_next_request_) {
- // If the current seed includes a country code, deltas are not supported (as
- // the serial number doesn't take into account the country code). The server
- // will update us with a seed that doesn't include a country code which will
- // enable deltas to work.
- // TODO(asvitkine): Remove the check in M50+ when the percentage of clients
- // that have an old seed with a country code becomes miniscule.
- if (!seed_store_.seed_has_country_code()) {
- // Tell the server that delta-compressed seeds are supported.
- enable_deltas = true;
- }
+ // Tell the server that delta-compressed seeds are supported.
+ enable_deltas = true;
+
// Get the seed only if its serial number doesn't match what we have.
pending_seed_request_->AddExtraRequestHeader(
- "If-None-Match:" + seed_store_.variations_serial_number());
+ "If-None-Match:" +
+ field_trial_creator_.seed_store().variations_serial_number());
}
// Tell the server that delta-compressed and gzipped seeds are supported.
const char* supported_im = enable_deltas ? "A-IM:x-bm,gzip" : "A-IM:gzip";
@@ -602,24 +494,19 @@ bool VariationsService::StoreSeed(const std::string& seed_data,
const base::Time& date_fetched,
bool is_delta_compressed,
bool is_gzip_compressed) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- std::unique_ptr<variations::VariationsSeed> seed(
- new variations::VariationsSeed);
- if (!seed_store_.StoreSeedData(seed_data, seed_signature, country_code,
- date_fetched, is_delta_compressed,
- is_gzip_compressed, seed.get())) {
+ std::unique_ptr<VariationsSeed> seed(new VariationsSeed);
+ if (!field_trial_creator_.seed_store().StoreSeedData(
+ seed_data, seed_signature, country_code, date_fetched,
+ is_delta_compressed, is_gzip_compressed, seed.get())) {
return false;
}
- RecordLastFetchTime();
- // Perform seed simulation only if |state_manager_| is not-NULL. The state
- // manager may be NULL for some unit tests.
- if (!state_manager_)
- return true;
+ RecordSuccessfulFetch();
- base::PostTaskAndReplyWithResult(
- client_->GetBlockingPool(), FROM_HERE,
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
client_->GetVersionForSimulationCallback(),
base::Bind(&VariationsService::PerformSimulationWithVersion,
weak_ptr_factory_.GetWeakPtr(), base::Passed(&seed)));
@@ -631,12 +518,8 @@ VariationsService::CreateLowEntropyProvider() {
return state_manager_->CreateLowEntropyProvider();
}
-bool VariationsService::LoadSeed(VariationsSeed* seed) {
- return seed_store_.LoadSeed(seed);
-}
-
void VariationsService::FetchVariationsSeed() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const web_resource::ResourceRequestAllowedNotifier::State state =
resource_request_allowed_notifier_->GetResourceRequestsAllowedState();
@@ -650,8 +533,8 @@ void VariationsService::FetchVariationsSeed() {
}
void VariationsService::NotifyObservers(
- const variations::VariationsSeedSimulator::Result& result) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ const VariationsSeedSimulator::Result& result) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (result.kill_critical_group_change_count > 0) {
for (auto& observer : observer_list_)
@@ -663,7 +546,7 @@ void VariationsService::NotifyObservers(
}
void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(pending_seed_request_.get(), source);
const bool is_first_request = !initial_request_completed_;
@@ -710,10 +593,12 @@ void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
DVLOG(1) << "Variations server request returned non-HTTP_OK response code: "
<< response_code;
if (response_code == net::HTTP_NOT_MODIFIED) {
- RecordLastFetchTime();
+ RecordSuccessfulFetch();
+
// Update the seed date value in local state (used for expiry check on
// next start up), since 304 is a successful response.
- seed_store_.UpdateSeedDateAndLogDayChange(response_date);
+ field_trial_creator_.seed_store().UpdateSeedDateAndLogDayChange(
+ response_date);
}
return;
}
@@ -729,7 +614,7 @@ void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
&is_gzip_compressed)) {
// The header does not specify supported instance manipulations, unable to
// process data. Details of errors were logged by GetInstanceManipulations.
- seed_store_.ReportUnsupportedSeedFormatError();
+ field_trial_creator_.seed_store().ReportUnsupportedSeedFormatError();
return;
}
@@ -745,7 +630,7 @@ void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
}
void VariationsService::OnResourceRequestsAllowed() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Note that this only attempts to fetch the seed at most once per period
// (kSeedFetchPeriodHours). This works because
@@ -763,9 +648,9 @@ void VariationsService::OnResourceRequestsAllowed() {
}
void VariationsService::PerformSimulationWithVersion(
- std::unique_ptr<variations::VariationsSeed> seed,
+ std::unique_ptr<VariationsSeed> seed,
const base::Version& version) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!version.IsValid())
return;
@@ -776,17 +661,12 @@ void VariationsService::PerformSimulationWithVersion(
state_manager_->CreateDefaultEntropyProvider();
std::unique_ptr<const base::FieldTrial::EntropyProvider> low_provider =
state_manager_->CreateLowEntropyProvider();
- variations::VariationsSeedSimulator seed_simulator(*default_provider,
- *low_provider);
-
- const std::string latest_country = GetLatestCountry();
- const variations::VariationsSeedSimulator::Result result =
- seed_simulator.SimulateSeedStudies(
- *seed, client_->GetApplicationLocale(),
- GetReferenceDateForExpiryChecks(local_state_), version,
- GetChannelForVariations(client_->GetChannel()),
- GetCurrentFormFactor(), GetHardwareClass(), latest_country,
- LoadPermanentConsistencyCountry(version, latest_country));
+ VariationsSeedSimulator seed_simulator(*default_provider, *low_provider);
+
+ std::unique_ptr<ClientFilterableState> client_state =
+ field_trial_creator_.GetClientFilterableStateForVersion(version);
+ const VariationsSeedSimulator::Result result =
+ seed_simulator.SimulateSeedStudies(*seed, *client_state);
UMA_HISTOGRAM_COUNTS_100("Variations.SimulateSeed.NormalChanges",
result.normal_group_change_count);
@@ -800,103 +680,37 @@ void VariationsService::PerformSimulationWithVersion(
NotifyObservers(result);
}
-void VariationsService::RecordLastFetchTime() {
- DCHECK(thread_checker_.CalledOnValidThread());
+void VariationsService::RecordSuccessfulFetch() {
+ field_trial_creator_.RecordLastFetchTime();
- // local_state_ is NULL in tests, so check it first.
- if (local_state_) {
- local_state_->SetInt64(prefs::kVariationsLastFetchTime,
- base::Time::Now().ToInternalValue());
- }
+ // Note: It's important to clear the crash streak as well as the fetch
+ // failures streak. Crashes that occur after a successful seed fetch do not
+ // prevent updating to a new seed, and therefore do not necessitate falling
+ // back to a safe seed.
+ local_state_->SetInteger(prefs::kVariationsCrashStreak, 0);
+ local_state_->SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 0);
}
std::string VariationsService::GetInvalidVariationsSeedSignature() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return seed_store_.GetInvalidSignature();
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return field_trial_creator_.seed_store().GetInvalidSignature();
}
-std::string VariationsService::LoadPermanentConsistencyCountry(
- const base::Version& version,
- const std::string& latest_country) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(version.IsValid());
-
- const std::string override_country =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kVariationsOverrideCountry);
- if (!override_country.empty())
- return override_country;
-
- const base::ListValue* list_value =
- local_state_->GetList(prefs::kVariationsPermanentConsistencyCountry);
- std::string stored_version_string;
- std::string stored_country;
+void VariationsService::GetClientFilterableStateForVersionCalledForTesting() {
+ const base::Version current_version(version_info::GetVersionNumber());
+ if (!current_version.IsValid())
+ return;
- // Determine if the saved pref value is present and valid.
- const bool is_pref_empty = list_value->empty();
- const bool is_pref_valid = list_value->GetSize() == 2 &&
- list_value->GetString(0, &stored_version_string) &&
- list_value->GetString(1, &stored_country) &&
- base::Version(stored_version_string).IsValid();
-
- // Determine if the version from the saved pref matches |version|.
- const bool does_version_match =
- is_pref_valid && version == base::Version(stored_version_string);
-
- // Determine if the country in the saved pref matches the country in
- // |latest_country|.
- const bool does_country_match = is_pref_valid && !latest_country.empty() &&
- stored_country == latest_country;
-
- // Record a histogram for how the saved pref value compares to the current
- // version and the country code in the variations seed.
- LoadPermanentConsistencyCountryResult result;
- if (is_pref_empty) {
- result = !latest_country.empty() ? LOAD_COUNTRY_NO_PREF_HAS_SEED
- : LOAD_COUNTRY_NO_PREF_NO_SEED;
- } else if (!is_pref_valid) {
- result = !latest_country.empty() ? LOAD_COUNTRY_INVALID_PREF_HAS_SEED
- : LOAD_COUNTRY_INVALID_PREF_NO_SEED;
- } else if (latest_country.empty()) {
- result = does_version_match ? LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_EQ
- : LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_NEQ;
- } else if (does_version_match) {
- result = does_country_match ? LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_EQ
- : LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_NEQ;
- } else {
- result = does_country_match ? LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_EQ
- : LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_NEQ;
- }
- UMA_HISTOGRAM_ENUMERATION("Variations.LoadPermanentConsistencyCountryResult",
- result, LOAD_COUNTRY_MAX);
-
- // Use the stored country if one is available and was fetched since the last
- // time Chrome was updated.
- if (does_version_match)
- return stored_country;
-
- if (latest_country.empty()) {
- if (!is_pref_valid)
- local_state_->ClearPref(prefs::kVariationsPermanentConsistencyCountry);
- // If we've never received a country code from the server, use an empty
- // country so that it won't pass any filters that specifically include
- // countries, but so that it will pass any filters that specifically exclude
- // countries.
- return std::string();
- }
+ field_trial_creator_.GetClientFilterableStateForVersion(current_version);
+}
- // Otherwise, update the pref with the current Chrome version and country.
- StorePermanentCountry(version, latest_country);
- return latest_country;
+std::string VariationsService::GetLatestCountry() const {
+ return field_trial_creator_.GetLatestCountry();
}
-void VariationsService::StorePermanentCountry(const base::Version& version,
- const std::string& country) {
- base::ListValue new_list_value;
- new_list_value.AppendString(version.GetString());
- new_list_value.AppendString(country);
- local_state_->Set(prefs::kVariationsPermanentConsistencyCountry,
- new_list_value);
+bool VariationsService::CreateTrialsFromSeed(base::FeatureList* feature_list) {
+ return field_trial_creator_.CreateTrialsFromSeed(CreateLowEntropyProvider(),
+ feature_list);
}
std::string VariationsService::GetStoredPermanentCountry() {
@@ -913,7 +727,7 @@ std::string VariationsService::GetStoredPermanentCountry() {
bool VariationsService::OverrideStoredPermanentCountry(
const std::string& country_override) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (country_override.empty())
return false;
@@ -929,17 +743,8 @@ bool VariationsService::OverrideStoredPermanentCountry(
return false;
base::Version version(version_info::GetVersionNumber());
- StorePermanentCountry(version, country_override);
+ field_trial_creator_.StorePermanentCountry(version, country_override);
return true;
}
-std::string VariationsService::GetLatestCountry() const {
- const std::string override_country =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kVariationsOverrideCountry);
- return !override_country.empty()
- ? override_country
- : local_state_->GetString(prefs::kVariationsCountry);
-}
-
} // namespace variations
diff --git a/chromium/components/variations/service/variations_service.h b/chromium/components/variations/service/variations_service.h
index 6e7033b69d9..75b6a774198 100644
--- a/chromium/components/variations/service/variations_service.h
+++ b/chromium/components/variations/service/variations_service.h
@@ -14,9 +14,10 @@
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/observer_list.h"
-#include "base/threading/thread_checker.h"
#include "base/time/time.h"
+#include "components/variations/client_filterable_state.h"
#include "components/variations/service/ui_string_overrider.h"
+#include "components/variations/service/variations_field_trial_creator.h"
#include "components/variations/service/variations_service_client.h"
#include "components/variations/variations_request_scheduler.h"
#include "components/variations/variations_seed_simulator.h"
@@ -151,10 +152,8 @@ class VariationsService
const char* disable_network_switch,
const UIStringOverrider& ui_string_overrider);
- // Factory method for creating a VariationsService in a testing context.
- static std::unique_ptr<VariationsService> CreateForTesting(
- std::unique_ptr<VariationsServiceClient> client,
- PrefService* local_state);
+ // Enables the VariationsService for testing, even in Chromium builds.
+ static void EnableForTesting();
// Set the PrefService responsible for getting policy-related preferences,
// such as the restrict parameter.
@@ -168,6 +167,9 @@ class VariationsService
// disabled.
std::string GetInvalidVariationsSeedSignature() const;
+ // Exposed for testing.
+ void GetClientFilterableStateForVersionCalledForTesting();
+
protected:
// Starts the fetching process once, where |OnURLFetchComplete| is called with
// the response.
@@ -213,6 +215,10 @@ class VariationsService
LoadPermanentConsistencyCountry);
FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest, CountryHeader);
FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest, GetVariationsServerURL);
+ FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest,
+ SafeMode_SuccessfulFetchClearsFailureStreaks);
+ FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest,
+ SafeMode_NotModifiedFetchClearsFailureStreaks);
// Set of different possible values to report for the
// Variations.LoadPermanentConsistencyCountryResult histogram. This enum must
@@ -231,15 +237,6 @@ class VariationsService
LOAD_COUNTRY_MAX,
};
- // Loads the seed from the variations store into |seed|. If successfull,
- // |seed| will contain the loaded data and true is returned. Set as virtual
- // so that it can be overridden by tests.
- virtual bool LoadSeed(VariationsSeed* seed);
-
- // Sets the stored permanent country pref for this client.
- void StorePermanentCountry(const base::Version& version,
- const std::string& country);
-
// Checks if prerequisites for fetching the Variations seed are met, and if
// so, performs the actual fetch using |DoActualFetch|.
void FetchVariationsSeed();
@@ -260,8 +257,10 @@ class VariationsService
std::unique_ptr<variations::VariationsSeed> seed,
const base::Version& version);
- // Record the time of the most recent successful fetch.
- void RecordLastFetchTime();
+ // Records a successful fetch:
+ // (1) Resets failure streaks for Safe Mode.
+ // (2) Records the time of this fetch as the most recent successful fetch.
+ void RecordSuccessfulFetch();
// Loads the country code to use for filtering permanent consistency studies,
// updating the stored country code if the stored value was for a different
@@ -273,7 +272,6 @@ class VariationsService
const std::string& latest_country);
std::unique_ptr<VariationsServiceClient> client_;
- UIStringOverrider ui_string_overrider_;
// The pref service used to store persist the variations seed.
PrefService* local_state_;
@@ -286,8 +284,6 @@ class VariationsService
// either be Local State or Profile prefs.
PrefService* policy_pref_service_;
- VariationsSeedStore seed_store_;
-
// Contains the scheduler instance that handles timing for requests to the
// server. Initially NULL and instantiated when the initial fetch is
// requested.
@@ -305,10 +301,6 @@ class VariationsService
// The URL to use for querying the variations server.
GURL variations_server_url_;
- // Tracks whether |CreateTrialsFromSeed| has been called, to ensure that
- // it gets called prior to |StartRepeatedVariationsSeedFetch|.
- bool create_trials_from_seed_called_;
-
// Tracks whether the initial request to the variations server had completed.
bool initial_request_completed_;
@@ -332,7 +324,10 @@ class VariationsService
// List of observers of the VariationsService.
base::ObserverList<Observer> observer_list_;
- base::ThreadChecker thread_checker_;
+ // Member responsible for creating trials from a variations seed.
+ VariationsFieldTrialCreator field_trial_creator_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<VariationsService> weak_ptr_factory_;
diff --git a/chromium/components/variations/service/variations_service_client.h b/chromium/components/variations/service/variations_service_client.h
index b3758a64b37..c9a269b327f 100644
--- a/chromium/components/variations/service/variations_service_client.h
+++ b/chromium/components/variations/service/variations_service_client.h
@@ -12,10 +12,6 @@
#include "base/version.h"
#include "components/version_info/version_info.h"
-namespace base {
-class SequencedWorkerPool;
-}
-
namespace net {
class URLRequestContextGetter;
}
@@ -35,14 +31,9 @@ class VariationsServiceClient {
// Returns the current application locale (e.g. "en-US").
virtual std::string GetApplicationLocale() = 0;
- // Returns the SequencedWorkerPool on which the VariationsService should run
- // tasks that may block.
- virtual base::SequencedWorkerPool* GetBlockingPool() = 0;
-
// Returns a callback that when run returns the base::Version to use for
// variations seed simulation. VariationsService guarantees that the callback
- // will be run on the pool returned by
- // VariationsServiceClient::GetBlockingPool().
+ // will be run on a background threadt that permits blocking.
virtual base::Callback<base::Version(void)>
GetVersionForSimulationCallback() = 0;
@@ -56,9 +47,6 @@ class VariationsServiceClient {
// |parameter| is an out-param that will contain the value of the restrict
// parameter if true is returned.
virtual bool OverridesRestrictParameter(std::string* parameter) = 0;
-
- // Called from VariationsService::PerformPreMainMessageLoopStartup().
- virtual void OnInitialStartup() {}
};
} // namespace variations
diff --git a/chromium/components/variations/service/variations_service_unittest.cc b/chromium/components/variations/service/variations_service_unittest.cc
index d6293876b09..54718df8434 100644
--- a/chromium/components/variations/service/variations_service_unittest.cc
+++ b/chromium/components/variations/service/variations_service_unittest.cc
@@ -11,17 +11,24 @@
#include <vector>
#include "base/base64.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/json/json_string_value_serializer.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
#include "base/sha1.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/test/histogram_tester.h"
+#include "base/test/scoped_task_environment.h"
#include "base/version.h"
+#include "components/metrics/clean_exit_beacon.h"
+#include "components/metrics/client_info.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/metrics_state_manager.h"
+#include "components/metrics/test_enabled_state_provider.h"
#include "components/prefs/testing_pref_service.h"
#include "components/variations/pref_names.h"
#include "components/variations/proto/study.pb.h"
@@ -34,20 +41,30 @@
#include "testing/gtest/include/gtest/gtest.h"
namespace variations {
-
namespace {
+// A stub for the metrics state manager.
+void StubStoreClientInfo(const metrics::ClientInfo& /* client_info */) {}
+
+// A stub for the metrics state manager.
+std::unique_ptr<metrics::ClientInfo> StubLoadClientInfo() {
+ return std::unique_ptr<metrics::ClientInfo>();
+}
+
+base::Version StubGetVersionForSimulation() {
+ return base::Version();
+}
+
class TestVariationsServiceClient : public VariationsServiceClient {
public:
TestVariationsServiceClient() {}
~TestVariationsServiceClient() override {}
- // variations::VariationsServiceClient:
+ // VariationsServiceClient:
std::string GetApplicationLocale() override { return std::string(); }
- base::SequencedWorkerPool* GetBlockingPool() override { return nullptr; }
base::Callback<base::Version(void)> GetVersionForSimulationCallback()
override {
- return base::Callback<base::Version(void)>();
+ return base::Bind(&StubGetVersionForSimulation);
}
net::URLRequestContextGetter* GetURLRequestContext() override {
return nullptr;
@@ -64,7 +81,6 @@ class TestVariationsServiceClient : public VariationsServiceClient {
*parameter = restrict_parameter_;
return true;
}
- void OnInitialStartup() override {}
void set_restrict_parameter(const std::string& value) {
restrict_parameter_ = value;
@@ -81,11 +97,12 @@ class TestVariationsService : public VariationsService {
public:
TestVariationsService(
std::unique_ptr<web_resource::TestRequestAllowedNotifier> test_notifier,
- PrefService* local_state)
+ PrefService* local_state,
+ metrics::MetricsStateManager* state_manager)
: VariationsService(base::WrapUnique(new TestVariationsServiceClient()),
std::move(test_notifier),
local_state,
- NULL,
+ state_manager,
UIStringOverrider()),
intercepts_fetch_(true),
fetch_attempted_(false),
@@ -139,11 +156,6 @@ class TestVariationsService : public VariationsService {
}
private:
- bool LoadSeed(VariationsSeed* seed) override {
- if (!seed_stored_)
- return false;
- return seed->ParseFromString(stored_seed_data_);
- }
bool intercepts_fetch_;
bool fetch_attempted_;
@@ -159,9 +171,7 @@ class TestVariationsService : public VariationsService {
class TestVariationsServiceObserver : public VariationsService::Observer {
public:
TestVariationsServiceObserver()
- : best_effort_changes_notified_(0),
- crticial_changes_notified_(0) {
- }
+ : best_effort_changes_notified_(0), crticial_changes_notified_(0) {}
~TestVariationsServiceObserver() override {}
void OnExperimentChangesDetected(Severity severity) override {
@@ -203,12 +213,12 @@ const char kTestSeedSerialNumber[] = "123";
// study called "test", which contains one experiment called "abc" with
// probability weight 100. |seed|'s study field will be cleared before adding
// the new study.
-variations::VariationsSeed CreateTestSeed() {
- variations::VariationsSeed seed;
- variations::Study* study = seed.add_study();
+VariationsSeed CreateTestSeed() {
+ VariationsSeed seed;
+ Study* study = seed.add_study();
study->set_name(kTestSeedStudyName);
study->set_default_experiment_name(kTestSeedExperimentName);
- variations::Study_Experiment* experiment = study->add_experiment();
+ Study_Experiment* experiment = study->add_experiment();
experiment->set_name(kTestSeedExperimentName);
experiment->set_probability_weight(kTestSeedExperimentProbability);
seed.set_serial_number(kTestSeedSerialNumber);
@@ -216,7 +226,7 @@ variations::VariationsSeed CreateTestSeed() {
}
// Serializes |seed| to protobuf binary format.
-std::string SerializeSeed(const variations::VariationsSeed& seed) {
+std::string SerializeSeed(const VariationsSeed& seed) {
std::string serialized_seed;
seed.SerializeToString(&serialized_seed);
return serialized_seed;
@@ -260,97 +270,37 @@ std::string ListValueToString(const base::ListValue& list_value) {
class VariationsServiceTest : public ::testing::Test {
protected:
- VariationsServiceTest() {}
+ VariationsServiceTest()
+ : enabled_state_provider_(
+ new metrics::TestEnabledStateProvider(false, false)) {
+ VariationsService::RegisterPrefs(prefs_.registry());
+ metrics::CleanExitBeacon::RegisterPrefs(prefs_.registry());
+ metrics::MetricsStateManager::RegisterPrefs(prefs_.registry());
+ }
+
+ metrics::MetricsStateManager* GetMetricsStateManager() {
+ // Lazy-initialize the metrics_state_manager so that it correctly reads the
+ // stability state from prefs after tests have a chance to initialize it.
+ if (!metrics_state_manager_) {
+ metrics_state_manager_ = metrics::MetricsStateManager::Create(
+ &prefs_, enabled_state_provider_.get(), base::string16(),
+ base::Bind(&StubStoreClientInfo), base::Bind(&StubLoadClientInfo));
+ }
+ return metrics_state_manager_.get();
+ }
+
+ protected:
+ TestingPrefServiceSimple prefs_;
private:
- base::MessageLoop message_loop_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ std::unique_ptr<metrics::TestEnabledStateProvider> enabled_state_provider_;
+ std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager_;
DISALLOW_COPY_AND_ASSIGN(VariationsServiceTest);
};
-TEST_F(VariationsServiceTest, CreateTrialsFromSeed) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
- // Create a local base::FieldTrialList, to hold the field trials created in
- // this test.
- base::FieldTrialList field_trial_list(nullptr);
-
- // Create a variations service.
- TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
- service.SetCreateTrialsFromSeedCalledForTesting(false);
-
- // Store a seed.
- service.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
- std::string(), base::Time::Now(), false, false);
- prefs.SetInt64(prefs::kVariationsLastFetchTime,
- base::Time::Now().ToInternalValue());
-
- // Check that field trials are created from the seed. Since the test study has
- // only 1 experiment with 100% probability weight, we must be part of it.
- EXPECT_TRUE(service.CreateTrialsFromSeed(base::FeatureList::GetInstance()));
- EXPECT_EQ(kTestSeedExperimentName,
- base::FieldTrialList::FindFullName(kTestSeedStudyName));
-}
-
-TEST_F(VariationsServiceTest, CreateTrialsFromSeedNoLastFetchTime) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
- // Create a local base::FieldTrialList, to hold the field trials created in
- // this test.
- base::FieldTrialList field_trial_list(nullptr);
-
- // Create a variations service
- TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
- service.SetCreateTrialsFromSeedCalledForTesting(false);
-
- // Store a seed. To simulate a first run, |prefs::kVariationsLastFetchTime|
- // is left empty.
- service.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
- std::string(), base::Time::Now(), false, false);
- EXPECT_EQ(0, prefs.GetInt64(prefs::kVariationsLastFetchTime));
-
- // Check that field trials are created from the seed. Since the test study has
- // only 1 experiment with 100% probability weight, we must be part of it.
- EXPECT_TRUE(service.CreateTrialsFromSeed(base::FeatureList::GetInstance()));
- EXPECT_EQ(base::FieldTrialList::FindFullName(kTestSeedStudyName),
- kTestSeedExperimentName);
-}
-
-TEST_F(VariationsServiceTest, CreateTrialsFromOutdatedSeed) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
- // Create a local base::FieldTrialList, to hold the field trials created in
- // this test.
- base::FieldTrialList field_trial_list(nullptr);
-
- // Create a variations service.
- TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
- service.SetCreateTrialsFromSeedCalledForTesting(false);
-
- // Store a seed, with a fetch time 31 days in the past.
- const base::Time seed_date =
- base::Time::Now() - base::TimeDelta::FromDays(31);
- service.StoreSeed(SerializeSeed(CreateTestSeed()), std::string(),
- std::string(), seed_date, false, false);
- prefs.SetInt64(prefs::kVariationsLastFetchTime, seed_date.ToInternalValue());
-
- // Check that field trials are not created from the seed.
- EXPECT_FALSE(service.CreateTrialsFromSeed(base::FeatureList::GetInstance()));
- EXPECT_TRUE(base::FieldTrialList::FindFullName(kTestSeedStudyName).empty());
-}
-
TEST_F(VariationsServiceTest, GetVariationsServerURL) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
const std::string default_variations_url =
VariationsService::GetDefaultVariationsServerURLForTesting();
@@ -360,31 +310,31 @@ TEST_F(VariationsServiceTest, GetVariationsServerURL) {
TestVariationsServiceClient* raw_client = client.get();
VariationsService service(
std::move(client),
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs, NULL, UIStringOverrider());
- GURL url = service.GetVariationsServerURL(&prefs, std::string());
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager(), UIStringOverrider());
+ GURL url = service.GetVariationsServerURL(&prefs_, std::string());
EXPECT_TRUE(base::StartsWith(url.spec(), default_variations_url,
base::CompareCase::SENSITIVE));
EXPECT_FALSE(net::GetValueForKeyInQuery(url, "restrict", &value));
- prefs.SetString(prefs::kVariationsRestrictParameter, "restricted");
- url = service.GetVariationsServerURL(&prefs, std::string());
+ prefs_.SetString(prefs::kVariationsRestrictParameter, "restricted");
+ url = service.GetVariationsServerURL(&prefs_, std::string());
EXPECT_TRUE(base::StartsWith(url.spec(), default_variations_url,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value));
EXPECT_EQ("restricted", value);
- // A client override should take precedence over what's in prefs.
+ // A client override should take precedence over what's in prefs_.
raw_client->set_restrict_parameter("client");
- url = service.GetVariationsServerURL(&prefs, std::string());
+ url = service.GetVariationsServerURL(&prefs_, std::string());
EXPECT_TRUE(base::StartsWith(url.spec(), default_variations_url,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value));
EXPECT_EQ("client", value);
// The override value passed to the method should take precedence over
- // what's in prefs and a client override.
- url = service.GetVariationsServerURL(&prefs, "override");
+ // what's in prefs_ and a client override.
+ url = service.GetVariationsServerURL(&prefs_, "override");
EXPECT_TRUE(base::StartsWith(url.spec(), default_variations_url,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value));
@@ -392,12 +342,10 @@ TEST_F(VariationsServiceTest, GetVariationsServerURL) {
}
TEST_F(VariationsServiceTest, VariationsURLHasOSNameParam) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
- const GURL url = service.GetVariationsServerURL(&prefs, std::string());
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+ const GURL url = service.GetVariationsServerURL(&prefs_, std::string());
std::string value;
EXPECT_TRUE(net::GetValueForKeyInQuery(url, "osname", &value));
@@ -405,15 +353,13 @@ TEST_F(VariationsServiceTest, VariationsURLHasOSNameParam) {
}
TEST_F(VariationsServiceTest, RequestsInitiallyNotAllowed) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
// Pass ownership to TestVariationsService, but keep a weak pointer to
// manipulate it for this test.
std::unique_ptr<web_resource::TestRequestAllowedNotifier> test_notifier =
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_);
web_resource::TestRequestAllowedNotifier* raw_notifier = test_notifier.get();
- TestVariationsService test_service(std::move(test_notifier), &prefs);
+ TestVariationsService test_service(std::move(test_notifier), &prefs_,
+ GetMetricsStateManager());
// Force the notifier to initially disallow requests.
raw_notifier->SetRequestsAllowedOverride(false);
@@ -425,15 +371,13 @@ TEST_F(VariationsServiceTest, RequestsInitiallyNotAllowed) {
}
TEST_F(VariationsServiceTest, RequestsInitiallyAllowed) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
// Pass ownership to TestVariationsService, but keep a weak pointer to
// manipulate it for this test.
std::unique_ptr<web_resource::TestRequestAllowedNotifier> test_notifier =
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_);
web_resource::TestRequestAllowedNotifier* raw_notifier = test_notifier.get();
- TestVariationsService test_service(std::move(test_notifier), &prefs);
+ TestVariationsService test_service(std::move(test_notifier), &prefs_,
+ GetMetricsStateManager());
raw_notifier->SetRequestsAllowedOverride(true);
test_service.StartRepeatedVariationsSeedFetch();
@@ -441,12 +385,9 @@ TEST_F(VariationsServiceTest, RequestsInitiallyAllowed) {
}
TEST_F(VariationsServiceTest, SeedStoredWhenOKStatus) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
service.set_intercepts_fetch(false);
net::TestURLFetcherFactory factory;
@@ -470,34 +411,31 @@ TEST_F(VariationsServiceTest, SeedNotStoredWhenNonOKStatus) {
net::HTTP_SERVICE_UNAVAILABLE,
};
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
service.set_intercepts_fetch(false);
for (size_t i = 0; i < arraysize(non_ok_status_codes); ++i) {
net::TestURLFetcherFactory factory;
service.DoActualFetch();
- EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue());
+ EXPECT_TRUE(prefs_.FindPreference(prefs::kVariationsCompressedSeed)
+ ->IsDefaultValue());
net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
SimulateServerResponse(non_ok_status_codes[i], fetcher);
service.OnURLFetchComplete(fetcher);
- EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue());
+ EXPECT_TRUE(prefs_.FindPreference(prefs::kVariationsCompressedSeed)
+ ->IsDefaultValue());
}
}
TEST_F(VariationsServiceTest, RequestGzipCompressedSeed) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
net::TestURLFetcherFactory factory;
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
service.set_intercepts_fetch(false);
service.DoActualFetch();
@@ -525,15 +463,13 @@ TEST_F(VariationsServiceTest, InstanceManipulations) {
{"IM:deflate,x-bm,gzip", false, false, false},
};
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
std::string serialized_seed = SerializeSeed(CreateTestSeed());
net::TestURLFetcherFactory factory;
for (size_t i = 0; i < arraysize(cases); ++i) {
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
service.set_intercepts_fetch(false);
service.DoActualFetch();
net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
@@ -552,12 +488,9 @@ TEST_F(VariationsServiceTest, InstanceManipulations) {
}
TEST_F(VariationsServiceTest, CountryHeader) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
-
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
service.set_intercepts_fetch(false);
net::TestURLFetcherFactory factory;
@@ -576,12 +509,10 @@ TEST_F(VariationsServiceTest, CountryHeader) {
}
TEST_F(VariationsServiceTest, Observer) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
VariationsService service(
base::MakeUnique<TestVariationsServiceClient>(),
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs, NULL, UIStringOverrider());
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager(), UIStringOverrider());
struct {
int normal_count;
@@ -607,7 +538,7 @@ TEST_F(VariationsServiceTest, Observer) {
TestVariationsServiceObserver observer;
service.AddObserver(&observer);
- variations::VariationsSeedSimulator::Result result;
+ VariationsSeedSimulator::Result result;
result.normal_group_change_count = cases[i].normal_count;
result.kill_best_effort_group_change_count = cases[i].best_effort_count;
result.kill_critical_group_change_count = cases[i].critical_count;
@@ -676,24 +607,24 @@ TEST_F(VariationsServiceTest, LoadPermanentConsistencyCountry) {
};
for (const auto& test : test_cases) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
VariationsService service(
base::MakeUnique<TestVariationsServiceClient>(),
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs, NULL, UIStringOverrider());
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager(), UIStringOverrider());
- if (test.pref_value_before) {
+ if (!test.pref_value_before) {
+ prefs_.ClearPref(prefs::kVariationsPermanentConsistencyCountry);
+ } else {
base::ListValue list_value;
for (const std::string& component :
base::SplitString(test.pref_value_before, ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL)) {
list_value.AppendString(component);
}
- prefs.Set(prefs::kVariationsPermanentConsistencyCountry, list_value);
+ prefs_.Set(prefs::kVariationsPermanentConsistencyCountry, list_value);
}
- variations::VariationsSeed seed(CreateTestSeed());
+ VariationsSeed seed(CreateTestSeed());
std::string latest_country;
if (test.latest_country_code)
latest_country = test.latest_country_code;
@@ -712,7 +643,7 @@ TEST_F(VariationsServiceTest, LoadPermanentConsistencyCountry) {
expected_list_value.AppendString(component);
}
const base::ListValue* pref_value =
- prefs.GetList(prefs::kVariationsPermanentConsistencyCountry);
+ prefs_.GetList(prefs::kVariationsPermanentConsistencyCountry);
EXPECT_EQ(ListValueToString(expected_list_value),
ListValueToString(*pref_value))
<< test.pref_value_before << ", " << test.version << ", "
@@ -748,23 +679,23 @@ TEST_F(VariationsServiceTest, OverrideStoredPermanentCountry) {
};
for (const auto& test : test_cases) {
- TestingPrefServiceSimple prefs;
- VariationsService::RegisterPrefs(prefs.registry());
TestVariationsService service(
- base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs),
- &prefs);
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
- if (!test.pref_value_before.empty()) {
+ if (test.pref_value_before.empty()) {
+ prefs_.ClearPref(prefs::kVariationsPermanentConsistencyCountry);
+ } else {
base::ListValue list_value;
for (const std::string& component :
base::SplitString(test.pref_value_before, ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL)) {
list_value.AppendString(component);
}
- prefs.Set(prefs::kVariationsPermanentConsistencyCountry, list_value);
+ prefs_.Set(prefs::kVariationsPermanentConsistencyCountry, list_value);
}
- variations::VariationsSeed seed(CreateTestSeed());
+ VariationsSeed seed(CreateTestSeed());
EXPECT_EQ(test.has_updated, service.OverrideStoredPermanentCountry(
test.country_code_override))
@@ -777,11 +708,228 @@ TEST_F(VariationsServiceTest, OverrideStoredPermanentCountry) {
expected_list_value.AppendString(component);
}
const base::ListValue* pref_value =
- prefs.GetList(prefs::kVariationsPermanentConsistencyCountry);
+ prefs_.GetList(prefs::kVariationsPermanentConsistencyCountry);
EXPECT_EQ(ListValueToString(expected_list_value),
ListValueToString(*pref_value))
<< test.pref_value_before << ", " << test.country_code_override;
}
}
+TEST_F(VariationsServiceTest, SafeMode_NoPrefs) {
+ // Create a variations service.
+ base::HistogramTester histogram_tester;
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.FellBackToSafeMode",
+ false, 1);
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 0,
+ 1);
+ histogram_tester.ExpectUniqueSample(
+ "Variations.SafeMode.Streak.FetchFailures", 0, 1);
+}
+
+TEST_F(VariationsServiceTest, SafeMode_NoCrashes_NoFetchFailures) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 0);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 0);
+
+ // Create a variations service.
+ base::HistogramTester histogram_tester;
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.FellBackToSafeMode",
+ false, 1);
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 0,
+ 1);
+ histogram_tester.ExpectUniqueSample(
+ "Variations.SafeMode.Streak.FetchFailures", 0, 1);
+}
+
+TEST_F(VariationsServiceTest, SafeMode_SomeCrashes_SomeFetchFailures) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 1);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 2);
+
+ // Create a variations service.
+ base::HistogramTester histogram_tester;
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.FellBackToSafeMode",
+ false, 1);
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 1,
+ 1);
+ histogram_tester.ExpectUniqueSample(
+ "Variations.SafeMode.Streak.FetchFailures", 2, 1);
+}
+
+TEST_F(VariationsServiceTest, SafeMode_NoCrashes_ManyFetchFailures) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 0);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 3);
+
+ // Create a variations service.
+ base::HistogramTester histogram_tester;
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.FellBackToSafeMode",
+ true, 1);
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 0,
+ 1);
+ histogram_tester.ExpectUniqueSample(
+ "Variations.SafeMode.Streak.FetchFailures", 3, 1);
+}
+
+TEST_F(VariationsServiceTest, SafeMode_ManyCrashes_NoFetchFailures) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 3);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 0);
+
+ // Create a variations service.
+ base::HistogramTester histogram_tester;
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.FellBackToSafeMode",
+ true, 1);
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 3,
+ 1);
+ histogram_tester.ExpectUniqueSample(
+ "Variations.SafeMode.Streak.FetchFailures", 0, 1);
+}
+
+TEST_F(VariationsServiceTest, SafeMode_OverriddenByCommandlineFlag) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 3);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 3);
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ ::switches::kForceFieldTrials, "SomeFieldTrial");
+
+ // Create a variations service.
+ base::HistogramTester histogram_tester;
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.FellBackToSafeMode",
+ false, 1);
+ histogram_tester.ExpectUniqueSample("Variations.SafeMode.Streak.Crashes", 3,
+ 1);
+ histogram_tester.ExpectUniqueSample(
+ "Variations.SafeMode.Streak.FetchFailures", 3, 1);
+}
+
+TEST_F(VariationsServiceTest, SafeMode_CrashIncrementsCrashStreak) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 1);
+ prefs_.SetBoolean(metrics::prefs::kStabilityExitedCleanly, false);
+
+ // Create a variations service.
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ EXPECT_EQ(2, prefs_.GetInteger(prefs::kVariationsCrashStreak));
+}
+
+TEST_F(VariationsServiceTest, SafeMode_NoCrashPreservesCrashStreak) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 1);
+ prefs_.SetBoolean(metrics::prefs::kStabilityExitedCleanly, true);
+
+ // Create a variations service.
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ EXPECT_EQ(1, prefs_.GetInteger(prefs::kVariationsCrashStreak));
+}
+
+TEST_F(VariationsServiceTest, SafeMode_StartingRequestIncrementsFetchFailures) {
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 1);
+
+ // Create a variations service and start the fetch.
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+ service.set_intercepts_fetch(false);
+ net::TestURLFetcherFactory factory;
+ service.DoActualFetch();
+
+ EXPECT_EQ(2, prefs_.GetInteger(prefs::kVariationsFailedToFetchSeedStreak));
+}
+
+TEST_F(VariationsServiceTest, SafeMode_SuccessfulFetchClearsFailureStreaks) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 2);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 1);
+
+ // Create a variations service and perform a successful fetch.
+ VariationsService service(
+ base::MakeUnique<TestVariationsServiceClient>(),
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager(), UIStringOverrider());
+
+ service.SetCreateTrialsFromSeedCalledForTesting(true);
+ net::TestURLFetcherFactory factory;
+ // This will actually start the fetch.
+ service.PerformPreMainMessageLoopStartup();
+
+ // The below seed and signature pair were generated using the server's
+ // private key.
+ const std::string base64_seed_data =
+ "CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p"
+ "Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB"
+ "SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0"
+ "EAFKDAoIZ3JvdXBfMDUQAUoMCghncm91cF8wNhABSgwKCGdyb3VwXzA3EAFKDAoIZ3JvdXBf"
+ "MDgQAUoMCghncm91cF8wORAB";
+ const std::string base64_seed_signature =
+ "MEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy"
+ "96JkMYgzTkHPwbv7K/CmgA==";
+
+ std::string response;
+ ASSERT_TRUE(base::Base64Decode(base64_seed_data, &response));
+ const std::string header = "X-Seed-Signature:" + base64_seed_signature;
+
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ SimulateServerResponseWithHeader(net::HTTP_OK, fetcher, &header);
+ fetcher->SetResponseString(response);
+ service.OnURLFetchComplete(fetcher);
+
+ // Verify that the streaks were rest.
+ EXPECT_EQ(0, prefs_.GetInteger(prefs::kVariationsCrashStreak));
+ EXPECT_EQ(0, prefs_.GetInteger(prefs::kVariationsFailedToFetchSeedStreak));
+}
+
+TEST_F(VariationsServiceTest, SafeMode_NotModifiedFetchClearsFailureStreaks) {
+ prefs_.SetInteger(prefs::kVariationsCrashStreak, 2);
+ prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 1);
+
+ // Create a variations service and perform a successful fetch.
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+ service.set_intercepts_fetch(false);
+
+ net::TestURLFetcherFactory factory;
+ service.DoActualFetch();
+
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ SimulateServerResponse(net::HTTP_NOT_MODIFIED, fetcher);
+ service.OnURLFetchComplete(fetcher);
+
+ EXPECT_EQ(0, prefs_.GetInteger(prefs::kVariationsCrashStreak));
+ EXPECT_EQ(0, prefs_.GetInteger(prefs::kVariationsFailedToFetchSeedStreak));
+}
+
+TEST_F(VariationsServiceTest, FieldTrialCreatorInitializedCorrectly) {
+ TestVariationsService service(
+ base::MakeUnique<web_resource::TestRequestAllowedNotifier>(&prefs_),
+ &prefs_, GetMetricsStateManager());
+
+ // Call will crash in service's VariationsFieldTrialCreator if not initialized
+ // correctly.
+ service.GetClientFilterableStateForVersionCalledForTesting();
+}
+
} // namespace variations