chromium/chrome/test/base/in_process_browser_test.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/test/base/in_process_browser_test.h"

#include <map>
#include <string_view>
#include <utility>

#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/sampling_heap_profiler/poisson_allocation_sampler.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/test_file_util.h"
#include "base/test/test_switches.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/after_startup_task_utils.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_browser_main.h"
#include "chrome/browser/chrome_browser_main_extra_parts.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/lifetime/application_lifetime_desktop.h"
#include "chrome/browser/lifetime/termination_notification.h"
#include "chrome/browser/navigation_predictor/search_engine_preconnector.h"
#include "chrome/browser/net/chrome_network_delegate.h"
#include "chrome/browser/net/net_error_tab_helper.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/predictors/loading_predictor_config.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar_controller_util.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/logging_chrome.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/renderer/chrome_content_renderer_client.h"
#include "chrome/test/base/chrome_test_suite.h"
#include "chrome/test/base/test_launcher_utils.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/captive_portal/core/buildflags.h"
#include "components/custom_handlers/test_protocol_handler_registry_delegate.h"
#include "components/embedder_support/switches.h"
#include "components/feature_engagement/public/feature_list.h"
#include "components/google/core/common/google_util.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/os_crypt/async/browser/key_provider.h"
#include "components/os_crypt/sync/os_crypt_mocker.h"
#include "components/search_engines/search_engine_choice/search_engine_choice_utils.h"
#include "content/public/browser/browser_main_parts.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_launcher.h"
#include "content/public/test/test_navigation_observer.h"
#include "extensions/buildflags/buildflags.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/device/public/cpp/device_features.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "ui/base/test/ui_controls.h"
#include "ui/base/ui_base_features.h"

#if BUILDFLAG(IS_MAC)
#include "base/apple/scoped_nsautorelease_pool.h"
#include "chrome/test/base/scoped_bundle_swizzler_mac.h"
#endif

#if BUILDFLAG(IS_WIN)
#include "base/win/scoped_com_initializer.h"
#include "base/win/windows_version.h"
#include "chrome/browser/os_crypt/app_bound_encryption_win.h"
#include "components/version_info/version_info.h"
#include "ui/base/win/atl_module.h"
#endif

#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
#include "services/device/public/cpp/test/fake_geolocation_system_permission_manager.h"
#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)

#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
#include "components/captive_portal/content/captive_portal_service.h"
#endif

#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/search_engine_choice/search_engine_choice_dialog_service.h"
#include "chrome/browser/ui/webui/whats_new/whats_new_util.h"
#include "components/storage_monitor/test_storage_monitor.h"
#endif

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_switches.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "ash/shell.h"
#include "base/system/sys_info.h"
#include "chrome/browser/ash/app_restore/full_restore_app_launch_handler.h"
#include "chrome/browser/ash/input_method/input_method_configuration.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
#include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
#include "chromeos/ash/services/device_sync/device_sync_impl.h"
#include "chromeos/ash/services/device_sync/fake_device_sync.h"
#include "components/user_manager/user_names.h"
#include "ui/display/display_switches.h"
#include "ui/events/test/event_generator.h"
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

#if BUILDFLAG(IS_OZONE)
#include "ui/views/test/test_desktop_screen_ozone.h"
#endif

#if defined(TOOLKIT_VIEWS)
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/tabs/tab.h"
#include "chrome/test/views/accessibility_checker.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/widget.h"
#endif

#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "base/base_switches.h"
#include "base/environment.h"
#include "base/files/file_path_watcher.h"
#include "base/process/launch.h"
#include "base/threading/thread_restrictions.h"
#include "base/uuid.h"
#include "base/version.h"
#include "chrome/browser/lacros/browser_test_util.h"
#include "chrome/browser/lacros/cert/cert_db_initializer_factory.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "chromeos/crosapi/mojom/test_controller.mojom-test-utils.h"
#include "chromeos/lacros/lacros_service.h"
#include "chromeos/startup/browser_params_proxy.h"
#include "components/account_manager_core/chromeos/account_manager.h"
#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"  // nogncheck
#include "components/account_manager_core/chromeos/fake_account_manager_ui.h"  // nogncheck
#include "components/variations/variations_switches.h"
#include "content/public/test/network_connection_change_simulator.h"
#endif

