2021-05-18 14:15:12 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
|
|
|
|
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
|
|
|
|
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
*/
|
|
|
|
|
2021-10-26 19:06:35 +00:00
|
|
|
#include <LibCore/ArgsParser.h>
|
2023-02-08 20:08:01 +00:00
|
|
|
#include <LibCore/DeprecatedFile.h>
|
2021-05-18 14:15:12 +00:00
|
|
|
#include <LibTest/JavaScriptTestRunner.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2021-06-27 18:57:35 +00:00
|
|
|
namespace Test {
|
|
|
|
|
|
|
|
TestRunner* ::Test::TestRunner::s_the = nullptr;
|
|
|
|
|
|
|
|
namespace JS {
|
2021-05-18 14:15:12 +00:00
|
|
|
|
|
|
|
RefPtr<::JS::VM> g_vm;
|
|
|
|
bool g_collect_on_every_allocation = false;
|
2021-06-12 03:49:25 +00:00
|
|
|
bool g_run_bytecode = false;
|
2022-12-04 18:02:33 +00:00
|
|
|
DeprecatedString g_currently_running_test;
|
|
|
|
HashMap<DeprecatedString, FunctionWithLength> s_exposed_global_functions;
|
2021-05-18 14:15:12 +00:00
|
|
|
Function<void()> g_main_hook;
|
2021-10-14 15:12:53 +00:00
|
|
|
Function<NonnullOwnPtr<JS::Interpreter>()> g_create_interpreter_hook;
|
2022-12-04 18:02:33 +00:00
|
|
|
HashMap<bool*, Tuple<DeprecatedString, DeprecatedString, char>> g_extra_args;
|
|
|
|
IntermediateRunFileResult (*g_run_file)(DeprecatedString const&, JS::Interpreter&, JS::ExecutionContext&) = nullptr;
|
|
|
|
DeprecatedString g_test_root;
|
2021-05-18 14:15:12 +00:00
|
|
|
int g_test_argc;
|
|
|
|
char** g_test_argv;
|
|
|
|
|
2021-06-27 18:57:35 +00:00
|
|
|
} // namespace JS
|
|
|
|
} // namespace Test
|
2021-05-18 14:15:12 +00:00
|
|
|
|
|
|
|
using namespace Test::JS;
|
|
|
|
|
|
|
|
static StringView g_program_name { "test-js"sv };
|
|
|
|
|
|
|
|
static void handle_sigabrt(int)
|
|
|
|
{
|
|
|
|
dbgln("{}: SIGABRT received, cleaning up.", g_program_name);
|
2021-06-27 18:57:35 +00:00
|
|
|
Test::cleanup();
|
2021-05-26 18:33:58 +00:00
|
|
|
struct sigaction act;
|
|
|
|
memset(&act, 0, sizeof(act));
|
|
|
|
act.sa_flags = SA_NOCLDWAIT;
|
|
|
|
act.sa_handler = SIG_DFL;
|
|
|
|
int rc = sigaction(SIGABRT, &act, nullptr);
|
|
|
|
if (rc < 0) {
|
|
|
|
perror("sigaction");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
abort();
|
2021-05-18 14:15:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char** argv)
|
|
|
|
{
|
2023-02-21 11:44:41 +00:00
|
|
|
Vector<StringView> arguments;
|
|
|
|
arguments.ensure_capacity(argc);
|
|
|
|
for (auto i = 0; i < argc; ++i)
|
|
|
|
arguments.append({ argv[i], strlen(argv[i]) });
|
|
|
|
|
2021-05-18 14:15:12 +00:00
|
|
|
g_test_argc = argc;
|
|
|
|
g_test_argv = argv;
|
2021-06-29 14:46:16 +00:00
|
|
|
auto program_name = LexicalPath::basename(argv[0]);
|
2021-05-18 14:15:12 +00:00
|
|
|
g_program_name = program_name;
|
|
|
|
|
|
|
|
struct sigaction act;
|
|
|
|
memset(&act, 0, sizeof(act));
|
|
|
|
act.sa_flags = SA_NOCLDWAIT;
|
|
|
|
act.sa_handler = handle_sigabrt;
|
|
|
|
int rc = sigaction(SIGABRT, &act, nullptr);
|
|
|
|
if (rc < 0) {
|
|
|
|
perror("sigaction");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef SIGINFO
|
|
|
|
signal(SIGINFO, [](int) {
|
|
|
|
static char buffer[4096];
|
2021-06-27 18:57:35 +00:00
|
|
|
auto& counts = ::Test::TestRunner::the()->counts();
|
2021-05-18 14:15:12 +00:00
|
|
|
int len = snprintf(buffer, sizeof(buffer), "Pass: %d, Fail: %d, Skip: %d\nCurrent test: %s\n", counts.tests_passed, counts.tests_failed, counts.tests_skipped, g_currently_running_test.characters());
|
|
|
|
write(STDOUT_FILENO, buffer, len);
|
|
|
|
});
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bool print_times = false;
|
|
|
|
bool print_progress =
|
2022-10-09 21:23:23 +00:00
|
|
|
#ifdef AK_OS_SERENITY
|
2021-05-18 14:15:12 +00:00
|
|
|
true; // Use OSC 9 to print progress
|
|
|
|
#else
|
|
|
|
false;
|
|
|
|
#endif
|
2021-06-04 07:32:43 +00:00
|
|
|
bool print_json = false;
|
2022-03-08 03:39:41 +00:00
|
|
|
bool per_file = false;
|
2023-02-28 20:41:43 +00:00
|
|
|
StringView specified_test_root;
|
2022-12-04 18:02:33 +00:00
|
|
|
DeprecatedString common_path;
|
|
|
|
DeprecatedString test_glob;
|
2021-05-18 14:15:12 +00:00
|
|
|
|
|
|
|
Core::ArgsParser args_parser;
|
|
|
|
args_parser.add_option(print_times, "Show duration of each test", "show-time", 't');
|
|
|
|
args_parser.add_option(Core::ArgsParser::Option {
|
2022-07-12 20:13:38 +00:00
|
|
|
.argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
|
2021-05-18 14:15:12 +00:00
|
|
|
.help_string = "Show progress with OSC 9 (true, false)",
|
|
|
|
.long_name = "show-progress",
|
|
|
|
.short_name = 'p',
|
2023-02-21 11:44:41 +00:00
|
|
|
.accept_value = [&](StringView str) {
|
2021-07-04 09:08:46 +00:00
|
|
|
if ("true"sv == str)
|
2021-05-18 14:15:12 +00:00
|
|
|
print_progress = true;
|
2021-07-04 09:08:46 +00:00
|
|
|
else if ("false"sv == str)
|
2021-05-18 14:15:12 +00:00
|
|
|
print_progress = false;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
});
|
2021-06-04 07:32:43 +00:00
|
|
|
args_parser.add_option(print_json, "Show results as JSON", "json", 'j');
|
2022-03-08 03:39:41 +00:00
|
|
|
args_parser.add_option(per_file, "Show detailed per-file results as JSON (implies -j)", "per-file", 0);
|
2021-05-18 14:15:12 +00:00
|
|
|
args_parser.add_option(g_collect_on_every_allocation, "Collect garbage after every allocation", "collect-often", 'g');
|
2021-06-12 03:49:25 +00:00
|
|
|
args_parser.add_option(g_run_bytecode, "Use the bytecode interpreter", "run-bytecode", 'b');
|
2021-10-24 11:34:46 +00:00
|
|
|
args_parser.add_option(JS::Bytecode::g_dump_bytecode, "Dump the bytecode", "dump-bytecode", 'd');
|
2021-06-27 18:57:35 +00:00
|
|
|
args_parser.add_option(test_glob, "Only run tests matching the given glob", "filter", 'f', "glob");
|
2021-05-30 06:05:44 +00:00
|
|
|
for (auto& entry : g_extra_args)
|
|
|
|
args_parser.add_option(*entry.key, entry.value.get<0>().characters(), entry.value.get<1>().characters(), entry.value.get<2>());
|
2021-05-18 14:15:12 +00:00
|
|
|
args_parser.add_positional_argument(specified_test_root, "Tests root directory", "path", Core::ArgsParser::Required::No);
|
|
|
|
args_parser.add_positional_argument(common_path, "Path to tests-common.js", "common-path", Core::ArgsParser::Required::No);
|
2023-02-21 11:44:41 +00:00
|
|
|
args_parser.parse(arguments);
|
2021-05-18 14:15:12 +00:00
|
|
|
|
2022-03-08 03:39:41 +00:00
|
|
|
if (per_file)
|
|
|
|
print_json = true;
|
|
|
|
|
2022-12-04 18:02:33 +00:00
|
|
|
test_glob = DeprecatedString::formatted("*{}*", test_glob);
|
2021-05-18 14:15:12 +00:00
|
|
|
|
|
|
|
if (getenv("DISABLE_DBG_OUTPUT")) {
|
|
|
|
AK::set_debug_enabled(false);
|
|
|
|
}
|
|
|
|
|
2021-10-24 11:34:46 +00:00
|
|
|
if (JS::Bytecode::g_dump_bytecode && !g_run_bytecode) {
|
2021-06-12 03:49:25 +00:00
|
|
|
warnln("--dump-bytecode can only be used when --run-bytecode is specified.");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2022-12-04 18:02:33 +00:00
|
|
|
DeprecatedString test_root;
|
2021-05-18 14:15:12 +00:00
|
|
|
|
2023-02-28 20:41:43 +00:00
|
|
|
if (!specified_test_root.is_empty()) {
|
2022-12-04 18:02:33 +00:00
|
|
|
test_root = DeprecatedString { specified_test_root };
|
2021-05-18 14:15:12 +00:00
|
|
|
} else {
|
2022-10-09 21:23:23 +00:00
|
|
|
#ifdef AK_OS_SERENITY
|
2022-12-04 18:02:33 +00:00
|
|
|
test_root = LexicalPath::join("/home/anon/Tests"sv, DeprecatedString::formatted("{}-tests", program_name.split_view('-').last())).string();
|
2021-05-18 14:15:12 +00:00
|
|
|
#else
|
|
|
|
char* serenity_source_dir = getenv("SERENITY_SOURCE_DIR");
|
|
|
|
if (!serenity_source_dir) {
|
|
|
|
warnln("No test root given, {} requires the SERENITY_SOURCE_DIR environment variable to be set", g_program_name);
|
|
|
|
return 1;
|
|
|
|
}
|
2022-12-04 18:02:33 +00:00
|
|
|
test_root = DeprecatedString::formatted("{}/{}", serenity_source_dir, g_test_root_fragment);
|
|
|
|
common_path = DeprecatedString::formatted("{}/Userland/Libraries/LibJS/Tests/test-common.js", serenity_source_dir);
|
2021-05-18 14:15:12 +00:00
|
|
|
#endif
|
|
|
|
}
|
2023-02-08 20:08:01 +00:00
|
|
|
if (!Core::DeprecatedFile::is_directory(test_root)) {
|
2021-05-18 14:15:12 +00:00
|
|
|
warnln("Test root is not a directory: {}", test_root);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (common_path.is_empty()) {
|
2022-10-09 21:23:23 +00:00
|
|
|
#ifdef AK_OS_SERENITY
|
2022-03-20 18:41:37 +00:00
|
|
|
common_path = "/home/anon/Tests/js-tests/test-common.js";
|
2021-05-18 14:15:12 +00:00
|
|
|
#else
|
|
|
|
char* serenity_source_dir = getenv("SERENITY_SOURCE_DIR");
|
|
|
|
if (!serenity_source_dir) {
|
|
|
|
warnln("No test root given, {} requires the SERENITY_SOURCE_DIR environment variable to be set", g_program_name);
|
|
|
|
return 1;
|
|
|
|
}
|
2022-12-04 18:02:33 +00:00
|
|
|
common_path = DeprecatedString::formatted("{}/Userland/Libraries/LibJS/Tests/test-common.js", serenity_source_dir);
|
2021-05-18 14:15:12 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2023-02-08 20:08:01 +00:00
|
|
|
test_root = Core::DeprecatedFile::real_path_for(test_root);
|
|
|
|
common_path = Core::DeprecatedFile::real_path_for(common_path);
|
2021-05-30 06:05:44 +00:00
|
|
|
|
2021-05-18 14:15:12 +00:00
|
|
|
if (chdir(test_root.characters()) < 0) {
|
|
|
|
auto saved_errno = errno;
|
|
|
|
warnln("chdir failed: {}", strerror(saved_errno));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_main_hook)
|
|
|
|
g_main_hook();
|
|
|
|
|
2022-01-18 18:39:36 +00:00
|
|
|
if (!g_vm) {
|
2021-05-18 14:15:12 +00:00
|
|
|
g_vm = JS::VM::create();
|
2022-01-18 18:39:36 +00:00
|
|
|
g_vm->enable_default_host_import_module_dynamically_hook();
|
|
|
|
}
|
2021-05-18 14:15:12 +00:00
|
|
|
|
2022-03-08 03:39:41 +00:00
|
|
|
Test::JS::TestRunner test_runner(test_root, common_path, print_times, print_progress, print_json, per_file);
|
2021-06-27 18:57:35 +00:00
|
|
|
test_runner.run(test_glob);
|
2021-05-18 14:15:12 +00:00
|
|
|
|
|
|
|
g_vm = nullptr;
|
|
|
|
|
|
|
|
return test_runner.counts().tests_failed > 0 ? 1 : 0;
|
|
|
|
}
|