From bbc55a309941f5176a9c8f5525b76815f6258bec Mon Sep 17 00:00:00 2001 From: dogeystamp Date: Thu, 2 Nov 2023 16:02:06 -0400 Subject: [PATCH] basic formatting of results --- README.md | 6 ++++++ testr.py | 18 ++++++++++++++++-- testr/file_data.py | 13 +++++++------ testr/runner.py | 35 +++++++++++++++++++++-------------- 4 files changed, 50 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 9476884..522ac40 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ # test runner testr is a script for running competitive programming test cases (for example for CodeForces). + +## dependencies + +You can install all of these via Arch Linux's repositories: + +- colorama diff --git a/testr.py b/testr.py index 4cec847..b659378 100644 --- a/testr.py +++ b/testr.py @@ -1,7 +1,10 @@ +from colorama import just_fix_windows_console, Fore, Style +just_fix_windows_console() + import asyncio from pathlib import Path from testr.file_data import DirectorySuite, ExecutableRunner -from testr.runner import TestOptions +from testr.runner import StatusCode, TestOptions import argparse parser = argparse.ArgumentParser() @@ -17,6 +20,14 @@ parser.add_argument("--timelim", help="Time limit in seconds", type=float, defau args = parser.parse_args() +code_styles = { + StatusCode.AC: Fore.GREEN, + StatusCode.WA: Fore.RED, + StatusCode.IR: Fore.YELLOW, + StatusCode.TLE: Fore.YELLOW, +} + + async def main(): test_suite = DirectorySuite(Path(args.testdir)) test_runner = ExecutableRunner(Path(args.exec)) @@ -27,7 +38,10 @@ async def main(): time_limit=args.timelim ) ): - print(test_case.code) + print( + f"{test_case.test_data.name : <20} " + f"{code_styles.get(test_case.code, '')}{test_case.code.name : >4}{Style.RESET_ALL}" + ) if __name__ == "__main__": diff --git a/testr/file_data.py b/testr/file_data.py index 1f62ece..b7fc129 100644 --- a/testr/file_data.py +++ b/testr/file_data.py @@ -6,13 +6,14 @@ from pathlib import Path class FileData(TestData): """Backend to parse test data from files.""" - def __init__(self, input_file: Path, output_file: Path): + def __init__(self, input_file: Path, output_file: Path, name: str): if not input_file.is_file(): raise ValueError(f"input_file must be a file, got '{input_file}'") if not output_file.is_file(): raise ValueError(f"output_file must be a file, got '{output_file}'") self.input_file = input_file self.output_file = output_file + self.name = name async def get_input(self) -> str: with open(self.input_file, "r") as f: @@ -45,17 +46,17 @@ class ExecutableRunner(TestRunner): proc.communicate(input=input_data.encode()), timeout=opts.time_limit) except TimeoutError: proc.kill() - return TestStatus(code=StatusCode.TLE, stderr="", stdout="", stdin=input_data) + return TestStatus(code=StatusCode.TLE, stderr="", stdout="", test_data = data) stdout: str = out_stream.decode() stderr: str = err_stream.decode() if proc.returncode != 0: - return TestStatus(code=StatusCode.IR, stdout=stdout, stderr=stderr, stdin=input_data) + return TestStatus(code=StatusCode.IR, stdout=stdout, stderr=stderr, test_data=data) correct: bool = await data.validate_output(stdout) ret_code = StatusCode.AC if correct else StatusCode.WA - return TestStatus(code=ret_code, stdout=stdout, stderr=stderr, stdin=input_data) + return TestStatus(code=ret_code, stdout=stdout, stderr=stderr, test_data=data) class DirectorySuite(TestSuite): @@ -66,13 +67,13 @@ class DirectorySuite(TestSuite): if not test_dir.is_dir(): raise ValueError(f"test_dir must be a directory, got '{test_dir}'") - for inp_file in test_dir.glob("*.in"): + for inp_file in sorted(test_dir.glob("*.in")): if not inp_file.is_file: continue outp_file = inp_file.with_suffix(".out") if not outp_file.is_file(): raise ValueError(f"output file '{outp_file}' is not a valid file") - self.test_cases.append(FileData(inp_file, outp_file)) + self.test_cases.append(FileData(inp_file, outp_file, name=inp_file.name)) def __iter__(self): diff --git a/testr/runner.py b/testr/runner.py index cbc4cb5..9f93e89 100644 --- a/testr/runner.py +++ b/testr/runner.py @@ -27,18 +27,6 @@ class StatusCode(Enum): """ -@dataclass -class TestStatus: - """ - Status of an individual test case. - """ - - code: StatusCode - stdin: str - stderr: str - stdout: str - - class TestInput(ABC): """Input provider for single test case.""" @@ -69,9 +57,14 @@ class TestOptions: class TestData(TestInput, TestValidator): - """Combined input/output for single test case""" + """ + All information for a single test case. - pass + name + Short name for the test case. Can be a file name. + """ + + name: str class TestSuite(ABC): @@ -81,6 +74,20 @@ class TestSuite(ABC): def __iter__(self) -> Iterator[TestData]: pass +@dataclass +class TestStatus: + """ + Status of an individual test case. + """ + + code: StatusCode + + test_data: TestData + + stderr: str + stdout: str + + class TestRunner(ABC): """Runner for test cases."""