diff options
author | Ali Mohammad Pur <ali.mpfard@gmail.com> | 2021-05-18 18:46:54 +0430 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-05-18 18:48:15 +0100 |
commit | d897abf4c2e7be4c3aab8fba9869221c19207497 (patch) | |
tree | bf7595de69b83409790fb792695c6a791b09d630 /Tests | |
parent | f137c1bfaac5714197dcba58c0f1f08d2761d133 (diff) | |
download | serenity-d897abf4c2e7be4c3aab8fba9869221c19207497.zip |
LibWeb: Implement test-web in terms of LibTest/JavaScriptTestRunner
This deduplicates the test-js copy-ism :^)
Diffstat (limited to 'Tests')
-rw-r--r-- | Tests/LibWeb/CMakeLists.txt | 3 | ||||
-rw-r--r-- | Tests/LibWeb/test-web.cpp | 683 |
2 files changed, 73 insertions, 613 deletions
diff --git a/Tests/LibWeb/CMakeLists.txt b/Tests/LibWeb/CMakeLists.txt index 0a0aa280c8..4ddba42a05 100644 --- a/Tests/LibWeb/CMakeLists.txt +++ b/Tests/LibWeb/CMakeLists.txt @@ -1,3 +1,2 @@ -add_executable(test-web test-web.cpp) -target_link_libraries(test-web LibCore LibWeb) +serenity_testjs_test(test-web.cpp test-web LIBS LibWeb) install(TARGETS test-web RUNTIME DESTINATION bin) diff --git a/Tests/LibWeb/test-web.cpp b/Tests/LibWeb/test-web.cpp index b6b193fb54..8155e2c881 100644 --- a/Tests/LibWeb/test-web.cpp +++ b/Tests/LibWeb/test-web.cpp @@ -4,664 +4,125 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include <AK/Function.h> -#include <AK/JsonObject.h> -#include <AK/JsonValue.h> -#include <AK/QuickSort.h> #include <AK/URL.h> -#include <LibCore/ArgsParser.h> -#include <LibCore/DirIterator.h> -#include <LibCore/File.h> #include <LibGUI/Application.h> #include <LibGUI/BoxLayout.h> #include <LibGUI/Widget.h> #include <LibGUI/Window.h> -#include <LibJS/Interpreter.h> -#include <LibJS/Lexer.h> -#include <LibJS/Parser.h> -#include <LibJS/Runtime/Array.h> -#include <LibJS/Runtime/JSONObject.h> -#include <LibTest/Results.h> +#include <LibTest/JavaScriptTestRunner.h> +#include <LibWeb/Bindings/MainThreadVM.h> #include <LibWeb/HTML/Parser/HTMLDocumentParser.h> #include <LibWeb/InProcessWebView.h> #include <LibWeb/Loader/ResourceLoader.h> -#include <signal.h> -#include <sys/time.h> -#define TOP_LEVEL_TEST_NAME "__$$TOP_LEVEL$$__" +using namespace Test::JS; -struct ParserError { - JS::Parser::Error error; - String hint; -}; +TEST_ROOT("Userland/Libraries/LibWeb/Tests"); -struct JSFileResult { - String name; - Optional<ParserError> error {}; - double time_taken { 0 }; - // A failed test takes precedence over a skipped test, which both have - // precedence over a passed test - Test::Result most_severe_test_result { Test::Result::Pass }; - Vector<Test::Suite> suites {}; - Vector<String> logged_messages {}; -}; +RefPtr<Web::InProcessWebView> g_page_view; +RefPtr<GUI::Application> g_app; +Optional<URL> next_page_to_load; +Vector<Function<void(JS::Object&)>> after_initial_load_hooks; +Vector<Function<void(JS::Object&)>> before_initial_load_hooks; -Function<void(const URL&)> g_on_page_change; - -class TestRunnerObject final : public JS::Object { - JS_OBJECT(TestRunnerObject, JS::Object); - -public: - explicit TestRunnerObject(JS::GlobalObject&); - virtual void initialize(JS::GlobalObject&) override; - virtual ~TestRunnerObject() override; - -private: - JS_DECLARE_NATIVE_FUNCTION(change_page); -}; - -TestRunnerObject::TestRunnerObject(JS::GlobalObject& global_object) - : Object(*global_object.object_prototype()) -{ -} - -void TestRunnerObject::initialize(JS::GlobalObject& global_object) -{ - Object::initialize(global_object); - define_native_function("changePage", change_page, 1); -} - -TestRunnerObject::~TestRunnerObject() +TESTJS_MAIN_HOOK() { + g_vm = Web::Bindings::main_thread_vm(); + g_app = GUI::Application::construct(g_test_argc, g_test_argv); + auto window = GUI::Window::construct(); + auto& main_widget = window->set_main_widget<GUI::Widget>(); + main_widget.set_fill_with_background_color(true); + main_widget.set_layout<GUI::VerticalBoxLayout>(); + auto& view = main_widget.add<Web::InProcessWebView>(); + view.set_document(Web::DOM::Document::create()); + g_page_view = view; } -JS_DEFINE_NATIVE_FUNCTION(TestRunnerObject::change_page) +TESTJS_GLOBAL_FUNCTION(load_local_page, loadLocalPage) { - auto url = vm.argument(0).to_string(global_object); + auto name = vm.argument(0).to_string(global_object); if (vm.exception()) return {}; - if (g_on_page_change) - g_on_page_change(url); + // Clear the hooks + before_initial_load_hooks.clear(); + after_initial_load_hooks.clear(); + // Set the load URL + if (name.starts_with('/')) + next_page_to_load = URL::create_with_file_protocol(name); + else + next_page_to_load = URL::create_with_file_protocol(LexicalPath::join(g_test_root, "Pages", name).string()); return JS::js_undefined(); } -class TestRunner { -public: - TestRunner(String web_test_root, String js_test_root, Web::InProcessWebView& page_view, bool print_times) - : m_web_test_root(move(web_test_root)) - , m_js_test_root(move(js_test_root)) - , m_print_times(print_times) - , m_page_view(page_view) - { - } - - void run(); - -private: - JSFileResult run_file_test(const String& test_path); - void print_file_result(const JSFileResult& file_result) const; - void print_test_results() const; - - String m_web_test_root; - String m_js_test_root; - bool m_print_times; - - double m_total_elapsed_time_in_ms { 0 }; - Test::Counts m_counts; - - RefPtr<Web::InProcessWebView> m_page_view; - - RefPtr<JS::Program> m_js_test_common; - RefPtr<JS::Program> m_web_test_common; -}; - -static void cleanup_and_exit() -{ - // Clear the taskbar progress. -#ifdef __serenity__ - fprintf(stderr, "\033]9;-1;\033\\"); -#endif - exit(1); -} - -#if 0 -static void handle_sigabrt(int) +TESTJS_GLOBAL_FUNCTION(after_initial_page_load, afterInitialPageLoad) { - dbgln("test-web: SIGABRT received, cleaning up."); - cleanup_and_exit(); -} -#endif - -static double get_time_in_ms() -{ - struct timeval tv1; - auto return_code = gettimeofday(&tv1, nullptr); - VERIFY(return_code >= 0); - return static_cast<double>(tv1.tv_sec) * 1000.0 + static_cast<double>(tv1.tv_usec) / 1000.0; -} - -template<typename Callback> -void iterate_directory_recursively(const String& directory_path, Callback callback) -{ - Core::DirIterator directory_iterator(directory_path, Core::DirIterator::Flags::SkipDots); - - while (directory_iterator.has_next()) { - auto file_path = directory_iterator.next_full_path(); - if (Core::File::is_directory(file_path)) { - iterate_directory_recursively(file_path, callback); - } else { - callback(move(file_path)); - } + auto function = vm.argument(0); + if (!function.is_function()) { + dbgln("afterInitialPageLoad argument is not a function"); + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Function"); + return {}; } -} - -static Vector<String> get_test_paths(const String& test_root) -{ - Vector<String> paths; - iterate_directory_recursively(test_root, [&](const String& file_path) { - if (!file_path.ends_with("test-common.js") && !file_path.ends_with(".html") && !file_path.ends_with(".ts")) - paths.append(file_path); + after_initial_load_hooks.append([fn = JS::make_handle(&function.as_function()), &vm](auto& page_object) { + [[maybe_unused]] auto unused = vm.call(const_cast<JS::Function&>(*fn.cell()), JS::js_undefined(), &page_object); }); - - quick_sort(paths); - - return paths; -} - -void TestRunner::run() -{ - size_t progress_counter = 0; - auto test_paths = get_test_paths(m_web_test_root); - - g_on_page_change = [this](auto& page_to_load) { - if (!page_to_load.is_valid()) { - printf("Invalid page URL (%s) on page change", page_to_load.to_string().characters()); - cleanup_and_exit(); - } - - VERIFY(m_page_view->document()); - - // We want to keep the same document since the interpreter is tied to the document, - // and we don't want to lose the test state. So, we just clear the document and - // give a new parser the existing document to work on. - m_page_view->document()->remove_all_children(); - - Web::LoadRequest request; - request.set_url(page_to_load); - - Web::ResourceLoader::the().load_sync( - request, - [&](auto data, auto&, auto) { - Web::HTML::HTMLDocumentParser parser(*m_page_view->document(), data, "utf-8"); - parser.run(page_to_load); - }, - [page_to_load](auto& error, auto) { - printf("Failed to load test page: %s (%s)", page_to_load.to_string().characters(), error.characters()); - cleanup_and_exit(); - }); - }; - - for (auto& path : test_paths) { - ++progress_counter; - print_file_result(run_file_test(path)); -#ifdef __serenity__ - fprintf(stderr, "\033]9;%zu;%zu;\033\\", progress_counter, test_paths.size()); -#endif - } - -#ifdef __serenity__ - fprintf(stderr, "\033]9;-1;\033\\"); -#endif - - print_test_results(); -} - -static Result<NonnullRefPtr<JS::Program>, ParserError> parse_file(const String& file_path) -{ - auto file = Core::File::construct(file_path); - auto result = file->open(Core::OpenMode::ReadOnly); - if (!result) { - printf("Failed to open the following file: \"%s\"\n", file_path.characters()); - cleanup_and_exit(); - } - - auto contents = file->read_all(); - String test_file_string(reinterpret_cast<const char*>(contents.data()), contents.size()); - file->close(); - - auto parser = JS::Parser(JS::Lexer(test_file_string)); - auto program = parser.parse_program(); - - if (parser.has_errors()) { - auto error = parser.errors()[0]; - return Result<NonnullRefPtr<JS::Program>, ParserError>(ParserError { error, error.source_location_hint(test_file_string) }); - } - - return Result<NonnullRefPtr<JS::Program>, ParserError>(program); + return JS::js_undefined(); } -static Optional<JsonValue> get_test_results(JS::Interpreter& interpreter) +TESTJS_GLOBAL_FUNCTION(before_initial_page_load, beforeInitialPageLoad) { - auto result = interpreter.vm().get_variable("__TestResults__", interpreter.global_object()); - auto json_string = JS::JSONObject::stringify_impl(interpreter.global_object(), result, JS::js_undefined(), JS::js_undefined()); - - auto json = JsonValue::from_string(json_string); - if (!json.has_value()) + auto function = vm.argument(0); + if (!function.is_function()) { + dbgln("beforeInitialPageLoad argument is not a function"); + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Function"); return {}; + } - return json.value(); + before_initial_load_hooks.append([fn = JS::make_handle(&function.as_function()), &vm](auto& page_object) { + [[maybe_unused]] auto unused = vm.call(const_cast<JS::Function&>(*fn.cell()), JS::js_undefined(), &page_object); + }); + return JS::js_undefined(); } -JSFileResult TestRunner::run_file_test(const String& test_path) +TESTJS_GLOBAL_FUNCTION(wait_for_page_to_load, waitForPageToLoad) { - double start_time = get_time_in_ms(); - VERIFY(m_page_view->document()); - auto& old_interpreter = m_page_view->document()->interpreter(); + // Create a new parser and immediately get its document to replace the old interpreter. + auto document = Web::DOM::Document::create(); - if (!m_js_test_common) { - auto result = parse_file(String::formatted("{}/test-common.js", m_js_test_root)); - if (result.is_error()) { - printf("Unable to parse %s/test-common.js\n", m_js_test_root.characters()); - printf("%s\n", result.error().error.to_string().characters()); - printf("%s\n", result.error().hint.characters()); - cleanup_and_exit(); - } - m_js_test_common = result.value(); - } - - if (!m_web_test_common) { - auto result = parse_file(String::formatted("{}/test-common.js", m_web_test_root)); - if (result.is_error()) { - printf("Unable to parse %s/test-common.js\n", m_web_test_root.characters()); - printf("%s\n", result.error().error.to_string().characters()); - printf("%s\n", result.error().hint.characters()); - cleanup_and_exit(); - } - m_web_test_common = result.value(); - } - - auto file_program = parse_file(test_path); - if (file_program.is_error()) - return { test_path, file_program.error() }; - - // Setup the test on the current page to get "__PageToLoad__". - old_interpreter.run(old_interpreter.global_object(), *m_web_test_common); - old_interpreter.run(old_interpreter.global_object(), *file_program.value()); - auto page_to_load = URL(old_interpreter.vm().get_variable("__PageToLoad__", old_interpreter.global_object()).as_string().string()); - if (!page_to_load.is_valid()) { - printf("Invalid page URL for %s", test_path.characters()); - cleanup_and_exit(); - } - - JSFileResult file_result; + // Run the "before" hooks + for (auto& entry : before_initial_load_hooks) + entry(document->interpreter().global_object()); + // Set the load hook Web::LoadRequest request; - request.set_url(page_to_load); + request.set_url(next_page_to_load.value()); - Web::ResourceLoader::the().load_sync( + auto& loader = Web::ResourceLoader::the(); + loader.load_sync( request, [&](auto data, auto&, auto) { - // Create a new parser and immediately get its document to replace the old interpreter. - auto document = Web::DOM::Document::create(); Web::HTML::HTMLDocumentParser parser(document, data, "utf-8"); - auto& new_interpreter = parser.document().interpreter(); - - // FIXME: This is a hack while we're refactoring Interpreter/VM stuff. - JS::VM::InterpreterExecutionScope scope(new_interpreter); - - // Setup the test environment and call "__BeforeInitialPageLoad__" - new_interpreter.global_object().define_property( - "libweb_tester", - new_interpreter.heap().allocate<TestRunnerObject>(new_interpreter.global_object(), new_interpreter.global_object()), - JS::Attribute::Enumerable | JS::Attribute::Configurable); - new_interpreter.run(new_interpreter.global_object(), *m_js_test_common); - new_interpreter.run(new_interpreter.global_object(), *m_web_test_common); - new_interpreter.run(new_interpreter.global_object(), *file_program.value()); - - auto& before_initial_page_load = new_interpreter.vm().get_variable("__BeforeInitialPageLoad__", new_interpreter.global_object()).as_function(); - [[maybe_unused]] auto rc_before = new_interpreter.vm().call(before_initial_page_load, JS::js_undefined()); - if (new_interpreter.exception()) - new_interpreter.vm().clear_exception(); - // Now parse the HTML page. - parser.run(page_to_load); - m_page_view->set_document(&parser.document()); - - // Finally run the test by calling "__AfterInitialPageLoad__" - auto& after_initial_page_load = new_interpreter.vm().get_variable("__AfterInitialPageLoad__", new_interpreter.global_object()).as_function(); - [[maybe_unused]] auto rc_after = new_interpreter.vm().call(after_initial_page_load, JS::js_undefined()); - if (new_interpreter.exception()) - new_interpreter.vm().clear_exception(); - - auto test_json = get_test_results(new_interpreter); - if (!test_json.has_value()) { - printf("Received malformed JSON from test \"%s\"\n", test_path.characters()); - cleanup_and_exit(); + parser.run(next_page_to_load.value()); + g_page_view->set_document(&parser.document()); + if (vm.exception()) { + // FIXME: Should we do something about this? the document itself threw unhandled exceptions... + vm.clear_exception(); } - file_result = { test_path.substring(m_web_test_root.length() + 1, test_path.length() - m_web_test_root.length() - 1) }; - - // Collect logged messages - auto& arr = new_interpreter.vm().get_variable("__UserOutput__", new_interpreter.global_object()).as_array(); - for (auto& entry : arr.indexed_properties()) { - auto message = entry.value_and_attributes(&new_interpreter.global_object()).value; - file_result.logged_messages.append(message.to_string_without_side_effects()); + // Run the "after" hooks + for (auto& entry : after_initial_load_hooks) { + entry(document->interpreter().global_object()); + if (vm.exception()) + break; } - - test_json.value().as_object().for_each_member([&](const String& suite_name, const JsonValue& suite_value) { - Test::Suite suite { suite_name }; - - VERIFY(suite_value.is_object()); - - suite_value.as_object().for_each_member([&](const String& test_name, const JsonValue& test_value) { - Test::Case test { test_name, Test::Result::Fail, "" }; - - VERIFY(test_value.is_object()); - VERIFY(test_value.as_object().has("result")); - - auto result = test_value.as_object().get("result"); - VERIFY(result.is_string()); - auto result_string = result.as_string(); - if (result_string == "pass") { - test.result = Test::Result::Pass; - m_counts.tests_passed++; - } else if (result_string == "fail") { - test.result = Test::Result::Fail; - m_counts.tests_failed++; - suite.most_severe_test_result = Test::Result::Fail; - VERIFY(test_value.as_object().has("details")); - auto details = test_value.as_object().get("details"); - VERIFY(result.is_string()); - test.details = details.as_string(); - } else { - test.result = Test::Result::Skip; - if (suite.most_severe_test_result == Test::Result::Pass) - suite.most_severe_test_result = Test::Result::Skip; - m_counts.tests_skipped++; - } - - suite.tests.append(test); - }); - - if (suite.most_severe_test_result == Test::Result::Fail) { - m_counts.suites_failed++; - file_result.most_severe_test_result = Test::Result::Fail; - } else { - if (suite.most_severe_test_result == Test::Result::Skip && file_result.most_severe_test_result == Test::Result::Pass) - file_result.most_severe_test_result = Test::Result::Skip; - m_counts.suites_passed++; - } - - file_result.suites.append(suite); - }); - - m_counts.files_total++; - - file_result.time_taken = get_time_in_ms() - start_time; - m_total_elapsed_time_in_ms += file_result.time_taken; }, - [page_to_load](auto& error, auto) { - printf("Failed to load test page: %s (%s)", page_to_load.to_string().characters(), error.characters()); - cleanup_and_exit(); + [&](auto) { + dbgln("Load of resource {} failed", next_page_to_load.value()); + vm.throw_exception<Web::DOM::NetworkError>(global_object); }); - return file_result; -} - -enum Modifier { - BG_RED, - BG_GREEN, - FG_RED, - FG_GREEN, - FG_ORANGE, - FG_GRAY, - FG_BLACK, - FG_BOLD, - ITALIC, - CLEAR, -}; - -static void print_modifiers(Vector<Modifier> modifiers) -{ - for (auto& modifier : modifiers) { - auto code = [&]() -> String { - switch (modifier) { - case BG_RED: - return "\033[48;2;255;0;102m"; - case BG_GREEN: - return "\033[48;2;102;255;0m"; - case FG_RED: - return "\033[38;2;255;0;102m"; - case FG_GREEN: - return "\033[38;2;102;255;0m"; - case FG_ORANGE: - return "\033[38;2;255;102;0m"; - case FG_GRAY: - return "\033[38;2;135;139;148m"; - case FG_BLACK: - return "\033[30m"; - case FG_BOLD: - return "\033[1m"; - case ITALIC: - return "\033[3m"; - case CLEAR: - return "\033[0m"; - } - VERIFY_NOT_REACHED(); - }; - printf("%s", code().characters()); - } -} - -void TestRunner::print_file_result(const JSFileResult& file_result) const -{ - if (file_result.most_severe_test_result == Test::Result::Fail || file_result.error.has_value()) { - print_modifiers({ BG_RED, FG_BLACK, FG_BOLD }); - printf(" FAIL "); - print_modifiers({ CLEAR }); - } else { - if (m_print_times || file_result.most_severe_test_result != Test::Result::Pass) { - print_modifiers({ BG_GREEN, FG_BLACK, FG_BOLD }); - printf(" PASS "); - print_modifiers({ CLEAR }); - } else { - return; - } - } - - printf(" %s", file_result.name.characters()); - - if (m_print_times) { - print_modifiers({ CLEAR, ITALIC, FG_GRAY }); - if (file_result.time_taken < 1000) { - printf(" (%dms)\n", static_cast<int>(file_result.time_taken)); - } else { - printf(" (%.3fs)\n", file_result.time_taken / 1000.0); - } - print_modifiers({ CLEAR }); - } else { - printf("\n"); - } - - if (!file_result.logged_messages.is_empty()) { - print_modifiers({ FG_GRAY, FG_BOLD }); -#ifdef __serenity__ - printf(" âš Console output:\n"); -#else - // This emoji has a second invisible byte after it. The one above does not - printf(" âšī¸ Console output:\n"); -#endif - print_modifiers({ CLEAR, FG_GRAY }); - for (auto& message : file_result.logged_messages) - printf(" %s\n", message.characters()); - } - - if (file_result.error.has_value()) { - auto test_error = file_result.error.value(); - - print_modifiers({ FG_RED }); -#ifdef __serenity__ - printf(" â The file failed to parse\n\n"); -#else - // No invisible byte here, but the spacing still needs to be altered on the host - printf(" â The file failed to parse\n\n"); -#endif - print_modifiers({ FG_GRAY }); - for (auto& message : test_error.hint.split('\n', true)) { - printf(" %s\n", message.characters()); - } - print_modifiers({ FG_RED }); - printf(" %s\n\n", test_error.error.to_string().characters()); - - return; - } - - if (file_result.most_severe_test_result != Test::Result::Pass) { - for (auto& suite : file_result.suites) { - if (suite.most_severe_test_result == Test::Result::Pass) - continue; - - bool failed = suite.most_severe_test_result == Test::Result::Fail; - - print_modifiers({ FG_GRAY, FG_BOLD }); - - if (failed) { -#ifdef __serenity__ - printf(" â Suite: "); -#else - // No invisible byte here, but the spacing still needs to be altered on the host - printf(" â Suite: "); -#endif - } else { -#ifdef __serenity__ - printf(" â Suite: "); -#else - // This emoji has a second invisible byte after it. The one above does not - printf(" â ī¸ Suite: "); -#endif - } - - print_modifiers({ CLEAR, FG_GRAY }); - - if (suite.name == TOP_LEVEL_TEST_NAME) { - printf("<top-level>\n"); - } else { - printf("%s\n", suite.name.characters()); - } - print_modifiers({ CLEAR }); - - for (auto& test : suite.tests) { - if (test.result == Test::Result::Pass) - continue; - - print_modifiers({ FG_GRAY, FG_BOLD }); - printf(" Test: "); - if (test.result == Test::Result::Fail) { - print_modifiers({ CLEAR, FG_RED }); - printf("%s (failed):\n", test.name.characters()); - printf(" %s\n", test.details.characters()); - } else { - print_modifiers({ CLEAR, FG_ORANGE }); - printf("%s (skipped)\n", test.name.characters()); - } - print_modifiers({ CLEAR }); - } - } - } -} - -void TestRunner::print_test_results() const -{ - printf("\nTest Suites: "); - if (m_counts.suites_failed) { - print_modifiers({ FG_RED }); - printf("%d failed, ", m_counts.suites_failed); - print_modifiers({ CLEAR }); - } - if (m_counts.suites_passed) { - print_modifiers({ FG_GREEN }); - printf("%d passed, ", m_counts.suites_passed); - print_modifiers({ CLEAR }); - } - printf("%d total\n", m_counts.suites_failed + m_counts.suites_passed); - - printf("Tests: "); - if (m_counts.tests_failed) { - print_modifiers({ FG_RED }); - printf("%d failed, ", m_counts.tests_failed); - print_modifiers({ CLEAR }); - } - if (m_counts.tests_skipped) { - print_modifiers({ FG_ORANGE }); - printf("%d skipped, ", m_counts.tests_skipped); - print_modifiers({ CLEAR }); - } - if (m_counts.tests_passed) { - print_modifiers({ FG_GREEN }); - printf("%d passed, ", m_counts.tests_passed); - print_modifiers({ CLEAR }); - } - printf("%d total\n", m_counts.tests_failed + m_counts.tests_passed); - - printf("Files: %d total\n", m_counts.files_total); - - printf("Time: "); - if (m_total_elapsed_time_in_ms < 1000.0) { - printf("%dms\n\n", static_cast<int>(m_total_elapsed_time_in_ms)); - } else { - printf("%-.3fs\n\n", m_total_elapsed_time_in_ms / 1000.0); - } -} - -int main(int argc, char** argv) -{ - bool print_times = false; - bool show_window = false; - -#if 0 - 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; - } -#endif - - Core::ArgsParser args_parser; - args_parser.add_option(print_times, "Show duration of each test", "show-time", 't'); - args_parser.add_option(show_window, "Show window while running tests", "window", 'w'); - args_parser.parse(argc, argv); - - auto app = GUI::Application::construct(argc, argv); - auto window = GUI::Window::construct(); - auto& main_widget = window->set_main_widget<GUI::Widget>(); - main_widget.set_fill_with_background_color(true); - main_widget.set_layout<GUI::VerticalBoxLayout>(); - auto& view = main_widget.add<Web::InProcessWebView>(); - - view.set_document(Web::DOM::Document::create()); - - if (show_window) { - window->set_title("LibWeb Test Window"); - window->resize(640, 480); - window->show(); - } - -#ifdef __serenity__ - TestRunner("/home/anon/web-tests", "/home/anon/js-tests", view, print_times).run(); -#else - char* serenity_source_dir = getenv("SERENITY_SOURCE_DIR"); - if (!serenity_source_dir) { - printf("test-web requires the SERENITY_SOURCE_DIR environment variable to be set"); - return 1; - } - TestRunner(String::formatted("{}/Userland/Libraries/LibWeb/Tests", serenity_source_dir), String::formatted("{}/Userland/Libraries/LibJS/Tests", serenity_source_dir), view, print_times).run(); -#endif - return 0; + return JS::js_undefined(); } |