namespace {

#if BUILDFLAG(IS_CHROMEOS_ASH)
class FakeDeviceSyncImplFactory
    : public ash::device_sync::DeviceSyncImpl::Factory {
 public:
  FakeDeviceSyncImplFactory() = default;
  ~FakeDeviceSyncImplFactory() override = default;

  // ash::device_sync::DeviceSyncImpl::Factory:
  std::unique_ptr<ash::device_sync::DeviceSyncBase> CreateInstance(
      signin::IdentityManager* identity_manager,
      instance_id::InstanceIDDriver* instance_id_driver,
      PrefService* profile_prefs,
      const ash::device_sync::GcmDeviceInfoProvider* gcm_device_info_provider,
      ash::device_sync::ClientAppMetadataProvider* client_app_metadata_provider,
      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
      std::unique_ptr<base::OneShotTimer> timer,
      ash::device_sync::AttestationCertificatesSyncer::
          GetAttestationCertificatesFunction
              get_attestation_certificates_function) override {
    return std::make_unique<ash::device_sync::FakeDeviceSync>();
  }
};

FakeDeviceSyncImplFactory* GetFakeDeviceSyncImplFactory() {
  static base::NoDestructor<FakeDeviceSyncImplFactory> factory;
  return factory.get();
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
class ChromeBrowserMainExtraPartsBrowserProcessInjection
    : public ChromeBrowserMainExtraParts {
 public:
  ChromeBrowserMainExtraPartsBrowserProcessInjection() = default;

  // ChromeBrowserMainExtraParts implementation
  void PreCreateMainMessageLoop() override {
    if (features::IsOsLevelGeolocationPermissionSupportEnabled()) {
      // Tests should not depend on the current state of the system-level
      // location permission on platforms where the permission cannot be
      // programmatically changed by tests. Insert a fake
      // GeolocationSystemPermissionManager and simulate a granted system-level
      // location permission.
      //
      // On ChromeOS, preserve the real manager so that tests can enable or
      // disable the system preference.
      auto fake_geolocation_system_permission_manager =
          std::make_unique<device::FakeGeolocationSystemPermissionManager>();
      fake_geolocation_system_permission_manager->SetSystemPermission(
          device::LocationSystemPermissionStatus::kAllowed);
      device::GeolocationSystemPermissionManager::SetInstance(
          std::move(fake_geolocation_system_permission_manager));
    }
  }

  ChromeBrowserMainExtraPartsBrowserProcessInjection(
      const ChromeBrowserMainExtraPartsBrowserProcessInjection&) = delete;
  ChromeBrowserMainExtraPartsBrowserProcessInjection& operator=(
      const ChromeBrowserMainExtraPartsBrowserProcessInjection&) = delete;
};
#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_CHROMEOS_LACROS)
// For browser tests that depend on AccountManager on Lacros - e.g. tests that
// manage accounts by calling methods like `signin::MakePrimaryAccountAvailable`
// from identity_test_utils.
// TODO(crbug.com/40635309): consider using this class on Ash, and remove
// the initialization from profile_impl.
class IdentityExtraSetUp : public ChromeBrowserMainExtraParts {
 public:
  void PreProfileInit() override {
    // Create and initialize Ash AccountManager.
    scoped_ash_account_manager_ =
        std::make_unique<ScopedAshAccountManagerForTests>(
            std::make_unique<FakeAccountManagerUI>());
    auto* account_manager = MaybeGetAshAccountManagerForTests();
    CHECK(account_manager);
    account_manager->InitializeInEphemeralMode(
        g_browser_process->system_network_context_manager()
            ->GetSharedURLLoaderFactory());

    // Make sure the primary accounts for all profiles are present in the
    // account manager, to prevent profiles from being deleted. This is useful
    // in particular for tests that create profiles in a PRE_ step and expect
    // the profiles to still exist when Chrome is restarted.
    ProfileAttributesStorage* storage =
        &g_browser_process->profile_manager()->GetProfileAttributesStorage();
    for (const ProfileAttributesEntry* entry :
         storage->GetAllProfilesAttributes()) {
      const std::string& gaia_id = entry->GetGAIAId();
      if (!gaia_id.empty()) {
        account_manager->UpsertAccount(
            {gaia_id, account_manager::AccountType::kGaia},
            base::UTF16ToUTF8(entry->GetUserName()),
            "identity_extra_setup_test_token");
      }
    }
  }

 private:
  std::unique_ptr<ScopedAshAccountManagerForTests> scoped_ash_account_manager_;
};

// Returns true if crosapi::mojom::TestController is available.
// Note: crosapi::mojom::TestController can be unavailable in the following
// case:
// 1. BrowserParamsProxy::IsCrosapiDisabledForTesting() returns true.
// 2. BrowserParamsProxy::InterfaceVersions() has no value. This happens in
// some tests that call chromeos::BrowserInitParams::SetInitParamsForTests.
bool IsTestControllerAvailable() {
  auto* lacros_service = chromeos::LacrosService::Get();
  return lacros_service &&
         lacros_service->IsAvailable<crosapi::mojom::TestController>();
}

#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

// This extra parts adds a test key provider to make sure that async
// initialization of OSCrypt Async always happens during browser_tests, but
// otherwise does nothing.
class OSCryptAsyncExtraSetUp : public ChromeBrowserMainExtraParts {};

void EnsureBrowserContextKeyedServiceFactoriesForTestingBuilt() {}

// TODO(neis): The name WaitForWindowCreation is a bit confusing. Technically,
// we are waiting for the window to become visible (or minimized) in Ash.
// Try to find a better name.
bool WaitForWindowCreation(Browser* browser) {}

InProcessBrowserTest* g_current_test;

}  // namespace

// static
InProcessBrowserTest::SetUpBrowserFunction*
    InProcessBrowserTest::global_browser_set_up_function_ =;

InProcessBrowserTest::InProcessBrowserTest() {}

#if defined(TOOLKIT_VIEWS)
InProcessBrowserTest::InProcessBrowserTest(
    std::unique_ptr<views::ViewsDelegate> views_delegate) {}
#endif

#if BUILDFLAG(IS_CHROMEOS_ASH)
void InProcessBrowserTest::set_launch_browser_for_testing(
    std::unique_ptr<ash::full_restore::ScopedLaunchBrowserForTesting>
        launch_browser_for_testing) {
  launch_browser_for_testing_ = std::move(launch_browser_for_testing);
}
#endif

void InProcessBrowserTest::RunScheduledLayouts() {}

#if BUILDFLAG(IS_CHROMEOS_LACROS)
FakeAccountManagerUI* InProcessBrowserTest::GetFakeAccountManagerUI() const {
  return static_cast<FakeAccountManagerUI*>(
      MaybeGetAshAccountManagerUIForTests());
}

base::Version InProcessBrowserTest::GetAshChromeVersion() {
  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  base::FilePath ash_chrome_path =
      command_line->GetSwitchValuePath("ash-chrome-path");
  CHECK(!ash_chrome_path.empty());
  base::CommandLine invoker(ash_chrome_path);
  invoker.AppendSwitch(switches::kVersion);
  std::string output;
  base::ScopedAllowBlockingForTesting blocking;
  CHECK(base::GetAppOutput(invoker, &output));
  std::vector<std::string> tokens = base::SplitString(
      output, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
  CHECK_GT(tokens.size(), 1U);
  // We assume Chrome version is always at the second last position.
  base::Version version(tokens[tokens.size() - 2]);
  CHECK(version.IsValid()) << "Can not find "
                           << "chrome version in string: " << output;
  return version;
}

void InProcessBrowserTest::VerifyNoAshBrowserWindowOpenRightNow() {
  CHECK(IsTestControllerAvailable());
  crosapi::mojom::TestControllerAsyncWaiter waiter(
      chromeos::LacrosService::Get()
          ->GetRemote<crosapi::mojom::TestController>()
          .get());

  uint32_t number = 1;
  waiter.GetOpenAshBrowserWindows(&number);
  EXPECT_EQ(0u, number)
      << "There should not be any ash browser window open at this point.";
}

void InProcessBrowserTest::CloseAllAshBrowserWindows() {
  CHECK(IsTestControllerAvailable());
  crosapi::mojom::TestControllerAsyncWaiter waiter(
      chromeos::LacrosService::Get()
          ->GetRemote<crosapi::mojom::TestController>()
          .get());
  bool success;
  waiter.CloseAllAshBrowserWindowsAndConfirm(&success);
  EXPECT_TRUE(success) << "Failed to close all ash browser windows";
}

void InProcessBrowserTest::WaitUntilAtLeastOneAshBrowserWindowOpen() {
  CHECK(IsTestControllerAvailable());
  crosapi::mojom::TestControllerAsyncWaiter waiter(
      chromeos::LacrosService::Get()
          ->GetRemote<crosapi::mojom::TestController>()
          .get());
  bool has_open_window;
  waiter.CheckAtLeastOneAshBrowserWindowOpen(&has_open_window);
  EXPECT_TRUE(has_open_window);
}
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

void InProcessBrowserTest::Initialize() {}

InProcessBrowserTest::~InProcessBrowserTest() {}

InProcessBrowserTest* InProcessBrowserTest::GetCurrent() {}

void InProcessBrowserTest::SetUp() {}

void InProcessBrowserTest::SetUpDefaultCommandLine(
    base::CommandLine* command_line) {}

void InProcessBrowserTest::TearDown() {}

// static
size_t InProcessBrowserTest::GetTestPreCount() {}

void InProcessBrowserTest::CreatedBrowserMainParts(
    content::BrowserMainParts* parts) {}

void InProcessBrowserTest::SelectFirstBrowser() {}

void InProcessBrowserTest::RecordPropertyFromMap(
    const std::map<std::string, std::string>& tags) {}

void InProcessBrowserTest::SetUpLocalStatePrefService(
    PrefService* local_state) {}

void InProcessBrowserTest::CloseBrowserSynchronously(Browser* browser) {}

void InProcessBrowserTest::CloseBrowserAsynchronously(Browser* browser) {}

void InProcessBrowserTest::CloseAllBrowsers() {}

void InProcessBrowserTest::RunUntilBrowserProcessQuits() {}

// TODO(alexmos): This function should expose success of the underlying
// navigation to tests, which should make sure navigations succeed when
// appropriate. See https://crbug.com/425335
bool InProcessBrowserTest::AddTabAtIndexToBrowser(
    Browser* browser,
    int index,
    const GURL& url,
    ui::PageTransition transition,
    bool check_navigation_success) {}

bool InProcessBrowserTest::AddTabAtIndexToBrowser(
    Browser* browser,
    int index,
    const GURL& url,
    ui::PageTransition transition) {}

bool InProcessBrowserTest::AddTabAtIndex(int index,
                                         const GURL& url,
                                         ui::PageTransition transition) {}

bool InProcessBrowserTest::SetUpUserDataDirectory() {}

void InProcessBrowserTest::SetScreenInstance() {}

#if !BUILDFLAG(IS_MAC)
void InProcessBrowserTest::OpenDevToolsWindow(
    content::WebContents* web_contents) {}

Browser* InProcessBrowserTest::OpenURLOffTheRecord(Profile* profile,
                                                   const GURL& url) {}

// Creates a browser with a single tab (about:blank), waits for the tab to
// finish loading and shows the browser.
Browser* InProcessBrowserTest::CreateBrowser(Profile* profile) {}

Browser* InProcessBrowserTest::CreateIncognitoBrowser(Profile* profile) {}

Browser* InProcessBrowserTest::CreateBrowserForPopup(Profile* profile) {}

Browser* InProcessBrowserTest::CreateBrowserForApp(const std::string& app_name,
                                                   Profile* profile) {}
#endif  // !BUILDFLAG(IS_MAC)

#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
Browser* InProcessBrowserTest::CreateGuestBrowser() {}
#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)

void InProcessBrowserTest::AddBlankTabAndShow(Browser* browser) {}

#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_CHROMEOS_LACROS)
base::CommandLine InProcessBrowserTest::GetCommandLineForRelaunch() {}
#endif  // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_CHROMEOS_LACROS)

base::FilePath InProcessBrowserTest::GetChromeTestDataDir() const {}

void InProcessBrowserTest::PreRunTestOnMainThread() {}

void InProcessBrowserTest::PostRunTestOnMainThread() {}

void InProcessBrowserTest::QuitBrowsers() {}

void InProcessBrowserTest::SetupProtocolHandlerTestFactories(
    content::BrowserContext* context) {}

#if BUILDFLAG(IS_CHROMEOS_LACROS)
void InProcessBrowserTest::StartUniqueAshChrome(
    const std::vector<std::string>& enabled_features,
    const std::vector<std::string>& disabled_features,
    const std::vector<std::string>& additional_cmdline_switches,
    const std::string& bug_number_and_reason) {
  DCHECK(!bug_number_and_reason.empty());
  CHECK(!chromeos::BrowserParamsProxy::IsCrosapiDisabledForTesting())
      << "You can only start unique ash chrome when crosapi is enabled. "
      << "It should not be necessary otherwise.";
  base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
  base::FilePath ash_dir_holder = cmdline->GetSwitchValuePath("unique-ash-dir");
  CHECK(!ash_dir_holder.empty());
  CHECK(unique_ash_user_data_dir_.CreateUniqueTempDirUnderPath(ash_dir_holder));
  base::FilePath socket_file =
      unique_ash_user_data_dir_.GetPath().Append("lacros.sock");

  // Reset the current test runner connecting to the unique ash chrome.
  cmdline->RemoveSwitch("lacros-mojo-socket-for-testing");
  cmdline->AppendSwitchPath("lacros-mojo-socket-for-testing", socket_file);
  // Need unique socket name for wayland globally. So for each ash and lacros
  // pair, they have a unique socket to communicate.
  base::Environment::Create()->SetVar(
      "WAYLAND_DISPLAY",
      base::JoinString({"unique_wayland",
                        base::Uuid::GenerateRandomV4().AsLowercaseString()},
                       "_"));

  base::FilePath ash_chrome_path =
      cmdline->GetSwitchValuePath("ash-chrome-path");
  CHECK(!ash_chrome_path.empty());
  base::CommandLine ash_cmdline(ash_chrome_path);
  ash_cmdline.AppendSwitchPath(switches::kUserDataDir,
                               unique_ash_user_data_dir_.GetPath());
  ash_cmdline.AppendSwitch("enable-wayland-server");
  ash_cmdline.AppendSwitch(switches::kNoStartupWindow);
  ash_cmdline.AppendSwitch("disable-lacros-keep-alive");
  ash_cmdline.AppendSwitch("disable-login-lacros-opening");
  ash_cmdline.AppendSwitch(
      variations::switches::kEnableFieldTrialTestingConfig);
  for (const std::string& cmdline_switch : additional_cmdline_switches) {
    size_t pos = cmdline_switch.find("=");
    if (pos == std::string::npos) {
      ash_cmdline.AppendSwitch(cmdline_switch);
    } else {
      CHECK_GT(pos, 0u);
      ash_cmdline.AppendSwitchASCII(cmdline_switch.substr(0, pos),
                                    cmdline_switch.substr(pos + 1));
    }
  }

  std::vector<std::string> all_enabled_features = {
      "LacrosSupport", "LacrosPrimary", "LacrosOnly"};
  all_enabled_features.insert(all_enabled_features.end(),
                              enabled_features.begin(),
                              enabled_features.end());
  // During the Lacros sunset process, LacrosOnly feature flag is retired before
  // Lacros itself is retired b/354842935.
  ash_cmdline.AppendSwitch("enable-lacros-for-testing");
  ash_cmdline.AppendSwitchASCII(switches::kEnableFeatures,
                                base::JoinString(all_enabled_features, ","));
  ash_cmdline.AppendSwitchASCII(switches::kDisableFeatures,
                                base::JoinString(disabled_features, ","));

  ash_cmdline.AppendSwitchPath("lacros-mojo-socket-for-testing", socket_file);
  std::string wayland_socket;
  CHECK(
      base::Environment::Create()->GetVar("WAYLAND_DISPLAY", &wayland_socket));
  DCHECK(!wayland_socket.empty());
  ash_cmdline.AppendSwitchASCII("wayland-server-socket", wayland_socket);
  const base::FilePath ash_ready_file =
      unique_ash_user_data_dir_.GetPath().AppendASCII("ash_ready.txt");
  ash_cmdline.AppendSwitchPath("ash-ready-file-path", ash_ready_file);

  // Need this for RunLoop. See
  // //docs/threading_and_tasks_testing.md#basetestsinglethreadtaskenvironment
  base::test::SingleThreadTaskEnvironment task_environment;
  base::FilePathWatcher watcher;
  base::RunLoop run_loop;
  CHECK(watcher.Watch(base::FilePath(ash_ready_file),
                      base::FilePathWatcher::Type::kNonRecursive,
                      base::BindLambdaForTesting(
                          [&](const base::FilePath& filepath, bool error) {
                            CHECK(!error);
                            run_loop.Quit();
                          })));
  base::LaunchOptions option;
  ash_process_ = base::LaunchProcess(ash_cmdline, option);
  CHECK(ash_process_.IsValid());
  run_loop.Run();
  // When ash is ready and crosapi was enabled, we expect mojo socket is
  // also ready.
  CHECK(base::PathExists(socket_file));
  LOG(INFO) << "Successfully started a unique ash chrome.";
}

#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)