Skip to content

Instantly share code, notes, and snippets.

@YtvwlD
Last active May 12, 2019 09:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save YtvwlD/283076ba3145483d9bbc1c4027df82fa to your computer and use it in GitHub Desktop.
Save YtvwlD/283076ba3145483d9bbc1c4027df82fa to your computer and use it in GitHub Desktop.
simple script to run tests for the nand2tetris course
#!/usr/bin/env python3
from pathlib import Path
from subprocess import run, PIPE, TimeoutExpired
from sys import argv
from collections import namedtuple
from queue import Queue
from typing import Iterator, List, Optional, Tuple, Union
import huepy
ERASE_LINE = '\x1b[2K'
RUN_TIMEOUT = 5 # Sekunden
TestCase = namedtuple("TestCase", ["name"])
TestResult = namedtuple("TestResult", ["name", "ok", "msg"])
def _run_proc(cmd: List[str]) -> Tuple[Optional[bool], str]:
try:
proc = run(cmd, stdout=PIPE, stderr=PIPE, timeout=RUN_TIMEOUT)
except TimeoutExpired:
return None, "timeout"
return (
proc.returncode == 0,
(proc.stdout.decode() + proc.stderr.decode()).strip()
)
def test_hdl(name: str) -> TestResult:
ok, msg = _run_proc(
["/bin/sh", "../tools/HardwareSimulator.sh", f"{name}.tst"]
)
return TestResult(name, ok, msg)
def test_asm(name: str) -> TestResult:
assemble_proc_ok, assemble_proc_msg = _run_proc(
["/bin/sh", "../tools/Assembler.sh", f"{name}.asm"],
)
if not assemble_proc_ok:
return TestResult(name, assemble_proc_ok, assemble_proc_msg)
test_proc_ok, test_proc_msg = _run_proc(
["/bin/sh", "../tools/CPUEmulator.sh", f"{name}.tst"],
)
return TestResult(name, test_proc_ok, test_proc_msg)
def test_file(file: Path) -> Iterator[Union[TestCase, TestResult]]:
assert file.is_file()
assert isinstance(file, Path)
name = str(file)[:-4]
yield TestCase(name)
for line in file.read_text().splitlines():
if line.startswith("load"):
try:
filename = line.split(" ")[1][:-1];
file_ = file.with_name(filename)
except:
yield TestResult(
name,
False,
f"Failed to parse: {line}"
)
continue
if not file_.exists():
yield TestResult(
name,
False,
f"{file_.name} is missing."
)
continue
assert file_.is_file()
if str(file_).endswith("hdl"):
yield test_hdl(name)
elif str(file_).endswith("hack"):
if file_.with_suffix(".asm").exists():
yield test_asm(name)
else:
raise NotImplementedError
elif str(file_).endswith("asm"):
yield test_asm(name)
else:
yield None
# TODO: other file types
def print_test(test: Union[TestCase, TestResult]) -> None:
if isinstance(test, TestCase):
print(ERASE_LINE, end="")
print(huepy.run(test.name), end="\r")
else:
assert isinstance(test, TestResult)
if test.ok:
print(huepy.good(test.name))
elif test.ok is None:
print(huepy.info(f"{test.name}: {test.msg}"))
else:
print(huepy.bad("{name}: {msg}".format(**test._asdict())))
def patch_tools_sh(file: Path) -> None:
# Sorry, aber das muss sein, sonst funktioniert timeout nicht.
assert isinstance(file, Path)
assert file.is_file()
file.write_text(file.read_text().replace("\tjava", "\t exec java"))
for file in Path("../tools").iterdir():
if not str(file).endswith(".sh"):
continue
patch_tools_sh(file)
results = list()
path = Path(argv[1])
if path.is_file():
test = test_file(path)
print_test(next(test))
results.append(next(test))
print_test(results[-1])
else:
assert path.exists()
assert path.is_dir()
print(huepy.run("Scanning folders..."), end="\r")
queue = Queue() # type: Queue[Path]
files = list() # type: List[Path]
for file in path.iterdir():
queue.put(file)
while not queue.empty():
file = queue.get()
if file.is_dir():
for file_ in file.iterdir():
queue.put(file_)
continue
if file.suffix == ".tst":
files.append(file)
files.sort()
print(ERASE_LINE, end="")
print(huepy.good(f"Found {len(files)} tests."))
for file in files:
test = test_file(file)
print_test(next(test))
result = next(test)
if result:
results.append(result)
print_test(result)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment