blob: da3fd302e4cf3ebcd21a7a4a0428ac8de32b26e5 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/public/test/setup_field_trials.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "cc/base/switches.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics/metrics_state_manager.h"
#include "components/metrics/test/test_enabled_state_provider.h"
#include "components/prefs/in_memory_pref_store.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/pref_service_factory.h"
#include "components/variations/platform_field_trials.h"
#include "components/variations/pref_names.h"
#include "components/variations/scoped_variations_ids_provider.h"
#include "components/variations/service/safe_seed_manager.h"
#include "components/variations/service/variations_field_trial_creator.h"
#include "components/variations/service/variations_service.h"
#include "components/variations/service/variations_service_client.h"
#include "components/variations/variations_ids_provider.h"
#include "components/variations/variations_safe_seed_store_local_state.h"
#include "components/variations/variations_switches.h"
#include "content/public/common/content_switch_dependent_feature_overrides.h"
#if BUILDFLAG(IS_ANDROID)
#include "components/variations/android/variations_seed_bridge.h"
#endif
namespace content {
namespace {
// TODO(crbug.com/40772375): Consider not needing VariationsServiceClient just
// to use VariationsFieldTrialCreator.
class VariationServiceClient : public variations::VariationsServiceClient {
public:
explicit VariationServiceClient(base::FilePath user_data_dir)
: user_data_dir_(std::move(user_data_dir)) {}
~VariationServiceClient() override = default;
VariationServiceClient(const VariationServiceClient&) = delete;
VariationServiceClient(VariationServiceClient&&) = delete;
VariationServiceClient& operator=(const VariationServiceClient&) = delete;
VariationServiceClient& operator=(VariationServiceClient&&) = delete;
// variations::VariationsServiceClient:
base::Version GetVersionForSimulation() final { return base::Version(); }
scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() final {
return nullptr;
}
network_time::NetworkTimeTracker* GetNetworkTimeTracker() final {
return nullptr;
}
version_info::Channel GetChannel() final {
return version_info::Channel::UNKNOWN;
}
bool OverridesRestrictParameter(std::string*) final { return false; }
base::FilePath GetVariationsSeedFileDir() final { return user_data_dir_; }
bool IsEnterprise() final { return false; }
// Profiles aren't supported, so nothing to do here.
void RemoveGoogleGroupsFromPrefsForDeletedProfiles(PrefService*) final {}
private:
base::FilePath user_data_dir_;
};
} // namespace
// Note that several objects, created here, are scoped to the lifetime of this
// function. They are only needed to set up field trials. It replaces the
// global base::FeatureList instance, which is the only important part.
void SetupFieldTrials() {
// The environment requires a ThreadPool to post tasks. Create one if
// needed. It will be torn down at the end of this function, so that it
// doesn't interfere with tests that need their own ThreadPool.
const bool need_thread_pool = !base::ThreadPoolInstance::Get();
if (need_thread_pool) {
base::ThreadPoolInstance::Create("SetupFieldTrials");
}
base::ScopedTempDir user_data_dir;
CHECK(user_data_dir.CreateUniqueTempDir());
auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>();
metrics::MetricsService::RegisterPrefs(pref_registry.get());
variations::VariationsService::RegisterPrefs(pref_registry.get());
PrefServiceFactory factory;
factory.set_user_prefs(base::MakeRefCounted<InMemoryPrefStore>());
std::unique_ptr<PrefService> pref_service = factory.Create(pref_registry);
CHECK(pref_service);
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
std::vector<base::FeatureList::FeatureOverrideInfo> feature_overrides =
GetSwitchDependentFeatureOverrides(*command_line);
// Needed so that content_shell can use fieldtrial_testing_config.
metrics::TestEnabledStateProvider enabled_state_provider(/*consent=*/false,
/*enabled=*/false);
const bool force_benchmarking_mode =
command_line->HasSwitch(switches::kEnableGpuBenchmarking);
std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager =
metrics::MetricsStateManager::Create(
pref_service.get(), &enabled_state_provider, std::wstring(),
user_data_dir.GetPath(), metrics::StartupVisibility::kUnknown,
{
.force_benchmarking_mode = force_benchmarking_mode,
});
CHECK(metrics_state_manager);
metrics_state_manager->InstantiateFieldTrialList();
std::unique_ptr<variations::SeedResponse> initial_seed;
#if BUILDFLAG(IS_ANDROID)
if (!pref_service.get()->HasPrefPath(
variations::prefs::kVariationsSeedSignature)) {
DVLOG(1) << "Importing first run seed from Java preferences.";
initial_seed = variations::android::GetVariationsFirstRunSeed();
}
#endif
VariationServiceClient variations_service_client(user_data_dir.GetPath());
variations::VariationsFieldTrialCreator field_trial_creator(
&variations_service_client,
std::make_unique<variations::VariationsSeedStore>(
pref_service.get(), std::move(initial_seed),
/*signature_verification_enabled=*/true,
std::make_unique<variations::VariationsSafeSeedStoreLocalState>(
pref_service.get(),
variations_service_client.GetVariationsSeedFileDir(),
variations_service_client.GetChannelForVariations(),
/*entropy_providers=*/nullptr),
variations_service_client.GetChannelForVariations(),
variations_service_client.GetVariationsSeedFileDir()),
variations::UIStringOverrider());
variations::SafeSeedManager safe_seed_manager(pref_service.get());
const std::vector<std::string> variation_ids; // Empty for tests.
const std::string command_line_variation_ids =
command_line->GetSwitchValueASCII(
variations::switches::kForceVariationIds);
auto feature_list = std::make_unique<base::FeatureList>();
variations::test::ScopedVariationsIdsProvider scoped_ids_provider(
variations::VariationsIdsProvider::Mode::kUseSignedInState);
variations::PlatformFieldTrials platform_field_trials;
// Since this is a test-only code path, some arguments to SetUpFieldTrials are
// not needed, and thus are set to null or empty values.
// TODO(crbug.com/40790318): Consider passing a low entropy source.
field_trial_creator.SetUpFieldTrials(
variation_ids, command_line_variation_ids, feature_overrides,
std::move(feature_list), metrics_state_manager.get(),
&platform_field_trials, &safe_seed_manager,
/*add_entropy_source_to_variations_ids=*/false,
*metrics_state_manager->CreateEntropyProviders(
/*enable_limited_entropy_mode=*/false));
// Tear down the temporary ThreadPool, so that it doesn't interfere with
// tests that needs their own ThreadPool.
if (need_thread_pool) {
// Note that `Start(..)` is needed, because one can't shutdown a thread pool
// that hasn't been started. `Start(...)` accesses the feature flags, so
// it can't be called before `SetUpFieldTrials()`.
base::ThreadPoolInstance::Get()->Start({/*num_threads=*/1});
base::ThreadPoolInstance::Get()->Shutdown();
base::ThreadPoolInstance::Get()->JoinForTesting();
base::ThreadPoolInstance::Set(nullptr);
}
}
} // namespace content