/** ** \file testsuite/testsuite-generator.cc ** \brief Implementation of testsuite::TestsuiteGenerator. */ #include #include #include #include namespace testsuite { /** * Generate a function which calls the provided test with no arguments. * @param definitions the testsuite definition * @param test_reference the function to create a unit test for * @return the new function's name */ static std::string add_test_to_testsuite( parse::Tweast& definitions, const ast::FunctionDec* test_reference) { const std::string test_name = test_reference->name_get().get(); const std::string callback_name = "testsuite_" + test_name; definitions << "function " + callback_name + "() = " "let " "var child_pid := -1 " "var status_code := -1 " "var exit_code := -1 " "in " "child_pid := fork(); " "if (child_pid = -1) then ( " "print_err(\"could not run child process. skipping test\"); " "skipped := skipped + 1 " ") " "else if (child_pid = 0) then ( " + test_name + "(); " "exit(0) " ") " "else ( " "status_code := wait_pid(child_pid); " "if (status_code = 0) then ( " "print(\" o \") " ") " "else (" "print(\" x \") " "); " "print(\"" + test_name + " test \"); " "if (status_code = 0) then ( " "print(\"passed\"); " "passed := passed + 1 " ") " "else (" "print(\"failed with \"); " "exit_code := get_exit_code(status_code); " "if (status_code = 6) then " "print(\"assertion failure\") " "else if (status_code = 8) then " "print(\"illegal instruction\") " "else if (status_code = 11) then " "print(\"segmentation fault\") " "else if (exit_code = 120) then " "print(\"runtime failure\") " "else ( " "print(\"other error code \"); " "print_int(exit_code) " "); " "failed := failed + 1" "); " "print(\"\\n\"); " "flush() " ") " "end "; return callback_name; } /** * Build the testsuite runtime, parse the result and return the AST. * @param definitions the function declarations to add within the runtime * @param names the names of the tests to run, once declared * @return the resulting AST */ static ast::ChunkInterface* build_testsuite( const parse::Tweast& definitions, const std::vector& names) { parse::Tweast runtime; runtime << "function _main() = let " "var skipped := 0 " "var passed := 0 " "var failed := 0 "; runtime << definitions.input_get(); runtime << " in " "print(\"====================== Begin testing =====================\\n\\n\"); "; for (const std::string& test : names) { runtime << test << "(); "; } runtime << "print(\"\\n=============== TC-RITERION testing result ===============\\n\"); " "print(\" o Passed: \"); " "print_int(passed); " "print(\"\\n o Failed: \"); " "print_int(failed); " "if (skipped > 0) then ( " "print(\"\\n o Skipped: \"); " "print_int(skipped) " "); " "print(\"\\n==========================================================\\n\"); " "flush() " "end"; return parse::parse_chunks(runtime); } ast::ChunkInterface* generate_testsuite_runtime( const std::vector& tests) { parse::Tweast declarations; std::vector test_names; for (const auto test : tests) { const std::string test_name = add_test_to_testsuite(declarations, test); test_names.push_back(test_name); } return build_testsuite(declarations, test_names); } } // namespace testsuite