| // 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 <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/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/single_thread_task_runner.h" |
| #include "base/test/test_file_util.h" |
| #include "base/test/test_switches.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/chrome_notification_types.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/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/profiler/main_thread_stack_sampling_profiler.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/embedder_support/switches.h" |
| #include "components/feature_engagement/public/feature_list.h" |
| #include "components/google/core/common/google_util.h" |
| #include "components/os_crypt/sync/os_crypt_mocker.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/network/public/cpp/shared_url_loader_factory.h" |
| #include "ui/base/ui_base_features.h" |
| |
| #if BUILDFLAG(IS_MAC) |
| #include "base/mac/scoped_nsautorelease_pool.h" |
| #include "chrome/test/base/scoped_bundle_swizzler_mac.h" |
| #include "services/device/public/cpp/test/fake_geolocation_manager.h" |
| #endif |
| |
| #if BUILDFLAG(IS_WIN) |
| #include "base/win/scoped_com_initializer.h" |
| #include "base/win/windows_version.h" |
| #include "ui/base/win/atl_module.h" |
| #endif |
| |
| #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| #include "components/captive_portal/content/captive_portal_service.h" |
| #endif |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| #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/guid.h" |
| #include "base/process/launch.h" |
| #include "chrome/browser/lacros/cert/cert_db_initializer_factory.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" |
| #include "ui/aura/test/ui_controls_factory_aura.h" |
| #include "ui/base/test/ui_controls.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, |
| gcm::GCMDriver* gcm_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) |
| class ChromeBrowserMainExtraPartsBrowserProcessInjection |
| : public ChromeBrowserMainExtraParts { |
| public: |
| ChromeBrowserMainExtraPartsBrowserProcessInjection() = default; |
| |
| // ChromeBrowserMainExtraParts implementation |
| void PreCreateMainMessageLoop() override { |
| // The real GeolocationManager initializes a CLLocationManager. It has |
| // been observed that when thousands of instances of this object are |
| // created, as happens when running browser tests, the CoreLocationAgent |
| // process uses lots of CPU. This makes test execution slower and causes |
| // jobs to time out. We therefore insert a fake. |
| auto fake_geolocation_manager = |
| std::make_unique<device::FakeGeolocationManager>(); |
| fake_geolocation_manager->SetSystemPermission( |
| device::LocationSystemPermissionStatus::kAllowed); |
| g_browser_process->SetGeolocationManager( |
| std::move(fake_geolocation_manager)); |
| } |
| |
| ChromeBrowserMainExtraPartsBrowserProcessInjection( |
| const ChromeBrowserMainExtraPartsBrowserProcessInjection&) = delete; |
| ChromeBrowserMainExtraPartsBrowserProcessInjection& operator=( |
| const ChromeBrowserMainExtraPartsBrowserProcessInjection&) = delete; |
| }; |
| #endif // BUILDFLAG(IS_MAC) |
| |
| #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(https://crbug.com/982233): 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_; |
| }; |
| #endif // BUILDFLAG(IS_CHROMEOS_LACROS) |
| |
| void EnsureBrowserContextKeyedServiceFactoriesForTestingBuilt() { |
| NotificationDisplayServiceTester::EnsureFactoryBuilt(); |
| } |
| |
| } // namespace |
| |
| // static |
| InProcessBrowserTest::SetUpBrowserFunction* |
| InProcessBrowserTest::global_browser_set_up_function_ = nullptr; |
| |
| InProcessBrowserTest::InProcessBrowserTest() { |
| Initialize(); |
| #if defined(TOOLKIT_VIEWS) |
| views_delegate_ = std::make_unique<AccessibilityChecker>(); |
| #endif |
| } |
| |
| #if defined(TOOLKIT_VIEWS) |
| InProcessBrowserTest::InProcessBrowserTest( |
| std::unique_ptr<views::ViewsDelegate> views_delegate) { |
| Initialize(); |
| views_delegate_ = std::move(views_delegate); |
| } |
| #endif |
| |
| void InProcessBrowserTest::RunScheduledLayouts() { |
| #if defined(TOOLKIT_VIEWS) |
| views::Widget::Widgets widgets_to_layout; |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| // WidgetTest::GetAllWidgets() doesn't work for ChromeOS in a production |
| // environment. We must get the Widgets ourself. |
| for (aura::Window* root_window : ash::Shell::GetAllRootWindows()) |
| views::Widget::GetAllChildWidgets(root_window, &widgets_to_layout); |
| #else |
| widgets_to_layout = views::test::WidgetTest::GetAllWidgets(); |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| for (views::Widget* widget : widgets_to_layout) |
| widget->LayoutRootViewIfNecessary(); |
| #endif // defined(TOOLKIT_VIEWS) |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| FakeAccountManagerUI* InProcessBrowserTest::GetFakeAccountManagerUI() const { |
| return static_cast<FakeAccountManagerUI*>( |
| MaybeGetAshAccountManagerUIForTests()); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS_LACROS) |
| |
| void InProcessBrowserTest::Initialize() { |
| CreateTestServer(GetChromeTestDataDir()); |
| base::FilePath src_dir; |
| CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)); |
| |
| // chrome::DIR_TEST_DATA isn't going to be setup until after we call |
| // ContentMain. However that is after tests' constructors or SetUp methods, |
| // which sometimes need it. So just override it. |
| CHECK(base::PathService::Override(chrome::DIR_TEST_DATA, |
| src_dir.Append(GetChromeTestDataDir()))); |
| |
| #if BUILDFLAG(IS_MAC) |
| bundle_swizzler_ = std::make_unique<ScopedBundleSwizzlerMac>(); |
| #endif |
| |
| std::vector<base::test::FeatureRef> disabled_features; |
| |
| // Preconnecting can cause non-deterministic test behavior especially with |
| // various test fixtures that mock servers. |
| disabled_features.push_back(features::kPreconnectToSearch); |
| |
| // If the network service fails to start sandboxed then this should cause |
| // tests to fail. |
| disabled_features.push_back( |
| features::kRestartNetworkServiceUnsandboxedForFailedLaunch); |
| |
| // In-product help can conflict with tests' expected window activation and |
| // focus. Individual tests can re-enable IPH. |
| block_all_iph_feature_list_.InitWithNoFeaturesAllowed(); |
| |
| scoped_feature_list_.InitWithFeatures({}, disabled_features); |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| launch_browser_for_testing_ = |
| std::make_unique<ash::full_restore::ScopedLaunchBrowserForTesting>(); |
| #endif |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| CertDbInitializerFactory::GetInstance() |
| ->SetCreateWithBrowserContextForTesting(/*should_create=*/false); |
| #endif // BUILDFLAG(IS_CHROMEOS_LACROS) |
| } |
| |
| InProcessBrowserTest::~InProcessBrowserTest() = default; |
| |
| void InProcessBrowserTest::SetUp() { |
| // Browser tests will create their own g_browser_process later. |
| DCHECK(!g_browser_process); |
| |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| |
| // Auto-reload breaks many browser tests, which assume error pages won't be |
| // reloaded out from under them. Tests that expect or desire this behavior can |
| // append embedder_support::kEnableAutoReload, which will override the disable |
| // here. |
| command_line->AppendSwitch(embedder_support::kDisableAutoReload); |
| |
| // Allow subclasses to change the command line before running any tests. |
| SetUpCommandLine(command_line); |
| // Add command line arguments that are used by all InProcessBrowserTests. |
| SetUpDefaultCommandLine(command_line); |
| |
| // PoissonAllocationSampler's TLS slots need to be set up before |
| // MainThreadStackSamplingProfiler, which can allocate TLS slots of its own. |
| // On some platforms pthreads can malloc internally to access higher-numbered |
| // TLS slots, which can cause reentry in the heap profiler. (See the comment |
| // on ReentryGuard::InitTLSSlot().) |
| // TODO(https://crbug.com/1411454): Clean up other paths that call this Init() |
| // function, which are now redundant. |
| base::PoissonAllocationSampler::Init(); |
| |
| // Initialize sampling profiler in browser tests. This mimics the behavior |
| // in standalone Chrome, where this is done in chrome/app/chrome_main.cc, |
| // which does not get called by browser tests. |
| sampling_profiler_ = std::make_unique<MainThreadStackSamplingProfiler>(); |
| |
| // Create a temporary user data directory if required. |
| ASSERT_TRUE(test_launcher_utils::CreateUserDataDir(&temp_user_data_dir_)) |
| << "Could not create user data directory."; |
| |
| // Allow subclasses the opportunity to make changes to the default user data |
| // dir before running any tests. |
| ASSERT_TRUE(SetUpUserDataDirectory()) |
| << "Could not set up user data directory."; |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| // No need to redirect log for test. |
| command_line->AppendSwitch(switches::kDisableLoggingRedirect); |
| |
| // Disable IME extension loading to avoid many browser tests failures. |
| ash::input_method::DisableExtensionLoading(); |
| |
| if (!command_line->HasSwitch(switches::kHostWindowBounds) && |
| !base::SysInfo::IsRunningOnChromeOS()) { |
| // Adjusting window location & size so that the ash desktop window fits |
| // inside the Xvfb's default resolution. Only do that when not running |
| // on device. Otherwise, device display is not properly configured. |
| command_line->AppendSwitchASCII(switches::kHostWindowBounds, |
| "0+0-1280x800"); |
| } |
| |
| // Default to run in a signed in session of stub user if tests do not run |
| // in the login screen (--login-manager), or logged in user session |
| // (--login-user), or the guest session (--bwsi). This is essentially |
| // the same as in `ChromeBrowserMainPartsAsh::PreEarlyInitialization` |
| // but it will be done on device and only for tests. |
| if (!command_line->HasSwitch(ash::switches::kLoginManager) && |
| !command_line->HasSwitch(ash::switches::kLoginUser) && |
| !command_line->HasSwitch(ash::switches::kGuestSession)) { |
| command_line->AppendSwitchASCII( |
| ash::switches::kLoginUser, |
| cryptohome::Identification(user_manager::StubAccountId()).id()); |
| if (!command_line->HasSwitch(ash::switches::kLoginProfile)) { |
| command_line->AppendSwitchASCII( |
| ash::switches::kLoginProfile, |
| ash::BrowserContextHelper::kTestUserBrowserContextDirName); |
| } |
| } |
| #endif |
| |
| SetScreenInstance(); |
| |
| // Use a mocked password storage if OS encryption is used that might block or |
| // prompt the user (which is when anything sensitive gets stored, including |
| // Cookies). Without this on Mac and Linux, many tests will hang waiting for a |
| // user to approve KeyChain/kwallet access. On Windows this is not needed as |
| // OS APIs never block. |
| #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| OSCryptMocker::SetUp(); |
| #endif |
| |
| #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| captive_portal::CaptivePortalService::set_state_for_testing( |
| captive_portal::CaptivePortalService::DISABLED_FOR_TESTING); |
| #endif |
| |
| chrome_browser_net::NetErrorTabHelper::set_state_for_testing( |
| chrome_browser_net::NetErrorTabHelper::TESTING_FORCE_DISABLED); |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| // On Chrome OS, access to files via file: scheme is restricted. Enable |
| // access to all files here since browser_tests and interactive_ui_tests |
| // rely on the ability to open any files via file: scheme. |
| ChromeNetworkDelegate::EnableAccessToAllFilesForTesting(true); |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| // Device sync (for multidevice "Better Together") is ash specific. |
| ash::device_sync::DeviceSyncImpl::Factory::SetCustomFactory( |
| GetFakeDeviceSyncImplFactory()); |
| |
| // Using a screenshot for clamshell to tablet mode transitions makes the flow |
| // async which we want to disable for most tests. |
| ash::ShellTestApi::SetTabletControllerUseScreenshotForTest(false); |
| |
| // Disable the notification delay timer used to prevent non system |
| // notifications from showing up right after login. |
| ash::ShellTestApi::SetUseLoginNotificationDelayForTest(false); |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| // Redirect the default download directory to a temporary directory. |
| ASSERT_TRUE(default_download_dir_.CreateUniqueTempDir()); |
| CHECK(base::PathService::Override(chrome::DIR_DEFAULT_DOWNLOADS, |
| default_download_dir_.GetPath())); |
| |
| #if defined(TOOLKIT_VIEWS) |
| // Prevent hover cards from appearing when the mouse is over the tab. Tests |
| // don't typically account for this possibly, so it can cause unrelated tests |
| // to fail. See crbug.com/1050012. |
| Tab::SetShowHoverCardOnMouseHoverForTesting(false); |
| #endif // defined(TOOLKIT_VIEWS) |
| |
| // Auto-redirect to the NTP, which can happen if remote content is enabled on |
| // What's New for tests that simulate first run, is unexpected by most tests. |
| whats_new::DisableRemoteContentForTests(); |
| |
| // The Privacy Sandbox service may attempt to show a modal prompt to the |
| // profile on browser start, which is unexpected by mosts tests. Tests which |
| // expect this can allow the prompt as desired. |
| PrivacySandboxService::SetPromptDisabledForTests(true); |
| |
| EnsureBrowserContextKeyedServiceFactoriesForTestingBuilt(); |
| |
| BrowserTestBase::SetUp(); |
| } |
| |
| void InProcessBrowserTest::SetUpDefaultCommandLine( |
| base::CommandLine* command_line) { |
| test_launcher_utils::PrepareBrowserCommandLineForTests(command_line); |
| test_launcher_utils::PrepareBrowserCommandLineForBrowserTests( |
| command_line, open_about_blank_on_browser_launch_); |
| |
| // TODO(pkotwicz): Investigate if we can remove this switch. |
| if (exit_when_last_browser_closes_) |
| command_line->AppendSwitch(switches::kDisableZeroBrowsersOpenForTests); |
| #if BUILDFLAG(IS_CHROMEOS) |
| // Do not automaximize in browser tests. |
| command_line->AppendSwitch(switches::kDisableAutoMaximizeForTests); |
| #endif |
| } |
| |
| void InProcessBrowserTest::TearDown() { |
| DCHECK(!g_browser_process); |
| #if BUILDFLAG(IS_WIN) |
| com_initializer_.reset(); |
| #endif |
| BrowserTestBase::TearDown(); |
| #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| OSCryptMocker::TearDown(); |
| #endif |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| ash::device_sync::DeviceSyncImpl::Factory::SetCustomFactory(nullptr); |
| launch_browser_for_testing_ = nullptr; |
| #endif |
| |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| if (ash_process_.IsValid()) { |
| // Need to wait for the termination so the temporary user data dir |
| // can be cleaned up. |
| ash_process_.Terminate(0, /*wait=*/true); |
| } |
| #endif |
| } |
| |
| // static |
| size_t InProcessBrowserTest::GetTestPreCount() { |
| constexpr base::StringPiece kPreTestPrefix = "PRE_"; |
| base::StringPiece test_name = |
| testing::UnitTest::GetInstance()->current_test_info()->name(); |
| size_t count = 0; |
| while (base::StartsWith(test_name, kPreTestPrefix)) { |
| ++count; |
| test_name = test_name.substr(kPreTestPrefix.size()); |
| } |
| return count; |
| } |
| |
| void InProcessBrowserTest::CreatedBrowserMainParts( |
| content::BrowserMainParts* parts) { |
| BrowserTestBase::CreatedBrowserMainParts(parts); |
| #if BUILDFLAG(IS_MAC) |
| static_cast<ChromeBrowserMainParts*>(parts)->AddParts( |
| std::make_unique<ChromeBrowserMainExtraPartsBrowserProcessInjection>()); |
| #endif |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| static_cast<ChromeBrowserMainParts*>(parts)->AddParts( |
| std::make_unique<IdentityExtraSetUp>()); |
| #endif |
| } |
| |
| void InProcessBrowserTest::SelectFirstBrowser() { |
| const BrowserList* browser_list = BrowserList::GetInstance(); |
| if (!browser_list->empty()) |
| browser_ = browser_list->get(0); |
| } |
| |
| void InProcessBrowserTest::RecordPropertyFromMap( |
| const std::map<std::string, std::string>& tags) { |
| std::string result = ""; |
| for (auto const& tag_pair : tags) { |
| // Make sure the key value pair does not contain ; and = characters. |
| DCHECK(tag_pair.first.find(";") == std::string::npos && |
| tag_pair.first.find("=") == std::string::npos); |
| DCHECK(tag_pair.second.find(";") == std::string::npos && |
| tag_pair.second.find("=") == std::string::npos); |
| if (!result.empty()) |
| result = base::StrCat({result, ";"}); |
| result = base::StrCat({result, tag_pair.first, "=", tag_pair.second}); |
| } |
| if (!result.empty()) |
| RecordProperty("gtest_tag", result); |
| } |
| |
| void InProcessBrowserTest::CloseBrowserSynchronously(Browser* browser) { |
| CloseBrowserAsynchronously(browser); |
| ui_test_utils::WaitForBrowserToClose(browser); |
| } |
| |
| void InProcessBrowserTest::CloseBrowserAsynchronously(Browser* browser) { |
| browser->window()->Close(); |
| #if BUILDFLAG(IS_MAC) |
| // BrowserWindowController depends on the auto release pool being recycled |
| // in the message loop to delete itself. |
| AutoreleasePool()->Recycle(); |
| #endif |
| } |
| |
| void InProcessBrowserTest::CloseAllBrowsers() { |
| chrome::CloseAllBrowsers(); |
| #if BUILDFLAG(IS_MAC) |
| // BrowserWindowController depends on the auto release pool being recycled |
| // in the message loop to delete itself. |
| AutoreleasePool()->Recycle(); |
| #endif |
| } |
| |
| void InProcessBrowserTest::RunUntilBrowserProcessQuits() { |
| std::exchange(run_loop_, nullptr)->Run(); |
| } |
| |
| // 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) { |
| return AddTabAtIndexToBrowser(browser, index, url, transition); |
| } |
| |
| bool InProcessBrowserTest::AddTabAtIndexToBrowser( |
| Browser* browser, |
| int index, |
| const GURL& url, |
| ui::PageTransition transition) { |
| NavigateParams params(browser, url, transition); |
| params.tabstrip_index = index; |
| params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; |
| Navigate(¶ms); |
| |
| return content::WaitForLoadStop(params.navigated_or_inserted_contents); |
| } |
| |
| bool InProcessBrowserTest::AddTabAtIndex(int index, |
| const GURL& url, |
| ui::PageTransition transition) { |
| return AddTabAtIndexToBrowser(browser(), index, url, transition, true); |
| } |
| |
| bool InProcessBrowserTest::SetUpUserDataDirectory() { |
| return true; |
| } |
| |
| void InProcessBrowserTest::SetScreenInstance() { |
| // TODO(crbug.com/1317416): On wayland platform, we need to check if the |
| // wayland-ozone platform is initialized at this point due to the async |
| // initialization of the display. Investigate if we can eliminate |
| // IsOzoneInitialized. |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) |
| if (!display::Screen::HasScreen() && |
| views::test::TestDesktopScreenOzone::IsOzoneInitialized()) { |
| // This is necessary for interactive UI tests. |
| // It is enabled in interactive_ui_tests_main.cc |
| // (or through GPUMain) |
| screen_ = views::test::TestDesktopScreenOzone::Create(); |
| } |
| #endif |
| } |
| |
| #if !BUILDFLAG(IS_MAC) |
| void InProcessBrowserTest::OpenDevToolsWindow( |
| content::WebContents* web_contents) { |
| ASSERT_FALSE(content::DevToolsAgentHost::HasFor(web_contents)); |
| DevToolsWindow::OpenDevToolsWindow(web_contents); |
| ASSERT_TRUE(content::DevToolsAgentHost::HasFor(web_contents)); |
| } |
| |
| Browser* InProcessBrowserTest::OpenURLOffTheRecord(Profile* profile, |
| const GURL& url) { |
| chrome::OpenURLOffTheRecord(profile, url); |
| Browser* browser = chrome::FindTabbedBrowser( |
| profile->GetPrimaryOTRProfile(/*create_if_needed=*/true), false); |
| content::TestNavigationObserver observer( |
| browser->tab_strip_model()->GetActiveWebContents()); |
| observer.Wait(); |
| return browser; |
| } |
| |
| // 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* browser = Browser::Create(Browser::CreateParams(profile, true)); |
| AddBlankTabAndShow(browser); |
| return browser; |
| } |
| |
| Browser* InProcessBrowserTest::CreateIncognitoBrowser(Profile* profile) { |
| // Use active profile if default nullptr was passed. |
| if (!profile) |
| profile = browser()->profile(); |
| // Create a new browser with using the incognito profile. |
| Browser* incognito = Browser::Create(Browser::CreateParams( |
| profile->GetPrimaryOTRProfile(/*create_if_needed=*/true), true)); |
| AddBlankTabAndShow(incognito); |
| return incognito; |
| } |
| |
| Browser* InProcessBrowserTest::CreateBrowserForPopup(Profile* profile) { |
| Browser* browser = Browser::Create( |
| Browser::CreateParams(Browser::TYPE_POPUP, profile, true)); |
| AddBlankTabAndShow(browser); |
| return browser; |
| } |
| |
| Browser* InProcessBrowserTest::CreateBrowserForApp(const std::string& app_name, |
| Profile* profile) { |
| Browser* browser = Browser::Create(Browser::CreateParams::CreateForApp( |
| app_name, false /* trusted_source */, gfx::Rect(), profile, true)); |
| AddBlankTabAndShow(browser); |
| return browser; |
| } |
| #endif // !BUILDFLAG(IS_MAC) |
| |
| #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH) |
| Browser* InProcessBrowserTest::CreateGuestBrowser() { |
| // Get Guest profile. |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| base::FilePath guest_path = profile_manager->GetGuestProfilePath(); |
| |
| Profile& guest_profile = |
| profiles::testing::CreateProfileSync(profile_manager, guest_path); |
| Profile* guest_profile_otr = |
| guest_profile.GetPrimaryOTRProfile(/*create_if_needed=*/true); |
| |
| // Create browser and add tab. |
| Browser* browser = |
| Browser::Create(Browser::CreateParams(guest_profile_otr, true)); |
| AddBlankTabAndShow(browser); |
| return browser; |
| } |
| #endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| void InProcessBrowserTest::AddBlankTabAndShow(Browser* browser) { |
| content::WebContents* blank_tab = chrome::AddSelectedTabWithURL( |
| browser, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_AUTO_TOPLEVEL); |
| content::TestNavigationObserver observer(blank_tab); |
| observer.Wait(); |
| |
| browser->window()->Show(); |
| } |
| |
| #if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_CHROMEOS_LACROS) |
| base::CommandLine InProcessBrowserTest::GetCommandLineForRelaunch() { |
| base::CommandLine new_command_line( |
| base::CommandLine::ForCurrentProcess()->GetProgram()); |
| base::CommandLine::SwitchMap switches = |
| base::CommandLine::ForCurrentProcess()->GetSwitches(); |
| switches.erase(switches::kUserDataDir); |
| switches.erase(switches::kSingleProcessTests); |
| switches.erase(switches::kSingleProcess); |
| new_command_line.AppendSwitch(switches::kLaunchAsBrowser); |
| |
| base::FilePath user_data_dir; |
| base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); |
| new_command_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir); |
| |
| for (base::CommandLine::SwitchMap::const_iterator iter = switches.begin(); |
| iter != switches.end(); ++iter) { |
| new_command_line.AppendSwitchNative((*iter).first, (*iter).second); |
| } |
| return new_command_line; |
| } |
| #endif // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_CHROMEOS_LACROS) |
| |
| base::FilePath InProcessBrowserTest::GetChromeTestDataDir() const { |
| return base::FilePath(FILE_PATH_LITERAL("chrome/test/data")); |
| } |
| |
| void InProcessBrowserTest::PreRunTestOnMainThread() { |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| content::NetworkConnectionChangeSimulator network_change_simulator; |
| network_change_simulator.InitializeChromeosConnectionType(); |
| #endif |
| |
| AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting(); |
| |
| // Take the ChromeBrowserMainParts' RunLoop to run ourself, when we |
| // want to wait for the browser to exit. |
| run_loop_ = ChromeBrowserMainParts::TakeRunLoopForTest(); |
| |
| // Pump startup related events. |
| content::RunAllPendingInMessageLoop(); |
| |
| SelectFirstBrowser(); |
| if (browser_) { |
| auto* tab = browser_->tab_strip_model()->GetActiveWebContents(); |
| content::WaitForLoadStop(tab); |
| SetInitialWebContents(tab); |
| } |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| // Do not use the real StorageMonitor for tests, which introduces another |
| // source of variability and potential slowness. |
| ASSERT_TRUE(storage_monitor::TestStorageMonitor::CreateForBrowserTests()); |
| #endif |
| |
| #if BUILDFLAG(IS_MAC) |
| // On Mac, without the following autorelease pool, code which is directly |
| // executed (as opposed to executed inside a message loop) would autorelease |
| // objects into a higher-level pool. This pool is not recycled in-sync with |
| // the message loops' pools and causes problems with code relying on |
| // deallocation via an autorelease pool (such as browser window closure and |
| // browser shutdown). To avoid this, the following pool is recycled after each |
| // time code is directly executed. |
| autorelease_pool_ = new base::mac::ScopedNSAutoreleasePool; |
| #endif |
| |
| // Pump any pending events that were created as a result of creating a |
| // browser. |
| content::RunAllPendingInMessageLoop(); |
| |
| if (browser_ && global_browser_set_up_function_) |
| ASSERT_TRUE(global_browser_set_up_function_(browser_)); |
| |
| #if BUILDFLAG(IS_MAC) |
| autorelease_pool_->Recycle(); |
| #endif |
| } |
| |
| void InProcessBrowserTest::PostRunTestOnMainThread() { |
| #if BUILDFLAG(IS_MAC) |
| autorelease_pool_->Recycle(); |
| #endif |
| |
| // Sometimes tests leave Quit tasks in the MessageLoop (for shame), so let's |
| // run all pending messages here to avoid preempting the QuitBrowsers tasks. |
| // TODO(https://crbug.com/922118): Remove this once it is no longer possible |
| // to post QuitCurrent* tasks. |
| content::RunAllPendingInMessageLoop(); |
| |
| QuitBrowsers(); |
| |
| // BrowserList should be empty at this point. |
| CHECK(BrowserList::GetInstance()->empty()); |
| } |
| |
| void InProcessBrowserTest::QuitBrowsers() { |
| if (chrome::GetTotalBrowserCount() == 0) { |
| browser_shutdown::NotifyAppTerminating(); |
| |
| // Post OnAppExiting call as a task because the code path CHECKs a RunLoop |
| // runs at the current thread. |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(&chrome::OnAppExiting)); |
| // Spin the message loop to ensure OnAppExitting finishes so that proper |
| // clean up happens before returning. |
| content::RunAllPendingInMessageLoop(); |
| return; |
| } |
| |
| // Invoke AttemptExit on a running message loop. |
| // AttemptExit exits the message loop after everything has been |
| // shut down properly. |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(&chrome::AttemptExit)); |
| RunUntilBrowserProcessQuits(); |
| |
| #if BUILDFLAG(IS_MAC) |
| // chrome::AttemptExit() will attempt to close all browsers by deleting |
| // their tab contents. The last tab contents being removed triggers closing of |
| // the browser window. |
| // |
| // On the Mac, this eventually reaches |
| // -[BrowserWindowController windowWillClose:], which will post a deferred |
| // -autorelease on itself to ultimately destroy the Browser object. The line |
| // below is necessary to pump these pending messages to ensure all Browsers |
| // get deleted. |
| content::RunAllPendingInMessageLoop(); |
| delete autorelease_pool_; |
| autorelease_pool_ = nullptr; |
| #endif |
| } |
| |
| #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(!base::CommandLine::ForCurrentProcess() |
| ->GetSwitchValuePath("lacros-mojo-socket-for-testing") |
| .empty()) |
| << "You can only start unique ash chrome when crosapi is enabled. " |
| << "It should not be necessary otherwise."; |
| CHECK(unique_ash_user_data_dir_.CreateUniqueTempDir()); |
| base::FilePath socket_file = |
| unique_ash_user_data_dir_.GetPath().Append("lacros.sock"); |
| |
| // Reset the current test runner connecting to the unique ash chrome. |
| base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); |
| 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::GUID::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) { |
| ash_cmdline.AppendSwitch(cmdline_switch); |
| } |
| |
| std::vector<std::string> all_enabled_features = { |
| "LacrosSupport", "LacrosPrimary", "LacrosOnly"}; |
| all_enabled_features.insert(enabled_features.end(), enabled_features.begin(), |
| enabled_features.end()); |
| 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) |