2024-07-17 20:29:51 -06:00
|
|
|
import argparse
|
|
|
|
import datetime
|
|
|
|
import difflib
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import tempfile
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description="Process some integers.")
|
2024-07-17 20:58:50 -06:00
|
|
|
parser.add_argument(
|
|
|
|
"src", default="src", type=Path, help="src directory", nargs="?"
|
|
|
|
)
|
2024-07-17 20:29:51 -06:00
|
|
|
parser.add_argument(
|
|
|
|
"tests", default="tests", type=Path, help="tests directory", nargs="?"
|
|
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
|
|
|
|
def make_log_file():
|
|
|
|
time = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
|
|
|
return (args.tests / time).with_suffix(".log")
|
|
|
|
|
|
|
|
|
|
|
|
def log_new_test(in_path):
|
|
|
|
global LOG_FILE
|
|
|
|
with open(LOG_FILE, "a") as f:
|
|
|
|
f.write("# ======================================\n")
|
|
|
|
f.write(f"# Test: {in_path}\n")
|
|
|
|
f.write("# ======================================\n")
|
|
|
|
|
|
|
|
|
|
|
|
def log_message(message):
|
|
|
|
global LOG_FILE
|
|
|
|
with open(LOG_FILE, "a") as f:
|
|
|
|
f.write(message)
|
|
|
|
f.write("\n")
|
|
|
|
|
|
|
|
|
|
|
|
def log_return(ret):
|
|
|
|
if ret[0]:
|
|
|
|
log_message("Passed")
|
|
|
|
else:
|
|
|
|
log_message(f"Error: {ret[1]}")
|
|
|
|
|
|
|
|
log_message("# ======================================\n")
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
def attempt_decode(out):
|
|
|
|
try:
|
|
|
|
return out.decode("utf-8")
|
|
|
|
except UnicodeDecodeError:
|
|
|
|
return "Invalid utf-8 in output"
|
|
|
|
|
|
|
|
|
|
|
|
def diff_files(file1, file2):
|
|
|
|
with open(file1, "r") as f1, open(file2, "r") as f2:
|
|
|
|
diff = difflib.unified_diff(
|
|
|
|
f1.readlines(),
|
|
|
|
f2.readlines(),
|
|
|
|
fromfile=str(file1),
|
|
|
|
tofile=str(file2),
|
|
|
|
)
|
|
|
|
return "".join(diff)
|
|
|
|
|
|
|
|
|
|
|
|
def run_test(exe_path, in_path):
|
|
|
|
expected_file = in_path.with_suffix(".out")
|
|
|
|
log_new_test(in_path)
|
|
|
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
|
|
temp = Path(tmp)
|
|
|
|
out_path = temp / "output"
|
|
|
|
|
|
|
|
test = subprocess.Popen(
|
|
|
|
[sys.executable, str(exe_path), str(in_path), "--out", str(out_path)],
|
|
|
|
stdout=subprocess.DEVNULL,
|
|
|
|
stderr=subprocess.PIPE,
|
|
|
|
)
|
|
|
|
|
|
|
|
try:
|
|
|
|
_, stderr = test.communicate(1)
|
|
|
|
except subprocess.TimeoutExpired:
|
|
|
|
return [False, "Timed out"]
|
|
|
|
|
|
|
|
if test.returncode != 0:
|
|
|
|
log_message(attempt_decode(stderr))
|
|
|
|
return log_return(
|
|
|
|
[
|
|
|
|
False,
|
|
|
|
f"Expected returncode 0 got: {test.returncode}",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
diff = diff_files(expected_file, out_path)
|
|
|
|
|
|
|
|
if diff != "":
|
|
|
|
log_message(diff)
|
|
|
|
return log_return([False, "Output did not match"])
|
|
|
|
|
|
|
|
return log_return([True])
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
LOG_FILE = make_log_file()
|
|
|
|
PASS_MESSAGE = "✅ Passed"
|
|
|
|
ERROR_MESSAGE = "❌ Error:"
|
|
|
|
|
|
|
|
tests = list(Path(args.tests).glob("**/*.in"))
|
|
|
|
max_len = max(len(str(t)) for t in tests)
|
|
|
|
|
|
|
|
for test in tests:
|
|
|
|
print(f"{test}...", end="")
|
|
|
|
res = run_test(args.src / "main.py", test)
|
|
|
|
|
|
|
|
if res[0]:
|
|
|
|
dots = 80 - len(str(test)) - 3 - len(PASS_MESSAGE)
|
|
|
|
|
|
|
|
if dots > 0:
|
|
|
|
print("." * dots, end="")
|
|
|
|
print(PASS_MESSAGE)
|
|
|
|
else:
|
|
|
|
dots = 80 - len(str(test)) - 3 - len(ERROR_MESSAGE) - len(res[1])
|
|
|
|
|
|
|
|
if dots > 0:
|
|
|
|
print("." * dots, end="")
|
|
|
|
print(f"{ERROR_MESSAGE} {res[1]}")
|
|
|
|
|
|
|
|
print(f"Log file at: {LOG_FILE}")
|