diff --git a/Libraries/LibWebView/Application.h b/Libraries/LibWebView/Application.h index 8afdb570a61..46a473cfe8e 100644 --- a/Libraries/LibWebView/Application.h +++ b/Libraries/LibWebView/Application.h @@ -34,7 +34,7 @@ public: static Application& the() { return *s_the; } static ChromeOptions const& chrome_options() { return the().m_chrome_options; } - static WebContentOptions const& web_content_options() { return the().m_web_content_options; } + static WebContentOptions& web_content_options() { return the().m_web_content_options; } static Requests::RequestClient& request_server_client() { return *the().m_request_server_client; } static ImageDecoderClient::Client& image_decoder_client() { return *the().m_image_decoder_client; } diff --git a/Libraries/LibWebView/Options.h b/Libraries/LibWebView/Options.h index d4b37b5a284..75fc8b4447d 100644 --- a/Libraries/LibWebView/Options.h +++ b/Libraries/LibWebView/Options.h @@ -127,6 +127,7 @@ struct WebContentOptions { ForceFontconfig force_fontconfig { ForceFontconfig::No }; EnableAutoplay enable_autoplay { EnableAutoplay::No }; CollectGarbageOnEveryAllocation collect_garbage_on_every_allocation { CollectGarbageOnEveryAllocation::No }; + Optional echo_server_port {}; }; } diff --git a/UI/Headless/Application.cpp b/UI/Headless/Application.cpp index 14d060058ee..35f5ab0db6e 100644 --- a/UI/Headless/Application.cpp +++ b/UI/Headless/Application.cpp @@ -93,10 +93,11 @@ ErrorOr Application::launch_test_fixtures() // FIXME: Add option to only run specific fixtures from command line by name // And an option to not run any fixtures at all - for (auto& fixture : Fixture::all()) { - if (auto result = fixture->setup(); result.is_error()) + for (auto const& fixture : Fixture::all()) { + if (auto result = fixture->setup(web_content_options()); result.is_error()) return result; } + return {}; } diff --git a/UI/Headless/Fixture.cpp b/UI/Headless/Fixture.cpp index 5caef844c22..8e4d97e106b 100644 --- a/UI/Headless/Fixture.cpp +++ b/UI/Headless/Fixture.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -19,7 +20,7 @@ Fixture::~Fixture() = default; Optional Fixture::lookup(StringView name) { - for (auto& fixture : all()) { + for (auto const& fixture : all()) { if (fixture->name() == name) return *fixture; } @@ -34,7 +35,7 @@ Vector>& Fixture::all() class HttpEchoServerFixture final : public Fixture { public: - virtual ErrorOr setup() override; + virtual ErrorOr setup(WebView::WebContentOptions&) override; virtual void teardown_impl() override; virtual StringView name() const override { return "HttpEchoServer"sv; } virtual bool is_running() const override { return m_process.has_value(); } @@ -44,26 +45,41 @@ private: Optional m_process; }; -ErrorOr HttpEchoServerFixture::setup() +ErrorOr HttpEchoServerFixture::setup(WebView::WebContentOptions& web_content_options) { - auto script_path = LexicalPath::join(s_fixtures_path, m_script_path); + auto const script_path = LexicalPath::join(s_fixtures_path, m_script_path); + auto const arguments = Vector { script_path.string(), "--directory", Ladybird::Application::the().test_root_path }; // FIXME: Pick a more reasonable log path that is more observable - auto log_path = LexicalPath::join(Core::StandardPaths::tempfile_directory(), "http-test-server.log"sv).string(); + auto const log_path = LexicalPath::join(Core::StandardPaths::tempfile_directory(), "http-test-server.log"sv).string(); - auto arguments = Vector { script_path.string(), "start", "--directory", Ladybird::Application::the().test_root_path }; - auto process_options = Core::ProcessSpawnOptions { + auto stdout_fds = TRY(Core::System::pipe2(0)); + + auto const process_options = Core::ProcessSpawnOptions { .executable = Ladybird::Application::the().python_executable_path, .search_for_executable_in_path = true, .arguments = arguments, .file_actions = { - Core::FileAction::OpenFile { ByteString::formatted("{}.stdout", log_path), Core::File::OpenMode::Write, STDOUT_FILENO }, Core::FileAction::OpenFile { ByteString::formatted("{}.stderr", log_path), Core::File::OpenMode::Write, STDERR_FILENO }, - } + Core::FileAction::DupFd { stdout_fds[1], STDOUT_FILENO } } }; m_process = TRY(Core::Process::spawn(process_options)); + TRY(Core::System::close(stdout_fds[1])); + + auto const stdout_file = MUST(Core::File::adopt_fd(stdout_fds[0], Core::File::OpenMode::Read)); + + auto buffer = MUST(ByteBuffer::create_uninitialized(5)); + TRY(stdout_file->read_some(buffer)); + + auto const raw_output = ByteString { buffer, AK::ShouldChomp::NoChomp }; + + if (auto const maybe_port = raw_output.to_number(); maybe_port.has_value()) + web_content_options.echo_server_port = maybe_port.value(); + else + warnln("Failed to read echo server port from buffer: '{}'", raw_output); + return {}; } @@ -73,14 +89,17 @@ void HttpEchoServerFixture::teardown_impl() auto script_path = LexicalPath::join(s_fixtures_path, m_script_path); - auto ret = Core::System::kill(m_process->pid(), SIGINT); - if (ret.is_error() && ret.error().code() != ESRCH) { - warnln("Failed to kill http-test-server.py: {}", ret.error()); - m_process = {}; - return; + if (auto kill_or_error = Core::System::kill(m_process->pid(), SIGINT); kill_or_error.is_error()) { + if (kill_or_error.error().code() != ESRCH) { + warnln("Failed to kill HTTP echo server, error: {}", kill_or_error.error()); + m_process = {}; + return; + } } - MUST(m_process->wait_for_termination()); + if (auto termination_or_error = m_process->wait_for_termination(); termination_or_error.is_error()) { + warnln("Failed to terminate HTTP echo server, error: {}", termination_or_error.error()); + } m_process = {}; } diff --git a/UI/Headless/Fixture.h b/UI/Headless/Fixture.h index 79c29b322fc..ea461ace662 100644 --- a/UI/Headless/Fixture.h +++ b/UI/Headless/Fixture.h @@ -18,7 +18,7 @@ class Fixture { public: virtual ~Fixture(); - virtual ErrorOr setup() = 0; + virtual ErrorOr setup(WebView::WebContentOptions&) = 0; void teardown() {