blob: a8b0ef0a121cc36139cb54c2acce7e154a24701b [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/web_test/browser/web_test_browser_main_runner.h"
#include <iostream>
#include <memory>
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/run_loop.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/viz/common/switches.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/public/browser/browser_main_runner.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/shell/browser/shell.h"
#include "content/shell/common/shell_switches.h"
#include "content/test/gpu_browsertest_helpers.h"
#include "content/web_test/browser/test_info_extractor.h"
#include "content/web_test/browser/web_test_browser_main_platform_support.h"
#include "content/web_test/browser/web_test_control_host.h"
#include "content/web_test/common/web_test_switches.h"
#include "gpu/config/gpu_switches.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "media/base/media_switches.h"
#include "net/base/filename_util.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/base/url_util.h"
#include "ppapi/buildflags/buildflags.h"
#include "services/network/public/cpp/network_switches.h"
#include "ui/base/ui_base_switches.h"
#include "ui/display/display_switches.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_switches.h"
#if BUILDFLAG(ENABLE_PPAPI)
#include "content/public/test/ppapi_test_utils.h"
#endif
#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_IOS)
#include <sys/socket.h>
#include <unistd.h>
#endif
#if BUILDFLAG(IS_FUCHSIA)
#include <lib/sys/cpp/component_context.h>
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/process_context.h"
#include "ui/ozone/public/ozone_switches.h"
#endif
namespace content {
namespace {
#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_IOS)
// Fuchsia doesn't support stdin stream for packaged apps, and stdout from
// run-test-suite not only has extra emissions from the Fuchsia test
// infrastructure, it also merges stderr and stdout together. Combined, these
// mean that when running content_shell on Fuchsia it's not possible to use
// stdin to pass list of tests or to reliably use stdout to emit results. To
// workaround this issue for web tests we redirect stdin and stdout to a TCP
// socket connected to the web test runner. The runner uses --stdio-redirect to
// specify address and port for stdin and stdout redirection.
//
// iOS is in a similar situation where the simulator does not support the use of
// the stdin stream for applications. Therefore, iOS also redirects stdin and
// stdout to a TCP socket that is connected to the web test runner.
constexpr char kStdioRedirectSwitch[] = "stdio-redirect";
void ConnectStdioSocket(const std::string& host_and_port) {
std::string host;
int port;
net::IPAddress address;
if (!net::ParseHostAndPort(host_and_port, &host, &port) ||
!address.AssignFromIPLiteral(host)) {
LOG(FATAL) << "Invalid stdio address: " << host_and_port;
}
sockaddr_storage sockaddr_storage;
sockaddr* addr = reinterpret_cast<sockaddr*>(&sockaddr_storage);
socklen_t addr_len = sizeof(sockaddr_storage);
net::IPEndPoint endpoint(address, port);
bool converted = endpoint.ToSockAddr(addr, &addr_len);
CHECK(converted);
int fd = socket(addr->sa_family, SOCK_STREAM, 0);
PCHECK(fd >= 0);
int result = connect(fd, addr, addr_len);
PCHECK(result == 0) << "Failed to connect to " << host_and_port;
result = dup2(fd, STDIN_FILENO);
PCHECK(result == STDIN_FILENO) << "Failed to dup socket to stdin";
result = dup2(fd, STDOUT_FILENO);
PCHECK(result == STDOUT_FILENO) << "Failed to dup socket to stdout";
PCHECK(close(fd) == 0);
}
#endif // BUILDFLAG(IS_FUCHSIA)
void RunOneTest(const content::TestInfo& test_info,
content::WebTestControlHost* web_test_control_host,
content::BrowserMainRunner* main_runner) {
TRACE_EVENT0("shell", "WebTestBrowserMainRunner::RunOneTest");
DCHECK(web_test_control_host);
web_test_control_host->PrepareForWebTest(test_info);
main_runner->Run();
web_test_control_host->ResetBrowserAfterWebTest();
}
void RunTests(content::BrowserMainRunner* main_runner) {
#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_IOS)
if (auto& cmd_line = *base::CommandLine::ForCurrentProcess();
cmd_line.HasSwitch(kStdioRedirectSwitch)) {
ConnectStdioSocket(cmd_line.GetSwitchValueASCII(kStdioRedirectSwitch));
}
#endif // BUILDFLAG(IS_FUCHSIA)
TRACE_EVENT0("shell", "WebTestBrowserMainRunner::RunTests");
content::WebTestControlHost test_controller;
{
// We're outside of the message loop here, and this is a test.
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath temp_path;
base::GetTempDir(&temp_path);
test_controller.SetTempPath(temp_path);
}
{
// Kick off the launch of the GPU process early, to minimize blocking
// startup of the first renderer process in PrepareForWebTest. (This avoids
// GPU process startup time from being counted in the first test's timeout,
// hopefully making it less likely to time out flakily.)
// https://crbug.com/953991
TRACE_EVENT0("shell",
"WebTestBrowserMainRunner::RunTests::EstablishGpuChannelSync");
content::GpuBrowsertestEstablishGpuChannelSyncRunLoop();
}
std::cout << "#READY\n";
std::cout.flush();
content::TestInfoExtractor test_extractor(
*base::CommandLine::ForCurrentProcess());
std::unique_ptr<content::TestInfo> test_info;
while ((test_info = test_extractor.GetNextTest())) {
RunOneTest(*test_info, &test_controller, main_runner);
}
}
} // namespace
void WebTestBrowserMainRunner::Initialize() {
#if BUILDFLAG(IS_WIN)
bool layout_system_deps_ok = content::WebTestBrowserCheckLayoutSystemDeps();
CHECK(layout_system_deps_ok);
#endif
base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
CHECK(browser_context_path_for_web_tests_.CreateUniqueTempDir());
CHECK(!browser_context_path_for_web_tests_.GetPath().MaybeAsASCII().empty());
command_line.AppendSwitchASCII(
switches::kContentShellUserDataDir,
browser_context_path_for_web_tests_.GetPath().MaybeAsASCII());
command_line.AppendSwitch(switches::kIgnoreCertificateErrors);
// Disable occlusion tracking. In a headless shell WebContents would always
// behave as if they were occluded, i.e. would not render frames and would
// not receive input events. For non-headless mode we do not want tests
// running in parallel to trigger occlusion tracking.
command_line.AppendSwitch(
switches::kDisableBackgroundingOccludedWindowsForTesting);
// Always disable the unsandbox GPU process for DX12 Info collection to avoid
// interference. This GPU process is launched 120 seconds after chrome starts.
command_line.AppendSwitch(switches::kDisableGpuProcessForDX12InfoCollection);
#if BUILDFLAG(ENABLE_PPAPI)
CHECK(ppapi::RegisterBlinkTestPlugin(&command_line));
#endif
command_line.AppendSwitch(cc::switches::kEnableGpuBenchmarking);
command_line.AppendSwitch(switches::kEnableLogging);
command_line.AppendSwitch(switches::kAllowFileAccessFromFiles);
// On IOS, we always use hardware GL for the web test as content_browsertests.
// See also https://crrev.com/c/4885954.
if constexpr (!BUILDFLAG(IS_IOS)) {
// only default to a software GL if the flag isn't already specified.
if (!command_line.HasSwitch(switches::kUseGpuInTests) &&
!command_line.HasSwitch(switches::kUseGL)) {
gl::SetSoftwareGLCommandLineSwitches(&command_line);
}
}
command_line.AppendSwitchASCII(switches::kTouchEventFeatureDetection,
switches::kTouchEventFeatureDetectionEnabled);
if (!command_line.HasSwitch(switches::kForceDeviceScaleFactor))
command_line.AppendSwitchASCII(switches::kForceDeviceScaleFactor, "1.0");
if (!command_line.HasSwitch(switches::kAutoplayPolicy)) {
command_line.AppendSwitchASCII(
switches::kAutoplayPolicy,
switches::autoplay::kNoUserGestureRequiredPolicy);
}
if (!command_line.HasSwitch(switches::kStableReleaseMode)) {
command_line.AppendSwitch(switches::kEnableExperimentalWebPlatformFeatures);
command_line.AppendSwitch(switches::kEnableBlinkTestFeatures);
}
// With display compositor pixel dumps, we ensure that we complete all
// stages of compositing before draw. We also can't have checker imaging,
// since it's incompatible with single threaded compositor and display
// compositor pixel dumps.
//
// TODO(crbug.com/894613) Add kRunAllCompositorStagesBeforeDraw back here
// once you figure out why it causes so much web test flakiness.
// command_line.AppendSwitch(switches::kRunAllCompositorStagesBeforeDraw);
command_line.AppendSwitch(cc::switches::kDisableCheckerImaging);
command_line.AppendSwitch(switches::kMuteAudio);
command_line.AppendSwitch(switches::kEnablePreciseMemoryInfo);
command_line.AppendSwitchASCII(network::switches::kHostResolverRules,
"MAP nonexistent.*.test ^NOTFOUND,"
"MAP web-platform.test:443 127.0.0.1:8444,"
"MAP not-web-platform.test:443 127.0.0.1:8444,"
"MAP devtools.test:443 127.0.0.1:8443,"
"MAP *.test. 127.0.0.1,"
"MAP *.test 127.0.0.1");
// These must be kept in sync with
// //third_party/blink/web_tests/external/wpt/config.json.
command_line.AppendSwitchASCII(network::switches::kIpAddressSpaceOverrides,
"127.0.0.1:8082=private,"
"127.0.0.1:8093=public,"
"127.0.0.1:8446=private,"
"127.0.0.1:8447=public");
// We want to know determanistically from command line flags if the Gpu
// process will provide gpu raster in its capabilities or not.
//
// If kEnableGpuRasterization is specified, the Gpu process always reports
// that it can gpu raster, and the renderer will use it. Otherwise, we don't
// want to choose at runtime, and we ensure that gpu raster is disabled.
if (!command_line.HasSwitch(switches::kEnableGpuRasterization))
command_line.AppendSwitch(switches::kDisableGpuRasterization);
// If Graphite is not explicitly enabled, disable it. This is to keep using
// Ganesh as renderer for web tests for now until we finish rebaselining all
// images for Graphite renderer.
if (!command_line.HasSwitch(switches::kEnableSkiaGraphite)) {
command_line.AppendSwitch(switches::kDisableSkiaGraphite);
}
// If the virtual test suite didn't specify a display color space, then
// force sRGB.
if (!command_line.HasSwitch(switches::kForceDisplayColorProfile))
command_line.AppendSwitchASCII(switches::kForceDisplayColorProfile, "srgb");
// We want stable/baseline results when running web tests.
command_line.AppendSwitch(switches::kDisableSkiaRuntimeOpts);
command_line.AppendSwitch(switches::kDisallowNonExactResourceReuse);
// Always run with fake media devices.
command_line.AppendSwitch(switches::kUseFakeUIForMediaStream);
// The check here ensures that a test's custom value for the switch is not
// overwritten by the default one.
if (!command_line.HasSwitch(switches::kUseFakeDeviceForMediaStream))
command_line.AppendSwitch(switches::kUseFakeDeviceForMediaStream);
// Always run with fake FedCM UI.
command_line.AppendSwitch(switches::kUseFakeUIForFedCM);
// Always run with fake digital identity credential UI.
command_line.AppendSwitch(switches::kUseFakeUIForDigitalIdentity);
// Enable the deprecated WebAuthn Mojo Testing API.
command_line.AppendSwitch(switches::kEnableWebAuthDeprecatedMojoTestingApi);
// Always disable the unsandbox GPU process for DX12 Info collection to avoid
// interference. This GPU process is launched 120 seconds after chrome starts.
command_line.AppendSwitch(switches::kDisableGpuProcessForDX12InfoCollection);
// Disable the backgrounding of renderers to make running tests faster.
command_line.AppendSwitch(switches::kDisableRendererBackgrounding);
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS)
content::WebTestBrowserPlatformInitialize();
#endif
RenderWidgetHostImpl::DisableResizeAckCheckForTesting();
}
void WebTestBrowserMainRunner::RunBrowserMain(
content::MainFunctionParams parameters) {
std::unique_ptr<content::BrowserMainRunner> main_runner =
content::BrowserMainRunner::Create();
int initialize_exit_code = main_runner->Initialize(std::move(parameters));
DCHECK_LT(initialize_exit_code, 0)
<< "BrowserMainRunner::Initialize failed in WebTestBrowserMainRunner";
RunTests(main_runner.get());
// Shell::Shutdown() will cause the |main_runner| loop to quit.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&Shell::Shutdown));
main_runner->Run();
main_runner->Shutdown();
}
} // namespace content