musique/etc/tools/test.py

150 lines
4.1 KiB
Python
Executable File

#!/usr/bin/env python3
from dataclasses import dataclass
from glob import glob
from sys import argv
from sys import exit
import os.path
import shlex
import subprocess
import json
from unittest import case
Interpreter = "bin/musique"
def directories_in_path(path: str):
dirs = []
while True:
dirname, _ = os.path.split(path)
if not dirname:
break
dirs.append(dirname)
path = dirname
if path == "/":
break
dirs.reverse()
return dirs
def mkdir_recursive(path: str):
for directory in directories_in_path(path):
try:
os.mkdir(directory)
except FileExistsError:
continue
@dataclass
class Test_Case:
returncode = 0
@staticmethod
def from_file(fname : str):
with open(fname) as f:
content = json.load(f)
tc = Test_Case()
for name in content:
# assert hasattr(tc, name), "Test_Case does not have attribute %s" % (name,)
setattr(tc, name, content[name])
return tc
@staticmethod
def from_run(run, flags=[]):
tc = Test_Case()
for attr in ["returncode", "stdout", "stderr"]: ### TODO FLAGS
try:
run_attr = getattr(run, attr).decode()
except (UnicodeDecodeError, AttributeError):
run_attr = getattr(run, attr)
setattr(tc, attr, run_attr)
setattr(tc, "flags", flags)
return tc
def save(self, fname : str):
j = {}
for attr in ["returncode", "stdout", "stderr", "flags"]:
j[attr] = getattr(self, attr)
mkdir_recursive(fname)
with open(fname, 'w') as f:
json.dump(j, f, indent=4)
def cmd_run_echoed(cmd, **kwargs):
print("[CMD] %s" % " ".join(map(shlex.quote, cmd)))
return subprocess.run(cmd, **kwargs)
def find_path_for_test_case(path: str) -> str:
directory, filename = os.path.split(path)
return (directory if directory else ".") + "/.tests_cache/" + filename + ".json"
def run_tests(file_paths: list):
return_code = 0
for program_file in file_paths:
test_case_file = find_path_for_test_case(program_file)
if os.path.exists(test_case_file):
tc = Test_Case.from_file(test_case_file)
else:
continue
flags_list = [Interpreter]
if hasattr(tc, "flags"):
flags_list.extend(tc.flags)
flags_list.append(program_file)
res = cmd_run_echoed(flags_list, capture_output=True)
for attr in [a for a in dir(tc) if a in ["returncode", "stdout", "stderr"]]:
tc_attr = getattr(tc, attr)
res_attr = getattr(res, attr)
try:
res_attr = res_attr.decode()
except (UnicodeDecodeError, AttributeError):
pass
if tc_attr != res_attr:
print(f"[ERROR] Failed test {program_file}")
print(f"Expected {attr} = ")
print(tc_attr)
print(f"Received {attr} = ")
print(res_attr)
return_code = 1
exit(return_code)
def record_tests(file_paths: list):
for program_file in file_paths:
test_case_file = find_path_for_test_case(program_file)
res = cmd_run_echoed([Interpreter, program_file], capture_output=True)
tc = Test_Case.from_run(res, [])
tc.save(test_case_file)
# list of files to test
def main():
file_paths, mode = [], run_tests
if len(argv) < 2:
print("[ERROR] Expected mode argument (either 'record' or 'test')")
exit(1)
if argv[1] == "test":
mode = run_tests
elif argv[1] == "record":
mode = record_tests
else:
print(f"[ERROR] Unrecognized mode '{argv[1]}'")
exit(1)
if len(argv) < 3:
print("[ERROR] Expected test case")
exit(1)
for path in argv[2:]:
if os.path.exists(path):
if os.path.isdir(path):
file_paths.extend(glob(f"{path}/*.mq"))
else:
file_paths.append(path)
mode(file_paths)
if __name__ == "__main__":
main()