diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-09-18 14:34:04 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-10-04 11:15:27 +0000 |
commit | e6430e577f105ad8813c92e75c54660c4985026e (patch) | |
tree | 88115e5d1fb471fea807111924dcccbeadbf9e4f /chromium/components/variations/service | |
parent | 53d399fe6415a96ea6986ec0d402a9c07da72453 (diff) | |
download | qtwebengine-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')
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 |