summaryrefslogtreecommitdiff
path: root/tiger-compiler/src/testsuite
diff options
context:
space:
mode:
Diffstat (limited to 'tiger-compiler/src/testsuite')
-rw-r--r--tiger-compiler/src/testsuite/libtestsuite.cc30
-rw-r--r--tiger-compiler/src/testsuite/libtestsuite.hh41
-rw-r--r--tiger-compiler/src/testsuite/local.am15
-rw-r--r--tiger-compiler/src/testsuite/tasks.cc49
-rw-r--r--tiger-compiler/src/testsuite/tasks.hh21
-rw-r--r--tiger-compiler/src/testsuite/tests-collector.cc19
-rw-r--r--tiger-compiler/src/testsuite/tests-collector.hh53
-rw-r--r--tiger-compiler/src/testsuite/tests-collector.hxx21
-rw-r--r--tiger-compiler/src/testsuite/testsuite-generator.cc143
-rw-r--r--tiger-compiler/src/testsuite/testsuite-generator.hh23
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