From 3fdb2b081fb0823056e6ac1d759330ccba19404b Mon Sep 17 00:00:00 2001 From: Andrew Kaster Date: Tue, 5 Nov 2024 16:45:43 -0700 Subject: [PATCH] Ladybird: Add a test fixture for the HTTP echo server --- Ladybird/Headless/Application.cpp | 3 ++ Ladybird/Headless/Application.h | 1 + Ladybird/Headless/Fixture.cpp | 63 +++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/Ladybird/Headless/Application.cpp b/Ladybird/Headless/Application.cpp index 767ee5c1643..d3e66a97917 100644 --- a/Ladybird/Headless/Application.cpp +++ b/Ladybird/Headless/Application.cpp @@ -18,6 +18,8 @@ namespace Ladybird { Application::Application(Badge, Main::Arguments&) : resources_folder(s_ladybird_resource_root) , test_concurrency(Core::System::hardware_concurrency()) + , python_executable_path("python3") + { } @@ -33,6 +35,7 @@ void Application::create_platform_arguments(Core::ArgsParser& args_parser) args_parser.add_option(dump_layout_tree, "Dump layout tree and exit", "dump-layout-tree", 'd'); args_parser.add_option(dump_text, "Dump text and exit", "dump-text", 'T'); args_parser.add_option(test_concurrency, "Maximum number of tests to run at once", "test-concurrency", 'j', "jobs"); + args_parser.add_option(python_executable_path, "Path to python3", "python-executable", 'P', "path"); args_parser.add_option(test_root_path, "Run tests in path", "run-tests", 'R', "test-root-path"); args_parser.add_option(test_glob, "Only run tests matching the given glob", "filter", 'f', "glob"); args_parser.add_option(test_dry_run, "List the tests that would be run, without running them", "dry-run"); diff --git a/Ladybird/Headless/Application.h b/Ladybird/Headless/Application.h index 4126d130154..58d10255f51 100644 --- a/Ladybird/Headless/Application.h +++ b/Ladybird/Headless/Application.h @@ -59,6 +59,7 @@ public: bool dump_gc_graph { false }; bool is_layout_test_mode { false }; size_t test_concurrency { 1 }; + ByteString python_executable_path; ByteString test_root_path; ByteString test_glob; bool test_dry_run { false }; diff --git a/Ladybird/Headless/Fixture.cpp b/Ladybird/Headless/Fixture.cpp index 6ac31230f77..73e760ad915 100644 --- a/Ladybird/Headless/Fixture.cpp +++ b/Ladybird/Headless/Fixture.cpp @@ -4,10 +4,16 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include +#include +#include namespace Ladybird { +static ByteString s_fixtures_path; + // Key function for Fixture Fixture::~Fixture() = default; @@ -26,8 +32,65 @@ Vector>& Fixture::all() return fixtures; } +class HttpEchoServerFixture final : public Fixture { +public: + virtual ErrorOr setup() 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(); } + +private: + ByteString m_script_path { "http-test-server.py" }; + Optional m_process; +}; + +ErrorOr HttpEchoServerFixture::setup() +{ + auto script_path = LexicalPath::join(s_fixtures_path, m_script_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 arguments = Vector { script_path.string(), "start", "--directory", Ladybird::Application::the().test_root_path }; + auto 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 }, + } + }; + + m_process = TRY(Core::Process::spawn(process_options)); + + return {}; +} + +void HttpEchoServerFixture::teardown_impl() +{ + VERIFY(m_process.has_value()); + + 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; + } + + MUST(m_process->wait_for_termination()); + + m_process = {}; +} + void Fixture::initialize_fixtures() { + s_fixtures_path = LexicalPath::join(Ladybird::Application::the().test_root_path, "Fixtures"sv).string(); + + auto& registry = all(); + registry.append(make()); } }