#!/usr/bin/python3
import unittest, subprocess, filecmp, argparse, os.path, os, shutil

REFERENCE_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), "tests")

REGEXES_DATA = [    # (regexes, should be accepted)
        ([r"[a-zA-Z0-9]*"], True),
        ([r"[a-dD-F9-0]*"], True),
        ([r"a", r"a|b"], True),
        ([r"a|b", r"a"], True),

        ([r"[[]", r"[]]", r"[]-[]", r"[][-]"], True),
        ([r"[^^]", r"[^]-[]", r"[^][-]"], True),
        ([r"[ ^]", r"[ ^-X ]", r"[]-^]"], True),
        ([r"[^-X]", r"[^][]"], True),
        ([r"[-]", r"[]-]"], True),
        ([r"[^-]", "[^]]", r"[^-]", r"[^-^]"], True),
        ([r"[^--]", r"[--]"], True),
        ([r"[^]"], False),
        ([r"[]"], False),

        ([r"\r", r"\n", r"\f", r"\v", r"\a", r"\t", r"\b", r"\s", r" "], True),
        ([r"\\", r"\*", r"\+", r"\|", r"\(", r"\)", r"\[", r"\[", r"\?", r"\."], True),

        ([r".", r"a*", r"a+", r"a?", r"(ab)*", r"a|b"], True),
        ([r"(a|b)*", r"(a|b)+", r"a**", r"a??", r"(ab)?"], True),
        ([r"("], False),
        ([r")"], False),
        ([r"["], False),
        ([r"]"], False),
        ([r"(test(a)qwerty"], False),
        ([r"test(a)qwerty)"], False),
        ([r"ab|"], False),
        ([r"|ab"], False),
        ([r"ab(c|)de"], False),
        ([r"ab(|c)de"], False),

        ([r""], False),
        ([r"()"], False),
        ([r"(?)"], False),
        ([r"((ab)c()de)"], False),
        ([r"|"], False),
        ([r"(|)"], False),
        ([r"?"], False),
        ([r"*"], False),
        ([r"(*)"], False),
        ([r"+"], False),
        ([r"(+)"], False),
        ]

EXAMPLE_TESTS = {   #Mapping from test name to executable and number of available test input files
        "keywords" : ("examples/keywords/keywords", 2),
        "highlighter": ("examples/SyntaxHighlighter/highlighter", 1),
        "leopard": ("examples/leopard/leopard", 8),
        }

def make_pipeline_test(regexes, should_accept, idx):
    def test(self):
        p = subprocess.Popen([os.path.join(args.builddir, "src", "Lexesis-test"), "test_%s" % idx, data_dir], stdin=subprocess.PIPE, stderr=subprocess.DEVNULL)
        p.communicate(bytes("\n".join(regexes) + "\n", "utf-8"))
    
        if should_accept:
            self.assertEqual(0, p.returncode)

            self.assertTrue(filecmp.cmp(os.path.join(REFERENCE_DIR, "test_%s.re" % idx), os.path.join(data_dir, "test_%s.re" % idx)), "The regex for testcase %s is incorrect" % idx)
            self.assertTrue(filecmp.cmp(os.path.join(REFERENCE_DIR, "test_%s.enfa" % idx), os.path.join(data_dir, "test_%s.enfa" % idx)), "The eNFA for testcase %s is incorrect" % idx)
            self.assertTrue(filecmp.cmp(os.path.join(REFERENCE_DIR, "test_%s.mssc" % idx), os.path.join(data_dir, "test_%s.mssc" % idx)), "The mssc'd DFA for testcase %s is incorrect" % idx)
            self.assertTrue(filecmp.cmp(os.path.join(REFERENCE_DIR, "test_%s.min" % idx), os.path.join(data_dir, "test_%s.min" % idx)), "The minimized DFA for testcase %s is incorrect" % idx)
        else:
            self.assertNotEqual(0, p.returncode)

    return test

def make_functional_test(name, prog, idx):
    def test(self):
        inpath = os.path.join(REFERENCE_DIR, "%s_%s.in" % (name, idx))
        infile = open(inpath)
        outpath = os.path.join(data_dir, "%s_%s.out" % (name, idx))
        outfile = open(outpath, "w")
        checkpath = os.path.join(REFERENCE_DIR, "%s_%s.exp" % (name, idx))

        p = subprocess.Popen([prog], stdin=infile, stdout=outfile)
        p.communicate()

        infile.close()
        outfile.close()

        self.assertTrue(filecmp.cmp(checkpath, outpath), "Testcase %s for example program %s failed" % (idx, name))

    return test

class Tests(unittest.TestCase):
    pass

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--builddir", metavar="builddir", default=os.path.join(os.path.abspath(os.path.dirname(__file__)), "build"), required=False)
    args = parser.parse_args()

    data_dir = os.path.join(args.builddir, "test_data")
    try:
        shutil.rmtree(data_dir)
    except Exception as err:
        pass
    os.mkdir(data_dir)

    for i, (regexes, should_accept) in enumerate(REGEXES_DATA):
        t = make_pipeline_test(regexes, should_accept, i)
        setattr(Tests, "test_%s" % i, t)

    for test, (prog, num) in EXAMPLE_TESTS.items():
        for i in range(num):
            t = make_functional_test(test, os.path.join(args.builddir, prog), i)
            setattr(Tests, "test_%s_%s" % (test, i), t)

    unittest.main()
