summaryrefslogtreecommitdiff
path: root/tiger-compiler/src/testsuite/testsuite-generator.cc
blob: 87ca479c3355d166f5cfbd98d8cc4a42c275dee0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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