mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-29 14:14:45 +03:00
test-test262: Port to Core::Stream and use TRY more
The only complication here is that Core::Stream::File is not RefCounted meaning we have to use OwnPtr instead of RefPtr. Unfortunately we cannot propagate errors as some errors must be caught and dealt with as the runner can do anything (like stop at any moment or close pipes).
This commit is contained in:
parent
49e3b387ac
commit
302593ded5
Notes:
sideshowbarker
2024-07-17 18:08:55 +09:00
Author: https://github.com/davidot Commit: https://github.com/SerenityOS/serenity/commit/302593ded5 Pull-request: https://github.com/SerenityOS/serenity/pull/15232 Reviewed-by: https://github.com/ADKaster Reviewed-by: https://github.com/Hendiadyoin1 Reviewed-by: https://github.com/sin-ack Reviewed-by: https://github.com/timschumi
@ -15,6 +15,8 @@
|
|||||||
#include <LibCore/ArgsParser.h>
|
#include <LibCore/ArgsParser.h>
|
||||||
#include <LibCore/File.h>
|
#include <LibCore/File.h>
|
||||||
#include <LibCore/Process.h>
|
#include <LibCore/Process.h>
|
||||||
|
#include <LibCore/Stream.h>
|
||||||
|
#include <LibCore/System.h>
|
||||||
#include <LibMain/Main.h>
|
#include <LibMain/Main.h>
|
||||||
#include <LibTest/TestRunnerUtil.h>
|
#include <LibTest/TestRunnerUtil.h>
|
||||||
#include <spawn.h>
|
#include <spawn.h>
|
||||||
@ -111,47 +113,34 @@ static constexpr StringView total_test_emoji = "🧪"sv;
|
|||||||
|
|
||||||
class Test262RunnerHandler {
|
class Test262RunnerHandler {
|
||||||
public:
|
public:
|
||||||
static OwnPtr<Test262RunnerHandler> create(char const* const command[])
|
static ErrorOr<OwnPtr<Test262RunnerHandler>> create(StringView command, char const* const arguments[])
|
||||||
{
|
{
|
||||||
int write_pipe_fds[2];
|
auto write_pipe_fds = TRY(Core::System::pipe2(O_CLOEXEC));
|
||||||
int read_pipe_fds[2];
|
auto read_pipe_fds = TRY(Core::System::pipe2(O_CLOEXEC));
|
||||||
if (pipe2(write_pipe_fds, O_CLOEXEC) < 0) {
|
|
||||||
perror("pipe2");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pipe2(read_pipe_fds, O_CLOEXEC) < 0) {
|
|
||||||
perror("pipe2");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
posix_spawn_file_actions_t file_actions;
|
posix_spawn_file_actions_t file_actions;
|
||||||
posix_spawn_file_actions_init(&file_actions);
|
posix_spawn_file_actions_init(&file_actions);
|
||||||
posix_spawn_file_actions_adddup2(&file_actions, write_pipe_fds[0], STDIN_FILENO);
|
posix_spawn_file_actions_adddup2(&file_actions, write_pipe_fds[0], STDIN_FILENO);
|
||||||
posix_spawn_file_actions_adddup2(&file_actions, read_pipe_fds[1], STDOUT_FILENO);
|
posix_spawn_file_actions_adddup2(&file_actions, read_pipe_fds[1], STDOUT_FILENO);
|
||||||
|
|
||||||
pid_t pid {};
|
auto pid = TRY(Core::System::posix_spawnp(command, &file_actions, nullptr, const_cast<char**>(arguments), environ));
|
||||||
|
|
||||||
if (posix_spawnp(&pid, command[0], &file_actions, nullptr, const_cast<char**>(command), environ) < 0) {
|
|
||||||
perror("posix_spawnp");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
posix_spawn_file_actions_destroy(&file_actions);
|
posix_spawn_file_actions_destroy(&file_actions);
|
||||||
|
ArmedScopeGuard runner_kill { [&pid] { kill(pid, SIGKILL); } };
|
||||||
|
|
||||||
close(write_pipe_fds[0]);
|
TRY(Core::System::close(write_pipe_fds[0]));
|
||||||
close(read_pipe_fds[1]);
|
TRY(Core::System::close(read_pipe_fds[1]));
|
||||||
|
|
||||||
auto infile = Core::File::construct();
|
auto infile = TRY(Core::Stream::File::adopt_fd(read_pipe_fds[0], Core::Stream::OpenMode::Read));
|
||||||
infile->open(read_pipe_fds[0], Core::OpenMode::ReadOnly, Core::File::ShouldCloseFileDescriptor::Yes);
|
|
||||||
|
|
||||||
auto outfile = Core::File::construct();
|
auto outfile = TRY(Core::Stream::File::adopt_fd(write_pipe_fds[1], Core::Stream::OpenMode::Write));
|
||||||
outfile->open(write_pipe_fds[1], Core::OpenMode::WriteOnly, Core::File::ShouldCloseFileDescriptor::Yes);
|
|
||||||
|
runner_kill.disarm();
|
||||||
|
|
||||||
return make<Test262RunnerHandler>(pid, move(infile), move(outfile));
|
return make<Test262RunnerHandler>(pid, move(infile), move(outfile));
|
||||||
}
|
}
|
||||||
|
|
||||||
Test262RunnerHandler(pid_t pid, NonnullRefPtr<Core::File> in_file, NonnullRefPtr<Core::File> out_file)
|
Test262RunnerHandler(pid_t pid, NonnullOwnPtr<Core::Stream::File> in_file, NonnullOwnPtr<Core::Stream::File> out_file)
|
||||||
: m_pid(pid)
|
: m_pid(pid)
|
||||||
, m_input(move(in_file))
|
, m_input(move(in_file))
|
||||||
, m_output(move(out_file))
|
, m_output(move(out_file))
|
||||||
@ -162,17 +151,17 @@ public:
|
|||||||
{
|
{
|
||||||
// It's possible the process dies before we can write all the tests
|
// It's possible the process dies before we can write all the tests
|
||||||
// to the stdin. So make sure that we don't crash but just stop writing.
|
// to the stdin. So make sure that we don't crash but just stop writing.
|
||||||
struct sigaction action_handler { };
|
struct sigaction action_handler {
|
||||||
|
.sa_handler = SIG_IGN, .sa_mask = {}, .sa_flags = 0,
|
||||||
|
};
|
||||||
struct sigaction old_action_handler;
|
struct sigaction old_action_handler;
|
||||||
action_handler.sa_flags = 0;
|
|
||||||
action_handler.sa_handler = SIG_IGN;
|
|
||||||
if (sigaction(SIGPIPE, &action_handler, &old_action_handler) < 0) {
|
if (sigaction(SIGPIPE, &action_handler, &old_action_handler) < 0) {
|
||||||
perror("sigaction");
|
perror("sigaction");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String const& line : lines) {
|
for (String const& line : lines) {
|
||||||
if (!m_output->write(String::formatted("{}\n", line)))
|
if (!m_output->write_or_error(String::formatted("{}\n", line).bytes()))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +177,12 @@ public:
|
|||||||
|
|
||||||
String read_all()
|
String read_all()
|
||||||
{
|
{
|
||||||
return String(m_input->read_all().bytes(), Chomp);
|
auto all_output_or_error = m_input->read_all();
|
||||||
|
if (all_output_or_error.is_error()) {
|
||||||
|
warnln("Got error: {} while reading runner output", all_output_or_error.error());
|
||||||
|
return ""sv;
|
||||||
|
}
|
||||||
|
return String(all_output_or_error.value().bytes(), Chomp);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ProcessResult {
|
enum class ProcessResult {
|
||||||
@ -199,23 +193,24 @@ public:
|
|||||||
Unknown,
|
Unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
ProcessResult status()
|
ErrorOr<ProcessResult> status()
|
||||||
{
|
{
|
||||||
if (m_pid == -1)
|
if (m_pid == -1)
|
||||||
return ProcessResult::Unknown;
|
return ProcessResult::Unknown;
|
||||||
|
|
||||||
int wstatus { 0 };
|
m_output->close();
|
||||||
int rc = waitpid(m_pid, &wstatus, WNOHANG);
|
|
||||||
if (rc == 0) {
|
auto wait_result = TRY(Core::System::waitpid(m_pid, WNOHANG));
|
||||||
|
if (wait_result.pid == 0) {
|
||||||
// Attempt to kill it, since it has not finished yet somehow
|
// Attempt to kill it, since it has not finished yet somehow
|
||||||
return ProcessResult::Running;
|
return ProcessResult::Running;
|
||||||
}
|
}
|
||||||
m_pid = -1;
|
m_pid = -1;
|
||||||
|
|
||||||
if (WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGALRM)
|
if (WIFSIGNALED(wait_result.status) && WTERMSIG(wait_result.status) == SIGALRM)
|
||||||
return ProcessResult::FailedFromTimeout;
|
return ProcessResult::FailedFromTimeout;
|
||||||
|
|
||||||
if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0)
|
if (WIFEXITED(wait_result.status) && WEXITSTATUS(wait_result.status) == 0)
|
||||||
return ProcessResult::DoneWithZeroExitCode;
|
return ProcessResult::DoneWithZeroExitCode;
|
||||||
|
|
||||||
return ProcessResult::Failed;
|
return ProcessResult::Failed;
|
||||||
@ -223,11 +218,11 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
pid_t m_pid;
|
pid_t m_pid;
|
||||||
NonnullRefPtr<Core::File> m_input;
|
NonnullOwnPtr<Core::Stream::File> m_input;
|
||||||
NonnullRefPtr<Core::File> m_output;
|
NonnullOwnPtr<Core::Stream::File> m_output;
|
||||||
};
|
};
|
||||||
|
|
||||||
static HashMap<size_t, TestResult> run_test_files(Span<String> files, size_t offset, char const* const command[])
|
static HashMap<size_t, TestResult> run_test_files(Span<String> files, size_t offset, StringView command, char const* const arguments[])
|
||||||
{
|
{
|
||||||
HashMap<size_t, TestResult> results {};
|
HashMap<size_t, TestResult> results {};
|
||||||
results.ensure_capacity(files.size());
|
results.ensure_capacity(files.size());
|
||||||
@ -239,11 +234,12 @@ static HashMap<size_t, TestResult> run_test_files(Span<String> files, size_t off
|
|||||||
};
|
};
|
||||||
|
|
||||||
while (test_index < files.size()) {
|
while (test_index < files.size()) {
|
||||||
auto runner_process = Test262RunnerHandler::create(command);
|
auto runner_process_or_error = Test262RunnerHandler::create(command, arguments);
|
||||||
if (!runner_process) {
|
if (runner_process_or_error.is_error()) {
|
||||||
fail_all_after();
|
fail_all_after();
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
auto& runner_process = runner_process_or_error.value();
|
||||||
|
|
||||||
if (!runner_process->write_lines(files.slice(test_index))) {
|
if (!runner_process->write_lines(files.slice(test_index))) {
|
||||||
fail_all_after();
|
fail_all_after();
|
||||||
@ -251,9 +247,12 @@ static HashMap<size_t, TestResult> run_test_files(Span<String> files, size_t off
|
|||||||
}
|
}
|
||||||
|
|
||||||
String output = runner_process->read_all();
|
String output = runner_process->read_all();
|
||||||
auto status = runner_process->status();
|
auto status_or_error = runner_process->status();
|
||||||
VERIFY(status != Test262RunnerHandler::ProcessResult::Running);
|
bool failed = false;
|
||||||
bool failed = status != Test262RunnerHandler::ProcessResult::DoneWithZeroExitCode;
|
if (!status_or_error.is_error()) {
|
||||||
|
VERIFY(status_or_error.value() != Test262RunnerHandler::ProcessResult::Running);
|
||||||
|
failed = status_or_error.value() != Test262RunnerHandler::ProcessResult::DoneWithZeroExitCode;
|
||||||
|
}
|
||||||
|
|
||||||
for (StringView line : output.split_view('\n')) {
|
for (StringView line : output.split_view('\n')) {
|
||||||
if (!line.starts_with("RESULT "sv))
|
if (!line.starts_with("RESULT "sv))
|
||||||
@ -286,7 +285,7 @@ static HashMap<size_t, TestResult> run_test_files(Span<String> files, size_t off
|
|||||||
|
|
||||||
if (failed) {
|
if (failed) {
|
||||||
TestResult result = TestResult::ProcessError;
|
TestResult result = TestResult::ProcessError;
|
||||||
if (status == Test262RunnerHandler::ProcessResult::FailedFromTimeout) {
|
if (!status_or_error.is_error() && status_or_error.value() == Test262RunnerHandler::ProcessResult::FailedFromTimeout) {
|
||||||
result = TestResult::TimeoutError;
|
result = TestResult::TimeoutError;
|
||||||
}
|
}
|
||||||
// assume the last test failed, if by SIGALRM signal it's a timeout
|
// assume the last test failed, if by SIGALRM signal it's a timeout
|
||||||
@ -379,7 +378,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||||||
while (index < paths.size()) {
|
while (index < paths.size()) {
|
||||||
print_progress();
|
print_progress();
|
||||||
auto this_batch_size = min(batch_size, paths.size() - index);
|
auto this_batch_size = min(batch_size, paths.size() - index);
|
||||||
auto batch_results = run_test_files(paths.span().slice(index, this_batch_size), index, raw_args.data());
|
auto batch_results = run_test_files(paths.span().slice(index, this_batch_size), index, args[0], raw_args.data());
|
||||||
|
|
||||||
results.ensure_capacity(results.size() + batch_results.size());
|
results.ensure_capacity(results.size() + batch_results.size());
|
||||||
for (auto& [key, value] : batch_results) {
|
for (auto& [key, value] : batch_results) {
|
||||||
@ -412,7 +411,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||||||
void write_per_file(HashMap<size_t, TestResult> const& result_map, Vector<String> const& paths, StringView per_file_name, double time_taken_in_ms)
|
void write_per_file(HashMap<size_t, TestResult> const& result_map, Vector<String> const& paths, StringView per_file_name, double time_taken_in_ms)
|
||||||
{
|
{
|
||||||
|
|
||||||
auto file_or_error = Core::File::open(per_file_name, Core::OpenMode::WriteOnly);
|
auto file_or_error = Core::Stream::File::open(per_file_name, Core::Stream::OpenMode::Write);
|
||||||
if (file_or_error.is_error()) {
|
if (file_or_error.is_error()) {
|
||||||
warnln("Failed to open per file for writing at {}: {}", per_file_name, file_or_error.error().string_literal());
|
warnln("Failed to open per file for writing at {}: {}", per_file_name, file_or_error.error().string_literal());
|
||||||
return;
|
return;
|
||||||
@ -428,7 +427,7 @@ void write_per_file(HashMap<size_t, TestResult> const& result_map, Vector<String
|
|||||||
complete_results.set("duration", time_taken_in_ms / 1000.);
|
complete_results.set("duration", time_taken_in_ms / 1000.);
|
||||||
complete_results.set("results", result_object);
|
complete_results.set("results", result_object);
|
||||||
|
|
||||||
if (!file->write(complete_results.to_string()))
|
if (!file->write_or_error(complete_results.to_string().bytes()))
|
||||||
warnln("Failed to write per-file");
|
warnln("Failed to write per-file");
|
||||||
file->close();
|
file->close();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user