JavaScriptTestRunnerMain.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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 <LibTest/JavaScriptTestRunner.h>
  9. #include <signal.h>
  10. #include <stdio.h>
  11. namespace Test::JS {
  12. RefPtr<::JS::VM> g_vm;
  13. bool g_collect_on_every_allocation = false;
  14. String g_currently_running_test;
  15. String g_test_glob;
  16. HashMap<String, FunctionWithLength> s_exposed_global_functions;
  17. Function<void()> g_main_hook;
  18. TestRunner* TestRunner::s_the = nullptr;
  19. String g_test_root;
  20. int g_test_argc;
  21. char** g_test_argv;
  22. }
  23. using namespace Test::JS;
  24. static StringView g_program_name { "test-js"sv };
  25. static void handle_sigabrt(int)
  26. {
  27. dbgln("{}: SIGABRT received, cleaning up.", g_program_name);
  28. cleanup();
  29. struct sigaction act;
  30. memset(&act, 0, sizeof(act));
  31. act.sa_flags = SA_NOCLDWAIT;
  32. act.sa_handler = SIG_DFL;
  33. int rc = sigaction(SIGABRT, &act, nullptr);
  34. if (rc < 0) {
  35. perror("sigaction");
  36. exit(1);
  37. }
  38. abort();
  39. }
  40. int main(int argc, char** argv)
  41. {
  42. g_test_argc = argc;
  43. g_test_argv = argv;
  44. auto program_name = LexicalPath { argv[0] }.basename();
  45. g_program_name = program_name;
  46. struct sigaction act;
  47. memset(&act, 0, sizeof(act));
  48. act.sa_flags = SA_NOCLDWAIT;
  49. act.sa_handler = handle_sigabrt;
  50. int rc = sigaction(SIGABRT, &act, nullptr);
  51. if (rc < 0) {
  52. perror("sigaction");
  53. return 1;
  54. }
  55. #ifdef SIGINFO
  56. signal(SIGINFO, [](int) {
  57. static char buffer[4096];
  58. auto& counts = TestRunner::the()->counts();
  59. 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());
  60. write(STDOUT_FILENO, buffer, len);
  61. });
  62. #endif
  63. bool print_times = false;
  64. bool print_progress =
  65. #ifdef __serenity__
  66. true; // Use OSC 9 to print progress
  67. #else
  68. false;
  69. #endif
  70. const char* specified_test_root = nullptr;
  71. String common_path;
  72. Core::ArgsParser args_parser;
  73. args_parser.add_option(print_times, "Show duration of each test", "show-time", 't');
  74. args_parser.add_option(Core::ArgsParser::Option {
  75. .requires_argument = true,
  76. .help_string = "Show progress with OSC 9 (true, false)",
  77. .long_name = "show-progress",
  78. .short_name = 'p',
  79. .accept_value = [&](auto* str) {
  80. if (StringView { "true" } == str)
  81. print_progress = true;
  82. else if (StringView { "false" } == str)
  83. print_progress = false;
  84. else
  85. return false;
  86. return true;
  87. },
  88. });
  89. args_parser.add_option(g_collect_on_every_allocation, "Collect garbage after every allocation", "collect-often", 'g');
  90. args_parser.add_option(g_test_glob, "Only run tests matching the given glob", "filter", 'f', "glob");
  91. args_parser.add_positional_argument(specified_test_root, "Tests root directory", "path", Core::ArgsParser::Required::No);
  92. args_parser.add_positional_argument(common_path, "Path to tests-common.js", "common-path", Core::ArgsParser::Required::No);
  93. args_parser.parse(argc, argv);
  94. g_test_glob = String::formatted("*{}*", g_test_glob);
  95. if (getenv("DISABLE_DBG_OUTPUT")) {
  96. AK::set_debug_enabled(false);
  97. }
  98. String test_root;
  99. if (specified_test_root) {
  100. test_root = String { specified_test_root };
  101. } else {
  102. #ifdef __serenity__
  103. test_root = LexicalPath::join("/home/anon", String::formatted("{}-tests", program_name.split_view('-').last())).string();
  104. #else
  105. char* serenity_source_dir = getenv("SERENITY_SOURCE_DIR");
  106. if (!serenity_source_dir) {
  107. warnln("No test root given, {} requires the SERENITY_SOURCE_DIR environment variable to be set", g_program_name);
  108. return 1;
  109. }
  110. test_root = String::formatted("{}/{}", serenity_source_dir, g_test_root_fragment);
  111. common_path = String::formatted("{}/Userland/Libraries/LibJS/Tests/test-common.js", serenity_source_dir);
  112. #endif
  113. }
  114. if (!Core::File::is_directory(test_root)) {
  115. warnln("Test root is not a directory: {}", test_root);
  116. return 1;
  117. }
  118. if (common_path.is_empty()) {
  119. #ifdef __serenity__
  120. common_path = "/home/anon/js-tests/test-common.js";
  121. #else
  122. char* serenity_source_dir = getenv("SERENITY_SOURCE_DIR");
  123. if (!serenity_source_dir) {
  124. warnln("No test root given, {} requires the SERENITY_SOURCE_DIR environment variable to be set", g_program_name);
  125. return 1;
  126. }
  127. common_path = String::formatted("{}/Userland/Libraries/LibJS/Tests/test-common.js", serenity_source_dir);
  128. #endif
  129. }
  130. if (chdir(test_root.characters()) < 0) {
  131. auto saved_errno = errno;
  132. warnln("chdir failed: {}", strerror(saved_errno));
  133. return 1;
  134. }
  135. if (g_main_hook)
  136. g_main_hook();
  137. if (!g_vm)
  138. g_vm = JS::VM::create();
  139. TestRunner test_runner(test_root, common_path, print_times, print_progress);
  140. test_runner.run();
  141. g_vm = nullptr;
  142. return test_runner.counts().tests_failed > 0 ? 1 : 0;
  143. }