Start implementing functions
[fur] / integration_tests.py
index ddf444b..25550f6 100644 (file)
@@ -3,7 +3,8 @@ import os.path
 import subprocess
 import unittest
 
-EXAMPLES_PATH = os.path.join(os.path.dirname(__file__), 'examples')
+# Go to the directory of the current file so we know where we are in the filesystem
+os.chdir(os.path.dirname(os.path.abspath(__file__)))
 
 class OutputTests(unittest.TestCase):
     pass
@@ -13,7 +14,7 @@ def add_example_output_test(filename):
         compile_fur_to_c_result = subprocess.call([
             'python',
             'main.py',
-            os.path.join(EXAMPLES_PATH, filename),
+            os.path.join('examples', filename),
         ])
 
         if compile_fur_to_c_result != 0:
@@ -21,29 +22,109 @@ def add_example_output_test(filename):
 
         compile_c_to_executable_result = subprocess.call([
             'gcc',
-            os.path.join(EXAMPLES_PATH, filename + '.c'),
+            os.path.join('examples', filename + '.c'),
         ])
 
         if compile_c_to_executable_result != 0:
             raise Exception('Example output "{}" did not compile'.format(filename + '.c'))
 
-        actual_output = subprocess.check_output(['./a.out'])
+        try:
+            p = subprocess.Popen('./a.out', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            actual_stdout, actual_stderr = p.communicate()
 
-        with open(os.path.join(EXAMPLES_PATH, filename + '.output.txt'), 'rb') as f:
-            expected_output = f.read()
+            expected_stdout_path = os.path.join('examples', filename + '.stdout.txt')
 
-        self.assertEqual(expected_output, actual_output)
+            if os.path.isfile(expected_stdout_path):
+                with open(expected_stdout_path, 'rb') as f:
+                    expected_stdout = f.read()
+            else:
+                expected_stdout = b''
+
+            expected_stderr_path = os.path.join('examples', filename + '.stderr.txt')
+
+            if os.path.isfile(expected_stderr_path):
+                with open(expected_stderr_path, 'rb') as f:
+                    expected_stderr = f.read()
+            else:
+                expected_stderr = b''
+
+            self.assertEqual(expected_stderr, actual_stderr)
+
+            # We don't clean up the C file in the finally clause because it can be useful to have in case of errors
+            os.remove(os.path.join('examples', filename + '.c'))
+
+        finally:
+            try:
+                os.remove('a.out')
+            except OSError:
+                pass
 
     setattr(OutputTests, 'test_' + filename, test)
 
+class MemoryLeakTests(unittest.TestCase):
+    pass
+
+def add_example_memory_leak_test(filename):
+    def test(self):
+        compile_fur_to_c_result = subprocess.call([
+            'python',
+            'main.py',
+            os.path.join('examples', filename),
+        ])
+
+        if compile_fur_to_c_result != 0:
+            raise Exception('Example "{}" did not compile'.format(filename))
+
+        compile_c_to_executable_result = subprocess.call([
+            'gcc',
+            '-ggdb3',
+            os.path.join('examples', filename + '.c'),
+        ])
+
+        if compile_c_to_executable_result != 0:
+            raise Exception('Example output "{}" did not compile'.format(filename + '.c'))
+
+        try:
+            with open(os.devnull, 'w') as devnull:
+                expected_return = 0
+                actual_return = subprocess.call(
+                    [
+                        'valgrind',
+                        '--tool=memcheck',
+                        '--leak-check=yes',
+                        '--show-reachable=yes',
+                        '--num-callers=20',
+                        '--track-fds=yes',
+                        '--error-exitcode=42',
+                        '-q',
+                        './a.out',
+                    ],
+                    stdout=devnull,
+                    stderr=devnull,
+                )
+
+                self.assertEqual(expected_return, actual_return)
+
+                # We don't clean up the C file in the finally clause because it can be useful to have in case of errors
+                os.remove(os.path.join('examples', filename + '.c'))
+
+        finally:
+            try:
+                os.remove('a.out')
+            except OSError:
+                pass
+
+    setattr(MemoryLeakTests, 'test_' + filename, test)
+
 filenames = (
     entry.name
-    for entry in os.scandir(EXAMPLES_PATH)
+    for entry in os.scandir('examples')
     if entry.is_file()
     if entry.name.endswith('.fur')
 )
 
 for filename in filenames:
     add_example_output_test(filename)
+    add_example_memory_leak_test(filename)
 
 unittest.main()