diff options
| author | Martial Simon <msimon_fr@hotmail.com> | 2025-09-15 01:07:58 +0200 |
|---|---|---|
| committer | Martial Simon <msimon_fr@hotmail.com> | 2025-09-15 01:07:58 +0200 |
| commit | 967be9e750221ab2ab783f95df79bb26d290a45e (patch) | |
| tree | 6802900a5e975f9f68b169f0f503f040056d6952 /tiger-compiler/src/testsuite | |
Diffstat (limited to 'tiger-compiler/src/testsuite')
| -rw-r--r-- | tiger-compiler/src/testsuite/libtestsuite.cc | 30 | ||||
| -rw-r--r-- | tiger-compiler/src/testsuite/libtestsuite.hh | 41 | ||||
| -rw-r--r-- | tiger-compiler/src/testsuite/local.am | 15 | ||||
| -rw-r--r-- | tiger-compiler/src/testsuite/tasks.cc | 49 | ||||
| -rw-r--r-- | tiger-compiler/src/testsuite/tasks.hh | 21 | ||||
| -rw-r--r-- | tiger-compiler/src/testsuite/tests-collector.cc | 19 | ||||
| -rw-r--r-- | tiger-compiler/src/testsuite/tests-collector.hh | 53 | ||||
| -rw-r--r-- | tiger-compiler/src/testsuite/tests-collector.hxx | 21 | ||||
| -rw-r--r-- | tiger-compiler/src/testsuite/testsuite-generator.cc | 143 | ||||
| -rw-r--r-- | tiger-compiler/src/testsuite/testsuite-generator.hh | 23 |
10 files changed, 415 insertions, 0 deletions
diff --git a/tiger-compiler/src/testsuite/libtestsuite.cc b/tiger-compiler/src/testsuite/libtestsuite.cc new file mode 100644 index 0000000..86a5661 --- /dev/null +++ b/tiger-compiler/src/testsuite/libtestsuite.cc @@ -0,0 +1,30 @@ +/** + ** \file testsuite/libtestsuite.cc + ** \brief Functions exported by the testsuite module. + */ + +#include <testsuite/libtestsuite.hh> + +#include <testsuite/tests-collector.hh> +#include <testsuite/testsuite-generator.hh> + +namespace testsuite +{ + + std::vector<const ast::FunctionDec*> + find_program_tests(const ast::ChunkList& program) + { + TestsCollector tests_collector; + + tests_collector(program); + + return tests_collector.tests_get(); + } + + ast::ChunkInterface* + create_testsuite_runtime(const std::vector<const ast::FunctionDec*>& tests) + { + return generate_testsuite_runtime(tests); + } + +} // namespace testsuite diff --git a/tiger-compiler/src/testsuite/libtestsuite.hh b/tiger-compiler/src/testsuite/libtestsuite.hh new file mode 100644 index 0000000..2c42e04 --- /dev/null +++ b/tiger-compiler/src/testsuite/libtestsuite.hh @@ -0,0 +1,41 @@ +/** + ** \file testsuite/libtestsuite.hh + ** \brief Declare functions and variables exported by testsuite module. + */ + +#pragma once + +#include <ast/function-dec.hh> + +namespace testsuite + { + + /** + * Find all the tests functions of a program and return references to each of + * them. + * + * A function is considered a test function if it has the following + * properties :\n + * - It is located at the first level of a program file\n + * - Its name starts with test_* + * + * @param program the program to analyse + * @return a vector containing a reference to each test function + */ + std::vector<const ast::FunctionDec*> + find_program_tests(const ast::ChunkList& program); + + /** + * Create a testsuite runtime, which runs each function referenced in the + * vector. + * + * The return tree may then be appended to the main program AST list to be + * compiled along with the rest of this latter. + * + * @param tests the tests to run + * @return an AST tree describing the resulting testsuite runtime + */ + ast::ChunkInterface* + create_testsuite_runtime(const std::vector<const ast::FunctionDec*>& tests); + + } // namespace testsuite diff --git a/tiger-compiler/src/testsuite/local.am b/tiger-compiler/src/testsuite/local.am new file mode 100644 index 0000000..0cd44bb --- /dev/null +++ b/tiger-compiler/src/testsuite/local.am @@ -0,0 +1,15 @@ +## testsuite module + +src_libtc_la_SOURCES += \ + %D%/libtestsuite.hh \ + %D%/libtestsuite.cc + +src_libtc_la_SOURCES += \ + %D%/tests-collector.hh %D%/tests-collector.cc \ + %D%/testsuite-generator.hh %D%/testsuite-generator.cc + + +## tasks + +TASKS += %D%/tasks.hh %D%/tasks.cc + diff --git a/tiger-compiler/src/testsuite/tasks.cc b/tiger-compiler/src/testsuite/tasks.cc new file mode 100644 index 0000000..c7f1164 --- /dev/null +++ b/tiger-compiler/src/testsuite/tasks.cc @@ -0,0 +1,49 @@ +/** + ** \file testsuite/tasks.hh + ** \brief Testsuite module related tasks' implementation. + */ + +#include <parse/libparse.hh> +#include <parse/tweast.hh> +#include <testsuite/libtestsuite.hh> + +#include <ast/tasks.hh> +#include <parse/tasks.hh> +#define DEFINE_TASKS 1 +#include <testsuite/tasks.hh> +#undef DEFINE_TASKS + +namespace testsuite::tasks + { + + void add_testsuite_runtime() + { + /** + * A testsuite being a series of function declaration, the prelude is + * not included by default. As such, we need to insert it on our own + * within the AST. + */ + parse::Tweast new_program; + + const auto prelude = parse::parse_prelude(); + const auto current_program = ast::tasks::the_program.release(); + + const auto tests = find_program_tests(*current_program); + + const auto testsuite_runtime = create_testsuite_runtime(tests); + + current_program->emplace_back(testsuite_runtime); + + if (prelude != nullptr) + { + new_program << prelude; + } + + new_program << current_program; + + const auto new_ast = parse::parse(new_program); + + ast::tasks::the_program = std::unique_ptr<ast::ChunkList>{new_ast}; + } + + } // namespace testsuite diff --git a/tiger-compiler/src/testsuite/tasks.hh b/tiger-compiler/src/testsuite/tasks.hh new file mode 100644 index 0000000..463e7e0 --- /dev/null +++ b/tiger-compiler/src/testsuite/tasks.hh @@ -0,0 +1,21 @@ +/** + ** \file testsuite/tasks.hh + ** \brief Testsuite module related tasks. + */ + +#pragma once + +#include <task/libtask.hh> + +namespace testsuite::tasks + { + + TASK_GROUP("Testsuite"); + + TASK_DECLARE("testsuite", + "run all declared test_* functions within a testsuite runtime " + "(preludes included automatically, _main must not be set)", + add_testsuite_runtime, + "parse"); + + } // namespace testsuite diff --git a/tiger-compiler/src/testsuite/tests-collector.cc b/tiger-compiler/src/testsuite/tests-collector.cc new file mode 100644 index 0000000..b3365b2 --- /dev/null +++ b/tiger-compiler/src/testsuite/tests-collector.cc @@ -0,0 +1,19 @@ +/** + ** \file testsuite/tests-collector.cc + ** \brief Implementation of testsuite::TestsCollector. + */ + +#include <testsuite/tests-collector.hh> + +namespace testsuite + { + + void TestsCollector::operator()(const ast::FunctionDec& e) + { + if (is_a_test_function(e)) + { + tests_.push_back(&e); + } + } + + } // namespace testsuite diff --git a/tiger-compiler/src/testsuite/tests-collector.hh b/tiger-compiler/src/testsuite/tests-collector.hh new file mode 100644 index 0000000..d7e3ef1 --- /dev/null +++ b/tiger-compiler/src/testsuite/tests-collector.hh @@ -0,0 +1,53 @@ +/** + ** \file testsuite/tests-collector.hh + ** \brief Declaration of testsuite::TestsCollector. + */ + +#pragma once +#include <ast/default-visitor.hh> +#include <ast/non-assert-visitor.hh> +#include <ast/non-object-visitor.hh> + +namespace testsuite + { + + class TestsCollector + : public ast::DefaultConstVisitor + , public ast::NonObjectConstVisitor + , public ast::NonAssertConstVisitor + { + + public: + using super_type = ast::DefaultConstVisitor; + using super_type::operator(); + + TestsCollector() = default; + + /** + * Return all the functions recognized as test functions by the + * visitor. + * + * @return a vector of references to FunctionDec nodes that represents + * test functions + */ + inline std::vector<const ast::FunctionDec*>& tests_get(); + + void operator()(const ast::FunctionDec& e) override; + + protected: + // The tests identified by the visitor + std::vector<const ast::FunctionDec*> tests_; + + /** + * Return whether the function declaration is identified as a test + * function. + * @param node an ast node + * @return true if the node references a valid test function, false + * otherwise + */ + inline bool is_a_test_function(const ast::FunctionDec& node) ; + }; + + } // namespace testsuite + +#include <testsuite/tests-collector.hxx> diff --git a/tiger-compiler/src/testsuite/tests-collector.hxx b/tiger-compiler/src/testsuite/tests-collector.hxx new file mode 100644 index 0000000..9570dc1 --- /dev/null +++ b/tiger-compiler/src/testsuite/tests-collector.hxx @@ -0,0 +1,21 @@ +#pragma once + +#include <testsuite/tests-collector.hh> + +namespace testsuite + { + + inline std::vector<const ast::FunctionDec*>& TestsCollector::tests_get() + { + return tests_; + } + + inline bool + TestsCollector::is_a_test_function(const ast::FunctionDec& node) + { + return node.name_get().get().starts_with("test_") + && node.formals_get().empty(); + } + + + } // namespace testsuite diff --git a/tiger-compiler/src/testsuite/testsuite-generator.cc b/tiger-compiler/src/testsuite/testsuite-generator.cc new file mode 100644 index 0000000..87ca479 --- /dev/null +++ b/tiger-compiler/src/testsuite/testsuite-generator.cc @@ -0,0 +1,143 @@ +/** + ** \file testsuite/testsuite-generator.cc + ** \brief Implementation of testsuite::TestsuiteGenerator. + */ + +#include <testsuite/testsuite-generator.hh> + +#include <ast/function-dec.hh> +#include <parse/libparse.hh> +#include <parse/tweast.hh> + +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<std::string>& 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<const ast::FunctionDec*>& tests) + { + parse::Tweast declarations; + std::vector<std::string> 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 diff --git a/tiger-compiler/src/testsuite/testsuite-generator.hh b/tiger-compiler/src/testsuite/testsuite-generator.hh new file mode 100644 index 0000000..c7d9424 --- /dev/null +++ b/tiger-compiler/src/testsuite/testsuite-generator.hh @@ -0,0 +1,23 @@ +/** + ** \file testsuite/testsuite-generator.hh + ** \brief Declaration of testsuite::TestsuiteGenerator. + */ + +#pragma once + +#include <ast/chunk-interface.hh> + +namespace testsuite + { + + /** + * Generate a new AST providing a _main function. This main function + * calls each provided function within the context of a testsuite runtime. + * + * @param tests a vector referencing every test to run within the testsuite + * @return a new AST which can be appened to the main program + */ + ast::ChunkInterface* + generate_testsuite_runtime(const std::vector<const ast::FunctionDec*>& tests); + + } // namespace testsuite |
