blob: bf5e9eeaf327bccb029aac1a573106ddf6cf0629 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/variations/variations_test_utils.h"
#include "base/base64.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "components/metrics/clean_exit_beacon.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/variations/client_filterable_state.h"
#include "components/variations/field_trial_config/fieldtrial_testing_config.h"
#include "components/variations/pref_names.h"
#include "components/variations/proto/client_variations.pb.h"
#include "components/variations/variations_associated_data.h"
#include "components/variations/variations_switches.h"
#include "third_party/zlib/google/compression_utils.h"
namespace variations {
namespace {
// kTestSeed is a simple VariationsSeed containing:
// serial_number: "test"
// study: {
// name: "UMA-Uniformity-Trial-50-Percent"
// consistency: PERMANENT
// experiment: {
// name: "default"
// probability_weight: 1
// }
// experiment: {
// name: "group_01"
// probability_weight: 1
// }
// }
const char* kTestSeed_StudyNames[] = {"UMA-Uniformity-Trial-50-Percent"};
const char kTestSeed_Base64UncompressedData[] =
"CgR0ZXN0Ej4KH1VNQS1Vbmlmb3JtaXR5LVRyaWFsLTUwLVBlcmNlbnQ4AUoLCgdkZWZhdWx0EA"
"FKDAoIZ3JvdXBfMDEQAQ==";
const char kTestSeed_Base64CompressedData[] =
"H4sIAAAAAAAA/+JiKUktLhGy45IP9XXUDc3LTMsvys0sqdQNKcpMzNE1NdANSC1KTs0rsWD04u"
"ZiT0lNSyzNKRFg9OLh4kgvyi8tiDcwFGAEBAAA//90/JgERgAAAA==";
const char kTestSeed_Base64Signature[] =
"MEUCIQD5AEAzk5qEuE3xOZl+xSZR15Ac1RJpsXMiou7i5W0sMAIgRn++ngh03HaMGC+Pjl9NOu"
"Doxf83qsSwycF2PSS1nYQ=";
const char* kCrashingSeed_StudyNames[] = {"CrashingStudy"};
// kCrashingSeed is a VariationsSeed that triggers a crash for testing:
// serial_number: "35ed2d9e354b414befdf930a734094019c0162f1"
// study: {
// name: "CrashingStudy"
// consistency: PERMANENT
// experiment: {
// name: "EnabledLaunch"
// probability_weight: 100
// feature_association: {
// enable_feature: "ForceFieldTrialSetupCrashForTesting"
// }
// }
// experiment: {
// name: "ForcedOn_ForceFieldTrialSetupCrashForTesting"
// probability_weight: 0
// feature_association: {
// forcing_feature_on: "ForceFieldTrialSetupCrashForTesting"
// }
// }
// experiment: {
// name: "ForcedOff_ForceFieldTrialSetupCrashForTesting"
// probability_weight: 0
// feature_association: {
// forcing_feature_off: "ForceFieldTrialSetupCrashForTesting"
// }
// }
// filter: {
// min_version: "91.*"
// channel: CANARY
// channel: DEV
// channel: BETA
// channel: STABLE
// platform: PLATFORM_ANDROID
// platform: PLATFORM_IOS
// platform: PLATFORM_ANDROID_WEBVIEW
// platform: PLATFORM_WINDOWS
// platform: PLATFORM_MAC
// platform: PLATFORM_LINUX
// platform: PLATFORM_CHROMEOS
// platform: PLATFORM_CHROMEOS_LACROS
// }
// }
// version: "hash/4aa56a1dc30dfc767615248d6fee29830198b276"
const char kCrashingSeed_Base64UncompressedData[] =
"CigzNWVkMmQ5ZTM1NGI0MTRiZWZkZjkzMGE3MzQwOTQwMTljMDE2MmYxEp4CCg1DcmFzaGluZ1"
"N0dWR5OAFKOAoNRW5hYmxlZExhdW5jaBBkYiUKI0ZvcmNlRmllbGRUcmlhbFNldHVwQ3Jhc2hG"
"b3JUZXN0aW5nSlcKLEZvcmNlZE9uX0ZvcmNlRmllbGRUcmlhbFNldHVwQ3Jhc2hGb3JUZXN0aW"
"5nEABiJRojRm9yY2VGaWVsZFRyaWFsU2V0dXBDcmFzaEZvclRlc3RpbmdKWAotRm9yY2VkT2Zm"
"X0ZvcmNlRmllbGRUcmlhbFNldHVwQ3Jhc2hGb3JUZXN0aW5nEABiJSIjRm9yY2VGaWVsZFRyaW"
"FsU2V0dXBDcmFzaEZvclRlc3RpbmdSHhIEOTEuKiAAIAEgAiADKAQoBSgGKAAoASgCKAMoCSIt"
"aGFzaC80YWE1NmExZGMzMGRmYzc2NzYxNTI0OGQ2ZmVlMjk4MzAxOThiMjc2";
const char kCrashingSeed_Base64CompressedData[] =
"H4sIAAAAAAAAAI3QwUvDMBTH8babwgKDsaMHKZNBEKdJk6bJWbbDEAQ30JskeS+2UKp07cF/"
"Zn+rZfgH9Py+73P4ESpyhAwMilw6yaXDAMEIZgshmZGMG8+4ygJfnhMyf27tqayar0PXw6+"
"O95rMt411NcKL7RtfLsCtyd3uu/W4q7CGY1vZ+oBd/"
"3P5HA5HPHUDsH8nD5cMXpvPEf0icuubUfAH2fzDIYyVV2Pkt9vl1PDH+zRK4zRJJ3RKr+"
"g1jWhMEzqhs9WmHPonaW2uLAcvGARfqELxPJMaVEDMjBbDotplhfoDs9NLbnoBAAA=";
const char kCrashingSeed_Base64Signature[] =
"MEQCIEn1+VsBfNA93dxzpk+BLhdO91kMQnofxfTK5Uo8vDi8AiAnTCFCIPgEGWNOKzuKfNWn6"
"emB6pnGWjSTbI/pvfxHnw==";
// Create mock testing config equivalent to:
// {
// "UnitTest": [
// {
// "platforms": [
// "android",
// "android_weblayer",
// "android_webview",
// "chromeos",
// "chromeos_lacros",
// "fuchsia",
// "ios",
// "linux",
// "mac",
// "windows"
// ],
// "experiments": [
// {
// "name": "Enabled",
// "params": {
// "x": "1"
// },
// "enable_features": [
// "UnitTestEnabled"
// ]
// }
// ]
// }
// ]
// }
const Study::Platform array_kFieldTrialConfig_platforms_0[] = {
Study::PLATFORM_ANDROID,
Study::PLATFORM_ANDROID_WEBLAYER,
Study::PLATFORM_ANDROID_WEBVIEW,
Study::PLATFORM_CHROMEOS,
Study::PLATFORM_CHROMEOS_LACROS,
Study::PLATFORM_FUCHSIA,
Study::PLATFORM_IOS,
Study::PLATFORM_LINUX,
Study::PLATFORM_MAC,
Study::PLATFORM_WINDOWS,
};
const char* enable_features_0[] = {"UnitTestEnabled"};
const FieldTrialTestingExperimentParams array_kFieldTrialConfig_params_0[] = {
{
"x",
"1",
},
};
const FieldTrialTestingExperiment array_kFieldTrialConfig_experiments_0[] = {
{/*name=*/"Enabled",
/*platforms=*/array_kFieldTrialConfig_platforms_0,
/*platforms_size=*/10,
/*form_factors=*/{},
/*form_factors_size=*/0,
/*is_low_end_device=*/absl::nullopt,
/*min_os_version=*/nullptr,
/*params=*/array_kFieldTrialConfig_params_0,
/*params_size=*/1,
/*enable_features=*/enable_features_0,
/*enable_features_size=*/1,
/*disable_features=*/nullptr,
/*disable_features_size=*/0,
/*forcing_flag=*/nullptr,
/*override_ui_string=*/nullptr,
/*override_ui_string_size=*/0},
};
const FieldTrialTestingStudy array_kFieldTrialConfig_studies[] = {
{/*name=*/"UnitTest",
/*experiments=*/array_kFieldTrialConfig_experiments_0,
/*experiments_size=*/1},
};
} // namespace
const SignedSeedData kTestSeedData{
kTestSeed_StudyNames, kTestSeed_Base64UncompressedData,
kTestSeed_Base64CompressedData, kTestSeed_Base64Signature};
const SignedSeedData kCrashingSeedData{
kCrashingSeed_StudyNames, kCrashingSeed_Base64UncompressedData,
kCrashingSeed_Base64CompressedData, kCrashingSeed_Base64Signature};
const SignedSeedPrefKeys kSafeSeedPrefKeys{prefs::kVariationsSafeCompressedSeed,
prefs::kVariationsSafeSeedSignature};
const SignedSeedPrefKeys kRegularSeedPrefKeys{prefs::kVariationsCompressedSeed,
prefs::kVariationsSeedSignature};
SignedSeedData::SignedSeedData(base::span<const char*> in_study_names,
const char* in_base64_uncompressed_data,
const char* in_base64_compressed_data,
const char* in_base64_signature)
: study_names(std::move(in_study_names)),
base64_uncompressed_data(in_base64_uncompressed_data),
base64_compressed_data(in_base64_compressed_data),
base64_signature(in_base64_signature) {}
SignedSeedData::~SignedSeedData() = default;
SignedSeedData::SignedSeedData(const SignedSeedData&) = default;
SignedSeedData::SignedSeedData(SignedSeedData&&) = default;
SignedSeedData& SignedSeedData::operator=(const SignedSeedData&) = default;
SignedSeedData& SignedSeedData::operator=(SignedSeedData&&) = default;
void DisableTestingConfig() {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableFieldTrialTestingConfig);
}
void EnableTestingConfig() {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableFieldTrialTestingConfig);
}
bool ExtractVariationIds(const std::string& variations,
std::set<VariationID>* variation_ids,
std::set<VariationID>* trigger_ids) {
std::string serialized_proto;
if (!base::Base64Decode(variations, &serialized_proto))
return false;
ClientVariations proto;
if (!proto.ParseFromString(serialized_proto))
return false;
for (int i = 0; i < proto.variation_id_size(); ++i)
variation_ids->insert(proto.variation_id(i));
for (int i = 0; i < proto.trigger_variation_id_size(); ++i)
trigger_ids->insert(proto.trigger_variation_id(i));
return true;
}
scoped_refptr<base::FieldTrial> CreateTrialAndAssociateId(
const std::string& trial_name,
const std::string& default_group_name,
IDCollectionKey key,
VariationID id) {
AssociateGoogleVariationID(key, trial_name, default_group_name, id);
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::CreateFieldTrial(trial_name, default_group_name));
DCHECK(trial);
if (trial) {
// Ensure the trial is registered under the correct key so we can look it
// up.
trial->Activate();
}
return trial;
}
void SimulateCrash(PrefService* local_state) {
local_state->SetBoolean(metrics::prefs::kStabilityExitedCleanly, false);
metrics::CleanExitBeacon::SkipCleanShutdownStepsForTesting();
}
void WriteSeedData(PrefService* local_state,
const SignedSeedData& seed_data,
const SignedSeedPrefKeys& pref_keys) {
local_state->SetString(pref_keys.base64_compressed_data_key,
seed_data.base64_compressed_data);
local_state->SetString(pref_keys.base64_signature_key,
seed_data.base64_signature);
local_state->CommitPendingWrite();
}
bool FieldTrialListHasAllStudiesFrom(const SignedSeedData& seed_data) {
return base::ranges::all_of(seed_data.study_names, [](const char* study) {
return base::FieldTrialList::TrialExists(study);
});
}
void ResetVariations() {
testing::ClearAllVariationIDs();
testing::ClearAllVariationParams();
}
const FieldTrialTestingConfig kTestingConfig = {
array_kFieldTrialConfig_studies,
1,
};
std::unique_ptr<ClientFilterableState> CreateDummyClientFilterableState() {
auto client_state = std::make_unique<ClientFilterableState>(
base::BindOnce([] { return false; }),
base::BindOnce([] { return base::flat_set<uint64_t>(); }));
client_state->locale = "en-CA";
client_state->reference_date = base::Time::Now();
client_state->version = base::Version("20.0.0.0");
client_state->channel = Study::STABLE;
client_state->form_factor = Study::PHONE;
client_state->platform = Study::PLATFORM_ANDROID;
return client_state;
}
MockEntropyProviders::MockEntropyProviders(
MockEntropyProviders::Results results,
uint32_t low_entropy_domain)
: EntropyProviders(results.high_entropy.has_value() ? "client_id" : "",
{0, low_entropy_domain}),
low_provider_(results.low_entropy),
high_provider_(results.high_entropy.value_or(0)) {}
MockEntropyProviders::~MockEntropyProviders() = default;
const base::FieldTrial::EntropyProvider& MockEntropyProviders::low_entropy()
const {
return low_provider_;
}
const base::FieldTrial::EntropyProvider& MockEntropyProviders::default_entropy()
const {
if (default_entropy_is_high_entropy()) {
return high_provider_;
}
return low_provider_;
}
} // namespace variations