blob: c1efccb8547d5a3bc1eded49d86a985baa8db891 [file]
// 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.
#ifndef CHROME_TEST_BASE_IN_PROCESS_BROWSER_TEST_H_
#define CHROME_TEST_BASE_IN_PROCESS_BROWSER_TEST_H_
#include <map>
#include <memory>
#include <string>
#include "base/callback_list.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/feature_engagement/test/scoped_iph_feature_list.h"
#include "content/public/test/browser_test_base.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/page_transition_types.h"
#if BUILDFLAG(IS_MAC)
#include <optional>
#include "base/apple/scoped_nsautorelease_pool.h"
#include "base/memory/stack_allocated.h"
#include "ui/base/test/scoped_fake_full_keyboard_access.h"
#endif
namespace base {
class CommandLine;
#if BUILDFLAG(IS_WIN)
namespace win {
class ScopedCOMInitializer;
}
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_CHROMEOS_LACROS)
class Process;
class Version;
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
} // namespace base
namespace content {
class BrowserContext;
class WebContents;
} // namespace content
#if defined(TOOLKIT_VIEWS)
namespace views {
class ViewsDelegate;
}
#endif // defined(TOOLKIT_VIEWS)
namespace display {
class Screen;
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
namespace ash::full_restore {
class ScopedLaunchBrowserForTesting;
} // namespace ash::full_restore
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
class Browser;
#if BUILDFLAG(IS_CHROMEOS_LACROS)
class FakeAccountManagerUI;
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
class MainThreadStackSamplingProfiler;
class PrefService;
class Profile;
#if BUILDFLAG(IS_MAC)
class ScopedBundleSwizzlerMac;
#endif // BUILDFLAG(IS_MAC)
// Base class for tests that bring up Browser instances.
// Writing tests with InProcessBrowserTest is slightly different than that of
// other tests. This is necessitated by InProcessBrowserTest running a message
// loop. To use InProcessBrowserTest do the following:
// . Use the macro IN_PROC_BROWSER_TEST_F to define your test.
// . Your test method is invoked on the ui thread. If you need to block until
// state changes you'll need to run the message loop from your test method.
// For example, if you need to wait till a find bar has completely been shown
// you'll need to create a base::RunLoop and call it's Run() method. When the
// message bar is shown, invoke loop.QuitWhenIdle()/loop.QuitWhenIdleClosure()
// to return control back to your test method.
// . If you subclass and override SetUp(), be sure and invoke
// InProcessBrowserTest::SetUp(). (But see also BrowserTestBase's
// SetUpOnMainThread(), SetUpInProcessBrowserTestFixture(), and other related
// methods for a cleaner alternative).
//
// To include the default implementation of RunTestOnMainThread() and TestBody()
// for Gtests, it's also necessary to include the file
// "content/public/test/browser_test.h"
//
// The following hook methods are called in sequence before BrowserMain(), so
// no browser has been created yet. They are mainly for setting up the
// environment for running the browser.
// . SetUpCommandLine()
// . SetUpDefaultCommandLine()
// . SetUpUserDataDirectory()
//
// Default command line switches are added in the default implementation of
// SetUpDefaultCommandLine(). Additional command line switches can be simply
// appended in SetUpCommandLine() without the need to invoke
// InProcessBrowserTest::SetUpCommandLine(). If a test needs to change the
// default command line, it can override SetUpDefaultCommandLine(), where it
// should invoke InProcessBrowserTest::SetUpDefaultCommandLine() to get the
// default switches, and modify them as needed.
//
// SetUpOnMainThread() is called just after creating the default browser object
// and before executing the real test code. It's mainly for setting up things
// related to the browser object and associated window, like opening a new Tab
// with a testing page loaded.
//
// TearDownOnMainThread() is called just after executing the real test code to
// do necessary clean-up before the browser is torn down.
//
// TearDownInProcessBrowserTestFixture() is called after BrowserMain() exits to
// clean up things set up for running the browser.
//
// By default a single Browser is created in BrowserMain(). You can obviously
// create more as needed.
// See ui_test_utils for a handful of methods designed for use with this class.
//
// It's possible to write browser tests that span a restart by splitting each
// run of the browser process into a separate test. Example:
//
// IN_PROC_BROWSER_TEST_F(Foo, PRE_Bar) {
// do something
// }
//
// IN_PROC_BROWSER_TEST_F(Foo, Bar) {
// verify something persisted from before
// }
//
// This is recursive, so PRE_PRE_Bar would run before PRE_BAR.
class InProcessBrowserTest : public content::BrowserTestBase {
public:
InProcessBrowserTest();
#if defined(TOOLKIT_VIEWS)
// |views_delegate| is used for tests that want to use a derived class of
// ViewsDelegate to observe or modify things like window placement and Widget
// params.
explicit InProcessBrowserTest(
std::unique_ptr<views::ViewsDelegate> views_delegate);
#endif
InProcessBrowserTest(const InProcessBrowserTest&) = delete;
InProcessBrowserTest& operator=(const InProcessBrowserTest&) = delete;
~InProcessBrowserTest() override;
// Returns the currently running InProcessBrowserTest.
static InProcessBrowserTest* GetCurrent();
// Configures everything for an in process browser test, then invokes
// BrowserMain(). BrowserMain() ends up invoking RunTestOnMainThreadLoop().
void SetUp() override;
// Restores state configured in SetUp().
void TearDown() override;
using SetUpBrowserFunction = bool(const Browser*);
// Sets a function that is called from InProcessBrowserTest::SetUp() with the
// first browser. This is intended to set up state applicable to all tests
// in the suite. For example, interactive_ui_tests installs a function that
// ensures the first browser is in the foreground, active and has focus.
static void set_global_browser_set_up_function(
SetUpBrowserFunction* set_up_function) {
global_browser_set_up_function_ = set_up_function;
}
// Counts the number of "PRE_" prefixes in the test name. This is used to
// differentiate between different PRE tests in browser test constructors
// and setup functions.
static size_t GetTestPreCount();
// Returns the browser created by BrowserMain().
// If no browser is created in BrowserMain(), this will return nullptr unless
// another browser instance is created at a later time and
// SelectFirstBrowser() is called.
// In Lacros only mode, ash web browser is disabled, therefore, browser()
// is not created and should not be accessed from the tests in
// browser_tests_require_lacros test suite.
Browser* browser() const { return browser_; }
// Set |browser_| to the first browser on the browser list.
// Call this when your test subclass wants to access a non-null browser
// instance through browser() but browser creation is delayed until after
// PreRunTestOnMainThread().
void SelectFirstBrowser();
// This function is used to record a set of properties for a test case in
// gtest result and that will be used by resultDB. The map's key value pair
// are defined by each test case. For use case check this bug:
// https://crbug.com/1365899
// The final value of the result is the format of key1=value1;key2=value2.
void RecordPropertyFromMap(const std::map<std::string, std::string>& tags);
// Tests can override this to customize the initial local_state.
virtual void SetUpLocalStatePrefService(PrefService* local_state);
// Start ash-chrome with specific flags.
// In general, there is a shared ash chrome started and a lacros chrome
// started before a test case. But for some tests, you may need a special
// ash chrome. 2 common use cases:
// 1. you need to enable a feature in ash chrome then your test can verify
// some behavior. In this case you need to call this function to start
// a unique ash chrome with the feature enabled.
// 2. your test case will pollute ash and cause following test cases fail or
// flaky. Instead of implementing cleanup in TearDown(), using a
// unique ash just for the test is better.
// Call this function in the test SetUp() function before invoking
// InProcessBrowserTest::SetUp().
// This function has negative performance impact:
// 1. Start additional ash chrome uses more time.
// 2. Additional ash chrome uses more resources.
// The shared ash chrome is still running. By calling this function,
// you start another ash chrome.
// Args:
// enabled_features: Additional features to be enabled in ash chrome.
// disabled_features: Additional features to be disabled in ash chrome.
// additional_cmdline_switches: Additional cmdline switches.
// e.g. {"enable-pixel-outputs-in-tests"}
// bug_number_and_reason: Not used in code. But please provide information
// about why you need unique ash chrome. Hopefully this can help reduce
// the usage of unique ash.
// e.g. "crbug.com/11. Switch to shared ash when feature XX is default."
//
// After you call this function in SetUp(), before the test case test body,
// a unique ash chrome will be started and a lacros chrome will be connected
// to it. After the test case finishes, the unique ash chrome will be
// terminated and the next test case will use the default shared ash chrome.
#if BUILDFLAG(IS_CHROMEOS_LACROS)
void 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);
#endif
protected:
// Closes the given browser and waits for it to release all its resources.
void CloseBrowserSynchronously(Browser* browser);
// Closes the browser without waiting for it to release all its resources.
// WARNING: This may leave tasks posted, but not yet run, in the message
// loops. Prefer CloseBrowserSynchronously() over this method.
void CloseBrowserAsynchronously(Browser* browser);
// Closes all browsers. No guarantees are made about the destruction of
// outstanding resources.
void CloseAllBrowsers();
// Runs the main thread message loop until the BrowserProcess indicates
// we should quit. This will normally be called automatically during test
// teardown, but may instead be run manually by the test, if necessary.
void RunUntilBrowserProcessQuits();
// Convenience methods for adding tabs to a Browser. Returns true if the
// navigation succeeded. |check_navigation_success| is ignored and will be
// removed as part of check_navigation_success http://crbug.com/1014186.
// Do not add new usages of the version with |check_navigation_success|.
[[nodiscard]] bool AddTabAtIndexToBrowser(Browser* browser,
int index,
const GURL& url,
ui::PageTransition transition,
bool check_navigation_success);
[[nodiscard]] bool AddTabAtIndexToBrowser(Browser* browser,
int index,
const GURL& url,
ui::PageTransition transition);
[[nodiscard]] bool AddTabAtIndex(int index,
const GURL& url,
ui::PageTransition transition);
// Sets up default command line that will be used to launch the child browser
// process with an in-process test. Called by SetUp() after SetUpCommandLine()
// to add default commandline switches. A default implementation is provided
// in this class. If a test does not want to use the default implementation,
// it should override this method.
virtual void SetUpDefaultCommandLine(base::CommandLine* command_line);
// Initializes the contents of the user data directory. Called by SetUp()
// after creating the user data directory, but before any browser is launched.
// If a test wishes to set up some initial non-empty state in the user data
// directory before the browser starts up, it can do so here. Returns true if
// successful. To set initial prefs, see SetUpLocalStatePrefService.
[[nodiscard]] virtual bool SetUpUserDataDirectory();
// Initializes the display::Screen instance.
virtual void SetScreenInstance();
// BrowserTestBase:
void PreRunTestOnMainThread() override;
void PostRunTestOnMainThread() override;
void CreatedBrowserMainParts(content::BrowserMainParts* parts) override;
// Ensures that no devtools are open, and then opens the devtools.
void OpenDevToolsWindow(content::WebContents* web_contents);
// Opens |url| in an incognito browser window with the incognito profile of
// |profile|, blocking until the navigation finishes. This will create a new
// browser if a browser with the incognito profile does not exist. Returns the
// incognito window Browser.
Browser* 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* CreateBrowser(Profile* profile);
// Similar to |CreateBrowser|, but creates an incognito browser. If |profile|
// is omitted, the currently active profile will be used.
Browser* CreateIncognitoBrowser(Profile* profile = nullptr);
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
// Similar to |CreateBrowser|, but creates a Guest browser.
// To create a ChromeOS Guest user session, you need to add proper switches to
// commandline while setting up the test. For an example see
// AppListClientGuestModeBrowserTest::SetUpCommandLine.
Browser* CreateGuestBrowser();
#endif
// Creates a browser for a popup window with a single tab (about:blank), waits
// for the tab to finish loading, and shows the browser.
Browser* CreateBrowserForPopup(Profile* profile);
// Creates a browser for an application and waits for it to load and shows
// the browser.
Browser* CreateBrowserForApp(const std::string& app_name, Profile* profile);
// Called from the various CreateBrowser methods to add a blank tab, wait for
// the navigation to complete, and show the browser's window.
void AddBlankTabAndShow(Browser* browser);
#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_CHROMEOS_LACROS)
// Return a CommandLine object that is used to relaunch the browser_test
// binary as a browser process. This function is deliberately not defined on
// the Mac because re-using an existing browser process when launching from
// the command line isn't a concept that we support on the Mac; AppleEvents
// are the Mac solution for the same need. Any test based on these functions
// doesn't apply to the Mac. Likewise, Lacros is always launched by ash, and
// not by the the process restarting itself.
base::CommandLine GetCommandLineForRelaunch();
#endif
#if BUILDFLAG(IS_MAC)
// Returns the autorelease pool in use inside RunTestOnMainThreadLoop().
base::apple::ScopedNSAutoreleasePool* AutoreleasePool() {
return &autorelease_pool_.value();
}
#endif // BUILDFLAG(IS_MAC)
// Returns the test data path used by the embedded test server.
base::FilePath GetChromeTestDataDir() const;
// Returns the HTTPS embedded test server.
// By default, the HTTPS test server is configured to have a valid
// certificate for the set of hostnames:
// - [*.]example.com
// - [*.]foo.com
// - [*.]bar.com
// - [*.]a.com
// - [*.]b.com
// - [*.]c.com
//
// After starting the server, you can get a working HTTPS URL for any of
// those hostnames. For example:
//
// ```
// ASSERT_TRUE(embedded_https_test_server().Start());
// embedded_https_test_server().GetURL("foo.com", "/simple.html");
// ```
//
// Tests can override the set of valid hostnames by calling
// `net::EmbeddedTestServer::SetCertHostnames()` before starting the test
// server, and a valid test certificate will be automatically generated for
// the hostnames passed in. For example:
//
// ```
// embedded_https_test_server().SetCertHostnames(
// {"example.com", "example.org"});
// ASSERT_TRUE(embedded_https_test_server().Start());
// embedded_https_test_server().GetURL("example.org", "/simple.html");
// ```
const net::EmbeddedTestServer& embedded_https_test_server() const {
return *embedded_https_test_server_;
}
net::EmbeddedTestServer& embedded_https_test_server() {
return *embedded_https_test_server_;
}
void set_exit_when_last_browser_closes(bool value) {
exit_when_last_browser_closes_ = value;
}
void set_open_about_blank_on_browser_launch(bool value) {
open_about_blank_on_browser_launch_ = value;
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
void set_launch_browser_for_testing(
std::unique_ptr<ash::full_restore::ScopedLaunchBrowserForTesting>
launch_browser_for_testing);
#endif
// Runs scheduled layouts on all Widgets using
// Widget::LayoutRootViewIfNecessary(). No-op outside of Views.
void RunScheduledLayouts();
#if BUILDFLAG(IS_CHROMEOS_LACROS)
FakeAccountManagerUI* GetFakeAccountManagerUI() const;
// Return the Ash chrome version hooked with Lacros. This API does not
// depend on crosapi. So it is safe to call when crosapi is disabled and
// as early as in test SetUp().
static base::Version GetAshChromeVersion();
// The following are the helper functions to manage Ash browser based windows
// from Lacros browser tests. When running with Ash, Lacros browser tests can
// create some Ash browser based UIs, such as SWA, Web UI, etc. These UIs
// must be cleaned up when the test tears down, so that they won't pollute
// the tests running after, since Lacros browser tests are running with the
// shared Ash instance by default.
void VerifyNoAshBrowserWindowOpenRightNow();
void CloseAllAshBrowserWindows();
void WaitUntilAtLeastOneAshBrowserWindowOpen();
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
std::unique_ptr<display::Screen> screen_;
#endif
private:
friend class StartUniqueAshBrowserTest;
void Initialize();
// Quits all open browsers and waits until there are no more browsers.
void QuitBrowsers();
// This is called to set up the test factories for each browser context.
// It ensures that ProtocolHandlerRegistry instances use
// TestProtocolHandlerRegistryDelegate, which prevents browser tests
// from changing the OS integration of protocols.
void SetupProtocolHandlerTestFactories(content::BrowserContext* context);
static SetUpBrowserFunction* global_browser_set_up_function_;
// Usually references the browser created in BrowserMain().
// If no browser is created in BrowserMain(), then |browser_| will remain
// nullptr unless SelectFirstBrowser() is called after the creation of the
// first browser instance at a later time.
raw_ptr<Browser, AcrossTasksDanglingUntriaged> browser_ = nullptr;
// Used to run the process until the BrowserProcess signals the test to quit.
std::unique_ptr<base::RunLoop> run_loop_;
// Temporary user data directory. Used only when a user data directory is not
// specified in the command line.
base::ScopedTempDir temp_user_data_dir_;
// True if we should exit the tests after the last browser instance closes.
bool exit_when_last_browser_closes_ = true;
// True if the about:blank tab should be opened when the browser is launched.
bool open_about_blank_on_browser_launch_ = true;
// Use a default download directory to make sure downloads don't end up in the
// system default location.
base::ScopedTempDir default_download_dir_;
base::test::ScopedFeatureList scoped_feature_list_;
// In-product help can conflict with tests' expected window activation and
// focus. This disables all IPH by default.
//
// This was previously done by disabling all IPH features, but that destroyed
// all field trials that included an IPH because overriding any feature
// touched by a field trial disables the field trial (see crbug.com/1381669).
//
// Individual tests can re-enable IPH using another ScopedIphFeatureList.
feature_engagement::test::ScopedIphFeatureList block_all_iph_feature_list_;
#if BUILDFLAG(IS_MAC)
STACK_ALLOCATED_IGNORE("https://crbug.com/1424190")
std::optional<base::apple::ScopedNSAutoreleasePool> autorelease_pool_;
std::unique_ptr<ScopedBundleSwizzlerMac> bundle_swizzler_;
// Enable fake full keyboard access by default, so that tests don't depend on
// system setting of the test machine. Also, this helps to make tests on Mac
// more consistent with other platforms, where most views are focusable by
// default.
ui::test::ScopedFakeFullKeyboardAccess faked_full_keyboard_access_;
#endif // BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_WIN)
std::unique_ptr<base::win::ScopedCOMInitializer> com_initializer_;
#endif
#if defined(TOOLKIT_VIEWS)
std::unique_ptr<views::ViewsDelegate> views_delegate_;
#endif
std::unique_ptr<MainThreadStackSamplingProfiler> sampling_profiler_;
// Used to set up test factories for each browser context.
base::CallbackListSubscription create_services_subscription_;
// Embedded HTTPS test server, cheap to create, started on demand.
std::unique_ptr<net::EmbeddedTestServer> embedded_https_test_server_;
#if BUILDFLAG(IS_CHROMEOS_ASH)
// ChromeOS does not create a browser by default when the full restore feature
// is enabled. However almost all existing browser tests assume a browser is
// created. Add ScopedLaunchBrowserForTesting to force creating a browser for
// testing, when the full restore feature is enabled.
std::unique_ptr<ash::full_restore::ScopedLaunchBrowserForTesting>
launch_browser_for_testing_;
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
base::ScopedTempDir unique_ash_user_data_dir_;
base::Process ash_process_;
#endif
};
// When including either in_process_browser_test.h or android_browser_test.h
// depending on the platform, use this type alias as the test base class.
using PlatformBrowserTest = InProcessBrowserTest;
#endif // CHROME_TEST_BASE_IN_PROCESS_BROWSER_TEST_H_