| // 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/string_util.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_switches.h" |
| #include "base/time/time.h" |
| #include "build/build_config.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/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/browser_window/public/browser_window_interface.h" |
| #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.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/chrome_test_utils.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/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/password_manager/core/browser/password_manager_switches.h" |
| #include "components/prefs/pref_service.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 "extensions/common/extension_features.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/test/test_file_util.h" |
| #include "base/win/scoped_com_initializer.h" |
| #include "base/win/windows_version.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) |
| #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) |
| |
| #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_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) |
| #include "chrome/browser/ui/ui_features.h" |
| #endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) |
| |
| namespace { |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| 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, |
| instance_id::InstanceIDDriver* instance_id_driver, |
| PrefService* profile_prefs, |
| 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) |
| |
| #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) |
| |
| // 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 { |
| public: |
| void PostEarlyInitialization() override { |
| g_browser_process->set_additional_os_crypt_async_provider_for_test( |
| // Lowest precedence, any other registered key provider should always |
| // take precedence over this one. |
| /*precedence=*/1u, |
| std::make_unique<SlowTestKeyProvider>(base::Milliseconds(10))); |
| } |
| |
| private: |
| class SlowTestKeyProvider : public os_crypt_async::KeyProvider { |
| public: |
| explicit SlowTestKeyProvider(base::TimeDelta sleep_time) |
| : sleep_time_(sleep_time) {} |
| |
| private: |
| void GetKey(KeyCallback callback) override { |
| // Fixed key. |
| os_crypt_async::Encryptor::Key key( |
| std::vector<uint8_t>( |
| os_crypt_async::Encryptor::Key::kAES256GCMKeySize, 0xCE), |
| os_crypt_async::mojom::Algorithm::kAES256GCM); |
| |
| base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce( |
| [](KeyCallback callback, os_crypt_async::Encryptor::Key key) { |
| std::move(callback).Run("test_key_provider", std::move(key)); |
| }, |
| std::move(callback), std::move(key)), |
| sleep_time_); |
| } |
| |
| // It's important this does not get used for encrypt because otherwise tests |
| // that verify rollback from async to sync will fail as data might be |
| // encrypted with the test key above. |
| bool UseForEncryption() override { return false; } |
| bool IsCompatibleWithOsCryptSync() override { return false; } |
| const base::TimeDelta sleep_time_; |
| }; |
| }; |
| |
| void EnsureBrowserContextKeyedServiceFactoriesForTestingBuilt() { |
| NotificationDisplayServiceTester::EnsureFactoryBuilt(); |
| } |
| |
| InProcessBrowserTest* g_current_test; |
| |
| } // 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 |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| 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 defined(TOOLKIT_VIEWS) |
| views::Widget::Widgets widgets_to_layout; |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| // 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()) { |
| widgets_to_layout.merge(views::Widget::GetAllChildWidgets(root_window)); |
| } |
| #else |
| widgets_to_layout = views::test::WidgetTest::GetAllWidgets(); |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| for (views::Widget* widget : widgets_to_layout) { |
| widget->LayoutRootViewIfNecessary(); |
| } |
| #endif // defined(TOOLKIT_VIEWS) |
| } |
| |
| void InProcessBrowserTest::Initialize() { |
| g_current_test = this; |
| |
| // 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. |
| chrome_test_utils::OverrideChromeTestDataDir(); |
| |
| #if BUILDFLAG(IS_MAC) |
| bundle_swizzler_ = std::make_unique<ScopedBundleSwizzlerMac>(); |
| #endif |
| |
| embedded_test_server()->AddDefaultHandlers(GetChromeTestDataDir()); |
| InitializeHTTPSTestServer(); |
| embedded_https_test_server().AddDefaultHandlers(GetChromeTestDataDir()); |
| |
| // Force all buttons not overflow to prevent test flakiness. |
| ToolbarControllerUtil::SetPreventOverflowForTesting(true); |
| |
| 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); |
| |
| // Allow unpacked extensions without developer mode for testing. |
| disabled_features.push_back( |
| extensions_features::kExtensionDisableUnsupportedDeveloper); |
| |
| #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) |
| // Disable session restore infobar the experiment as it causes test failures. |
| disabled_features.push_back(features::kSessionRestoreInfobar); |
| #endif |
| // 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); |
| |
| create_services_subscription_ = |
| BrowserContextDependencyManager::GetInstance() |
| ->RegisterCreateServicesCallbackForTesting(base::BindRepeating( |
| &InProcessBrowserTest::OnWillCreateBrowserContextKeyedServices, |
| base::Unretained(this))); |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| launch_browser_for_testing_ = |
| std::make_unique<ash::full_restore::ScopedLaunchBrowserForTesting>(); |
| #endif |
| #if BUILDFLAG(IS_WIN) |
| base::GetPathsAllowedToLeak() = {L"\\Sync Data", L"\\Local Storage\\leveldb", |
| L"\\DataSharing", L"\\Collaboration"}; |
| #endif // BUILDFLAG(IS_WIN) |
| } |
| |
| InProcessBrowserTest::~InProcessBrowserTest() { |
| g_current_test = nullptr; |
| } |
| |
| InProcessBrowserTest* InProcessBrowserTest::GetCurrent() { |
| return g_current_test; |
| } |
| |
| void InProcessBrowserTest::SetUp() { |
| // Browser tests will create their own g_browser_process later. |
| DCHECK(!g_browser_process); |
| |
| ui_controls::ResetUIControlsIfEnabled(); |
| |
| 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(switches::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(crbug.com/40062835): Clean up other paths that call this Init() |
| // function, which are now redundant. |
| base::PoissonAllocationSampler::Init(); |
| |
| // 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) |
| // 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, many tests will hang waiting for a user to |
| // approve KeyChain/kwallet access. On Linux this is done in |
| // test_launcher_utils::PrepareBrowserCommandLineForTests by using |
| // --password-store=basic. On Windows this is not needed as OS APIs never |
| // block. |
| #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS) |
| OSCryptMocker::SetUp(); |
| #elif BUILDFLAG(IS_LINUX) |
| // On Linux, verify that a password store backend is specified - it's either |
| // set to "basic" in test_launcher_utils::PrepareBrowserCommandLineForTests or |
| // could be overridden on the command line manually. |
| CHECK(command_line->HasSwitch(password_manager::kPasswordStore)); |
| #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); |
| |
| // Device sync (for multidevice "Better Together") is ChromeOS 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); |
| |
| // On CrOS, we need to use ash::Shell to get all root windows. |
| views::test::WidgetTest::SetRootWindowProvider( |
| base::BindRepeating(&ash::Shell::GetAllRootWindows)); |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| // 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(); |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| // The Search Engine Choice service may attempt to show a modal dialog to the |
| // profile on browser start, which is unexpected by mosts tests. Tests which |
| // expect this can allow the prompt as desired. |
| SearchEngineChoiceDialogService::SetDialogDisabledForTests( |
| /*dialog_disabled=*/true); |
| #endif |
| |
| 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 (embedded_https_test_server().Started()) { |
| ASSERT_TRUE(embedded_https_test_server().ShutdownAndWaitUntilComplete()); |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| ash::device_sync::DeviceSyncImpl::Factory::SetCustomFactory(nullptr); |
| launch_browser_for_testing_ = nullptr; |
| views::test::WidgetTest::SetRootWindowProvider(base::NullCallback()); |
| #endif |
| } |
| |
| // static |
| size_t InProcessBrowserTest::GetTestPreCount() { |
| constexpr std::string_view kPreTestPrefix = "PRE_"; |
| std::string_view 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) || BUILDFLAG(IS_WIN) |
| static_cast<ChromeBrowserMainParts*>(parts)->AddParts( |
| std::make_unique<ChromeBrowserMainExtraPartsBrowserProcessInjection>()); |
| #endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) |
| static_cast<ChromeBrowserMainParts*>(parts)->AddParts( |
| std::make_unique<OSCryptAsyncExtraSetUp>()); |
| } |
| |
| void InProcessBrowserTest::SetBrowser(BrowserWindowInterface* browser) { |
| browser_ = browser ? browser->GetBrowserForMigrationOnly() : nullptr; |
| } |
| |
| 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::SetUpLocalStatePrefService( |
| PrefService* local_state) { |
| #if BUILDFLAG(IS_WIN) |
| // Put the current build version number in the prefs, so that pinned taskbar |
| // icons aren't migrated. |
| local_state->SetString(prefs::kShortcutMigrationVersion, |
| std::string(version_info::GetVersionNumber())); |
| #endif // BUILDFLAG(IS_WIN); |
| } |
| |
| Profile* InProcessBrowserTest::GetProfile() const { |
| return browser() ? browser()->profile() : nullptr; |
| } |
| |
| void InProcessBrowserTest::CloseBrowserSynchronously( |
| BrowserWindowInterface* browser) { |
| CloseBrowserAsynchronously(browser); |
| ui_test_utils::WaitForBrowserToClose(browser); |
| } |
| |
| void InProcessBrowserTest::CloseBrowserAsynchronously( |
| BrowserWindowInterface* browser) { |
| browser->GetWindow()->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( |
| BrowserWindowInterface* browser, |
| int index, |
| const GURL& url, |
| ui::PageTransition transition, |
| bool check_navigation_success) { |
| return AddTabAtIndexToBrowser(browser, index, url, transition); |
| } |
| |
| bool InProcessBrowserTest::AddTabAtIndexToBrowser( |
| BrowserWindowInterface* browser, |
| int index, |
| const GURL& url, |
| ui::PageTransition transition) { |
| NavigateParams params(browser, url, transition); |
| params.tabstrip_index = index; |
| params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; |
| params.pwa_navigation_capturing_force_off = true; |
| Navigate(¶ms); |
| RunScheduledLayouts(); |
| |
| 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/40222482): 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) |
| 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, |
| DevToolsOpenedByAction::kUnknown); |
| 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) |
| 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) |
| |
| 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(); |
| RunScheduledLayouts(); |
| browser->window()->Show(); |
| } |
| |
| #if !BUILDFLAG(IS_MAC) |
| 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) |
| |
| base::FilePath InProcessBrowserTest::GetChromeTestDataDir() const { |
| return chrome_test_utils::GetChromeTestDataDir(); |
| } |
| |
| void InProcessBrowserTest::PreRunTestOnMainThread() { |
| 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(); |
| |
| SetBrowser(GetLastActiveBrowserWindowInterfaceWithAnyProfile()); |
| if (browser_ && !browser_->tab_strip_model()->empty()) { |
| base::WeakPtr<content::WebContents> tab = |
| browser_->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(); |
| content::WaitForLoadStop(tab.get()); |
| if (tab) { |
| SetInitialWebContents(tab.get()); |
| } |
| } |
| |
| #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_.emplace(); |
| #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 |
| |
| 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 OnAppExiting finishes so that proper |
| // clean up happens before returning. |
| content::RunAllPendingInMessageLoop(); |
| #if BUILDFLAG(IS_MAC) |
| autorelease_pool_.reset(); |
| #endif |
| 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(); |
| autorelease_pool_.reset(); |
| #endif |
| } |
| |
| void InProcessBrowserTest::OnWillCreateBrowserContextKeyedServices( |
| content::BrowserContext* context) { |
| SetUpProtocolHandlerTestFactories(context); |
| SetUpBrowserContextKeyedServices(context); |
| } |
| |
| void InProcessBrowserTest::SetUpProtocolHandlerTestFactories( |
| content::BrowserContext* context) { |
| // Use TestProtocolHandlerRegistryDelegate to prevent OS integration during |
| // the protocol registration process. |
| ProtocolHandlerRegistryFactory::GetInstance()->SetTestingFactory( |
| context, base::BindRepeating([](content::BrowserContext* context) |
| -> std::unique_ptr<KeyedService> { |
| return custom_handlers::ProtocolHandlerRegistry::Create( |
| Profile::FromBrowserContext(context)->GetPrefs(), |
| std::make_unique< |
| custom_handlers::TestProtocolHandlerRegistryDelegate>()); |
| })); |
| } |