Start working on the interpeter
[fur] / integration_tests.py
1 import os
2 import os.path
3 import subprocess
4 import unittest
5
6 # Go to the directory of the current file so we know where we are in the filesystem
7 os.chdir(os.path.dirname(os.path.abspath(__file__)))
8
9 class InterpreterOutputTests(unittest.TestCase):
10     pass
11
12 def add_example_interpreter_output_test(filename):
13     def test(self):
14         p = subprocess.Popen(
15             (
16                 'python',
17                 'main.py',
18                 'interpret',
19                 os.path.join('examples', filename),
20             ),
21             stdout=subprocess.PIPE,
22             stderr=subprocess.PIPE,
23         )
24
25         actual_stdout, actual_stderr = p.communicate()
26
27         expected_stdout_path = os.path.join('examples', filename + '.stdout.txt')
28
29         if os.path.isfile(expected_stdout_path):
30             with open(expected_stdout_path, 'rb') as f:
31                 expected_stdout = f.read()
32         else:
33             expected_stdout = b''
34
35         expected_stderr_path = os.path.join('examples', filename + '.stderr.txt')
36
37         if os.path.isfile(expected_stderr_path):
38             with open(expected_stderr_path, 'rb') as f:
39                 expected_stderr = f.read()
40         else:
41             expected_stderr = b''
42
43         self.assertEqual(expected_stderr, actual_stderr)
44
45     setattr(InterpreterOutputTests, 'test_' + filename[:-4], test)
46
47 class CompilerOutputTests(unittest.TestCase):
48     pass
49
50 def add_example_compiler_output_test(filename):
51     def test(self):
52         compile_fur_to_c_result = subprocess.call([
53             'python',
54             'main.py',
55             'compile',
56             os.path.join('examples', filename),
57         ])
58
59         if compile_fur_to_c_result != 0:
60             raise Exception('Example "{}" did not compile'.format(filename))
61
62         compile_c_to_executable_result = subprocess.call([
63             'gcc',
64             os.path.join('examples', filename + '.c'),
65         ])
66
67         if compile_c_to_executable_result != 0:
68             raise Exception('Example output "{}" did not compile'.format(filename + '.c'))
69
70         try:
71             p = subprocess.Popen('./a.out', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
72             actual_stdout, actual_stderr = p.communicate()
73
74             expected_stdout_path = os.path.join('examples', filename + '.stdout.txt')
75
76             if os.path.isfile(expected_stdout_path):
77                 with open(expected_stdout_path, 'rb') as f:
78                     expected_stdout = f.read()
79             else:
80                 expected_stdout = b''
81
82             expected_stderr_path = os.path.join('examples', filename + '.stderr.txt')
83
84             if os.path.isfile(expected_stderr_path):
85                 with open(expected_stderr_path, 'rb') as f:
86                     expected_stderr = f.read()
87             else:
88                 expected_stderr = b''
89
90             self.assertEqual(expected_stderr, actual_stderr)
91
92             # We don't clean up the C file in the finally clause because it can be useful to have in case of errors
93             os.remove(os.path.join('examples', filename + '.c'))
94
95         finally:
96             try:
97                 os.remove('a.out')
98             except OSError:
99                 pass
100
101     setattr(CompilerOutputTests, 'test_' + filename[:-4], test)
102
103 class MemoryLeakTests(unittest.TestCase):
104     pass
105
106 def add_example_memory_leak_test(filename):
107     def test(self):
108         compile_fur_to_c_result = subprocess.call([
109             'python',
110             'main.py',
111             os.path.join('examples', filename),
112         ])
113
114         if compile_fur_to_c_result != 0:
115             raise Exception('Example "{}" did not compile'.format(filename))
116
117         compile_c_to_executable_result = subprocess.call([
118             'gcc',
119             '-ggdb3',
120             os.path.join('examples', filename + '.c'),
121         ])
122
123         if compile_c_to_executable_result != 0:
124             raise Exception('Example output "{}" did not compile'.format(filename + '.c'))
125
126         try:
127             with open(os.devnull, 'w') as devnull:
128                 expected_return = 0
129                 actual_return = subprocess.call(
130                     [
131                         'valgrind',
132                         '--tool=memcheck',
133                         '--leak-check=yes',
134                         '--show-reachable=yes',
135                         '--num-callers=20',
136                         '--track-fds=yes',
137                         '--error-exitcode=42',
138                         '-q',
139                         './a.out',
140                     ],
141                     stdout=devnull,
142                     stderr=devnull,
143                 )
144
145                 self.assertEqual(expected_return, actual_return)
146
147                 # We don't clean up the C file in the finally clause because it can be useful to have in case of errors
148                 os.remove(os.path.join('examples', filename + '.c'))
149
150         finally:
151             try:
152                 os.remove('a.out')
153             except OSError:
154                 pass
155
156     setattr(MemoryLeakTests, 'test_' + filename[:-4], test)
157
158 filenames = (
159     entry.name
160     for entry in os.scandir('examples')
161     if entry.is_file()
162     if entry.name.endswith('.fur')
163 )
164
165 for filename in filenames:
166     add_example_compiler_output_test(filename)
167     add_example_interpreter_output_test(filename)
168     add_example_memory_leak_test(filename)
169
170 unittest.main()