"""Integration test driver for the compiler.

This module defines a metaclass which generates test cases from files on disk,
and a test class based off that metaclass. For each file that matches
"tests/ctests/*.c", a test function is generated.

If the C file contains a line of the form:

// Return: ###

Then, the test expects the main() in that test file to return the value
"###". If no such line exists, the default expected return value is 0.

If the C file contains line(s) of the form:
// Issue: ____

Then, the test expects compiliation to fail with an error or warning whose
message contains the string "____".

"""

from errors import error_collector
import glob
import subprocess
import shivyc
import unittest


class MetaIntegrationTests(type):
    """Metaclass for creating integration tests."""

    def __new__(meta, name, bases, dct):
        """Create a class with the desired functions."""
        # Dictionary mapping test names to issues they are expected to raise.

        def generate_test(test_file_name):
            def test_function(self):
                # Read test parameters from test file
                with open(test_file_name) as f:
                    ret_val = 0
                    issues = []

                    for line in f.readlines():
                        ret_mark = "// Return:"
                        issue_mark = "// Issue:"

                        if line.strip().startswith(ret_mark):
                            ret_val = int(line.split(ret_mark)[-1])
                        elif line.strip().startswith(issue_mark):
                            issues.append(line.split(issue_mark)[-1])

                # Mock out arguments to ShivyC call
                class MockArguments:
                    filename = test_file_name
                    show_il = False

                shivyc.get_arguments = lambda: MockArguments()

                # Mock out error collector functions
                error_collector.show = lambda: True

                shivyc.main()

                if not issues:
                    self.assertEqual(error_collector.issues, [])
                    self.assertEqual(subprocess.call(["./out"]), ret_val)
                else:
                    self.assertEqual(len(issues), len(error_collector.issues))
                    for issue, actual in zip(issues, error_collector.issues):
                        self.assertIn(issue.strip(), str(actual))

            return test_function

        test_file_names = glob.glob("tests/ctests/*.c")
        for test_file_name in test_file_names:
            short_name = test_file_name.split("/")[-1][:-2]
            test_func_name = "test_" + short_name
            dct[test_func_name] = generate_test(test_file_name)

        return super().__new__(meta, name, bases, dct)


class IntegrationTests(unittest.TestCase, metaclass=MetaIntegrationTests):
    """Integration tests for the compiler.

    All these tests are generated by the metaclass above.

    """

    def setUp(self):
        """Clear error collector before each test."""
        error_collector.clear()

if __name__ == "__main__":
    unittest.main()

@@ -2229,58 +2229,53 @@
     
-self.assertEqual(len(issues), len(error_collector.
+clean_issues = list(map(lambda x: x.strip(), 
 issu
@@ -2279,16 +2279,17 @@
 ssues))%0A
+%0A
         
@@ -2305,39 +2305,164 @@
    f
-or issue, actual in zip(issues,
+ront_strip = len(test_file_name) + 1%0A                    clean_actual = list(map(lambda x: str(x)%5Bfront_strip:%5D,%0A                                           
  err
@@ -2481,22 +2481,19 @@
 .issues)
-:%0A    
+)%0A%0A
         
@@ -2519,38 +2519,45 @@
 sert
-In(issue.strip(), str(
+ListEqual(clean_issues, clean_
 actual)
-)
 %0A%0A  

Improve error testing assertions