JavaScriptTestRunnerMain.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
  3. * Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
  4. * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <LibCore/ArgsParser.h>
  9. #include <LibFileSystem/FileSystem.h>
  10. #include <LibTest/JavaScriptTestRunner.h>
  11. #include <signal.h>
  12. #include <stdio.h>
  13. namespace Test {
  14. TestRunner* ::Test::TestRunner::s_the = nullptr;
  15. namespace JS {
  16. RefPtr<::JS::VM> g_vm;
  17. bool g_collect_on_every_allocation = false;
  18. bool g_run_bytecode = false;
  19. DeprecatedString g_currently_running_test;
  20. HashMap<DeprecatedString, FunctionWithLength> s_exposed_global_functions;
  21. Function<void()> g_main_hook;
  22. Function<NonnullOwnPtr<JS::Interpreter>()> g_create_interpreter_hook;
  23. HashMap<bool*, Tuple<DeprecatedString, DeprecatedString, char>> g_extra_args;
  24. IntermediateRunFileResult (*g_run_file)(DeprecatedString const&, JS::Interpreter&, JS::ExecutionContext&) = nullptr;
  25. DeprecatedString g_test_root;
  26. int g_test_argc;
  27. char** g_test_argv;
  28. } // namespace JS
  29. } // namespace Test
  30. using namespace Test::JS;
  31. static StringView g_program_name { "test-js"sv };
  32. static void handle_sigabrt(int)
  33. {
  34. dbgln("{}: SIGABRT received, cleaning up.", g_program_name);
  35. Test::cleanup();
  36. struct sigaction act;
  37. memset(&act, 0, sizeof(act));
  38. act.sa_flags = SA_NOCLDWAIT;
  39. act.sa_handler = SIG_DFL;
  40. int rc = sigaction(SIGABRT, &act, nullptr);
  41. if (rc < 0) {
  42. perror("sigaction");
  43. exit(1);
  44. }
  45. abort();
  46. }
  47. int main(int argc, char** argv)
  48. {
  49. Vector<StringView> arguments;
  50. arguments.ensure_capacity(argc);
  51. for (auto i = 0; i < argc; ++i)
  52. arguments.append({ argv[i], strlen(argv[i]) });
  53. g_test_argc = argc;
  54. g_test_argv = argv;
  55. auto program_name = LexicalPath::basename(argv[0]);
  56. g_program_name = program_name;
  57. struct sigaction act;
  58. memset(&act, 0, sizeof(act));
  59. act.sa_flags = SA_NOCLDWAIT;
  60. act.sa_handler = handle_sigabrt;
  61. int rc = sigaction(SIGABRT, &act, nullptr);
  62. if (rc < 0) {
  63. perror("sigaction");
  64. return 1;
  65. }
  66. #ifdef SIGINFO
  67. signal(SIGINFO, [](int) {
  68. static char buffer[4096];
  69. auto& counts = ::Test::TestRunner::the()->counts();
  70. 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());
  71. write(STDOUT_FILENO, buffer, len);
  72. });
  73. #endif
  74. bool print_times = false;
  75. bool print_progress =
  76. #ifdef AK_OS_SERENITY
  77. true; // Use OSC 9 to print progress
  78. #else
  79. false;
  80. #endif
  81. bool print_json = false;
  82. bool per_file = false;
  83. StringView specified_test_root;
  84. DeprecatedString common_path;
  85. DeprecatedString test_glob;
  86. Core::ArgsParser args_parser;
  87. args_parser.add_option(print_times, "Show duration of each test", "show-time", 't');
  88. args_parser.add_option(Core::ArgsParser::Option {
  89. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  90. .help_string = "Show progress with OSC 9 (true, false)",
  91. .long_name = "show-progress",
  92. .short_name = 'p',
  93. .accept_value = [&](StringView str) {
  94. if ("true"sv == str)
  95. print_progress = true;
  96. else if ("false"sv == str)
  97. print_progress = false;
  98. else
  99. return false;
  100. return true;
  101. },
  102. });
  103. args_parser.add_option(print_json, "Show results as JSON", "json", 'j');
  104. args_parser.add_option(per_file, "Show detailed per-file results as JSON (implies -j)", "per-file", 0);
  105. args_parser.add_option(g_collect_on_every_allocation, "Collect garbage after every allocation", "collect-often", 'g');
  106. args_parser.add_option(g_run_bytecode, "Use the bytecode interpreter", "run-bytecode", 'b');
  107. args_parser.add_option(JS::Bytecode::g_dump_bytecode, "Dump the bytecode", "dump-bytecode", 'd');
  108. args_parser.add_option(test_glob, "Only run tests matching the given glob", "filter", 'f', "glob");
  109. for (auto& entry : g_extra_args)
  110. args_parser.add_option(*entry.key, entry.value.get<0>().characters(), entry.value.get<1>().characters(), entry.value.get<2>());
  111. args_parser.add_positional_argument(specified_test_root, "Tests root directory", "path", Core::ArgsParser::Required::No);
  112. args_parser.add_positional_argument(common_path, "Path to tests-common.js", "common-path", Core::ArgsParser::Required::No);
  113. args_parser.parse(arguments);
  114. if (per_file)
  115. print_json = true;
  116. test_glob = DeprecatedString::formatted("*{}*", test_glob);
  117. if (getenv("DISABLE_DBG_OUTPUT")) {
  118. AK::set_debug_enabled(false);
  119. }
  120. if (JS::Bytecode::g_dump_bytecode && !g_run_bytecode) {
  121. warnln("--dump-bytecode can only be used when --run-bytecode is specified.");
  122. return 1;
  123. }
  124. DeprecatedString test_root;
  125. if (!specified_test_root.is_empty()) {
  126. test_root = DeprecatedString { specified_test_root };
  127. } else {
  128. #ifdef AK_OS_SERENITY
  129. test_root = LexicalPath::join("/home/anon/Tests"sv, DeprecatedString::formatted("{}-tests", program_name.split_view('-').last())).string();
  130. #else
  131. char* serenity_source_dir = getenv("SERENITY_SOURCE_DIR");
  132. if (!serenity_source_dir) {
  133. warnln("No test root given, {} requires the SERENITY_SOURCE_DIR environment variable to be set", g_program_name);
  134. return 1;
  135. }
  136. test_root = DeprecatedString::formatted("{}/{}", serenity_source_dir, g_test_root_fragment);
  137. common_path = DeprecatedString::formatted("{}/Userland/Libraries/LibJS/Tests/test-common.js", serenity_source_dir);
  138. #endif
  139. }
  140. if (!FileSystem::is_directory(test_root)) {
  141. warnln("Test root is not a directory: {}", test_root);
  142. return 1;
  143. }
  144. if (common_path.is_empty()) {
  145. #ifdef AK_OS_SERENITY
  146. common_path = "/home/anon/Tests/js-tests/test-common.js";
  147. #else
  148. char* serenity_source_dir = getenv("SERENITY_SOURCE_DIR");
  149. if (!serenity_source_dir) {
  150. warnln("No test root given, {} requires the SERENITY_SOURCE_DIR environment variable to be set", g_program_name);
  151. return 1;
  152. }
  153. common_path = DeprecatedString::formatted("{}/Userland/Libraries/LibJS/Tests/test-common.js", serenity_source_dir);
  154. #endif
  155. }
  156. auto test_root_or_error = FileSystem::real_path(test_root);
  157. if (test_root_or_error.is_error()) {
  158. warnln("Failed to resolve test root: {}", test_root_or_error.error());
  159. return 1;
  160. }
  161. test_root = test_root_or_error.release_value().to_deprecated_string();
  162. auto common_path_or_error = FileSystem::real_path(common_path);
  163. if (common_path_or_error.is_error()) {
  164. warnln("Failed to resolve common path: {}", common_path_or_error.error());
  165. return 1;
  166. }
  167. common_path = common_path_or_error.release_value().to_deprecated_string();
  168. if (chdir(test_root.characters()) < 0) {
  169. auto saved_errno = errno;
  170. warnln("chdir failed: {}", strerror(saved_errno));
  171. return 1;
  172. }
  173. if (g_main_hook)
  174. g_main_hook();
  175. if (!g_vm) {
  176. g_vm = MUST(JS::VM::create());
  177. g_vm->enable_default_host_import_module_dynamically_hook();
  178. }
  179. Test::JS::TestRunner test_runner(test_root, common_path, print_times, print_progress, print_json, per_file);
  180. test_runner.run(test_glob);
  181. g_vm = nullptr;
  182. return test_runner.counts().tests_failed > 0 ? 1 : 0;
  183. }