From da38cad23d3b6034720bc8d0283e2f2ea5d2aae7 Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Tue, 16 Jun 2026 14:35:52 +0200 Subject: [PATCH] feat(tests): add generator tester --- midas/generator/generator.py | 26 +++++++++++------ tests/base.py | 6 +++- tests/generator.py | 55 ++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 tests/generator.py diff --git a/midas/generator/generator.py b/midas/generator/generator.py index 0c086d8..a2eb5c2 100644 --- a/midas/generator/generator.py +++ b/midas/generator/generator.py @@ -2,6 +2,7 @@ import ast import shutil from dataclasses import dataclass, field from pathlib import Path +from typing import Optional from midas.ast.location import Location import midas.ast.python as p @@ -44,21 +45,28 @@ class Generator(p.Stmt.Visitor[ast.stmt], p.Expr.Visitor[ast.expr]): self._alias_count: int = 0 self._scopes: list[Scope] = [] - def generate(self, typed_ast: TypedAST, src_path: Path) -> Path: + def generate_ast(self, typed_ast: TypedAST, src_path: Path) -> ast.AST: self.rel_src_path = src_path.relative_to(self.workdir) self._typed_ast = typed_ast body: list[ast.stmt] = self._visit_body(typed_ast.stmts) module = ast.Module(body=body, type_ignores=[]) module = ast.fix_missing_locations(module) + return module + + def generate( + self, typed_ast: TypedAST, src_path: Path, out_path: Optional[Path] = None + ) -> Path: + module: ast.AST = self.generate_ast(typed_ast, src_path) compiled: str = ast.unparse(module) - out_path: Path = (self.build_dir / self.rel_src_path).resolve() - try: - _ = out_path.relative_to(self.build_dir) - except ValueError: - raise ValueError( - f"Directory traversal, {self.rel_src_path} points outside of parent directory" - ) - out_path.parent.mkdir(parents=True, exist_ok=True) + if out_path is None: + out_path = (self.build_dir / self.rel_src_path).resolve() + try: + _ = out_path.relative_to(self.build_dir) + except ValueError: + raise ValueError( + f"Directory traversal, {self.rel_src_path} points outside of parent directory" + ) + out_path.parent.mkdir(parents=True, exist_ok=True) out_path.write_text(compiled) return out_path diff --git a/tests/base.py b/tests/base.py index 0749d79..c8bad17 100644 --- a/tests/base.py +++ b/tests/base.py @@ -21,6 +21,10 @@ class Tester(ABC): @abstractmethod def namespace(self) -> str: ... + @property + def extension(self) -> str: + return "json" + @property def base_dir(self) -> Path: return self.CASES_DIR / self.namespace @@ -99,7 +103,7 @@ class Tester(ABC): return True def _result_path(self, test_path: Path) -> Path: - return test_path.parent / (test_path.name + ".ref.json") + return test_path.parent / (test_path.name + f".ref.{self.extension}") def _print_diff(self, diff: Iterator[str]): for line in diff: diff --git a/tests/generator.py b/tests/generator.py new file mode 100644 index 0000000..72b7002 --- /dev/null +++ b/tests/generator.py @@ -0,0 +1,55 @@ +import ast +from dataclasses import dataclass +from pathlib import Path + +from midas.checker.checker import TypeChecker +from midas.checker.diagnostic import DiagnosticType +from midas.generator.generator import Generator +from midas.utils import TypedAST +from tests.base import Tester + + +@dataclass +class CaseResult: + compiled_ast: ast.AST = ast.Module([], []) + + def dumps(self) -> str: + return ast.dump(self.compiled_ast, indent=2) + + +class GeneratorTester(Tester): + @property + def namespace(self) -> str: + return "generator" + + @property + def extension(self) -> str: + return "txt" + + def _list_tests(self) -> list[Path]: + return list(self.base_dir.rglob("*.py")) + + def _exec_case(self, path: Path) -> CaseResult: + if not path.exists(): + raise FileNotFoundError(f"Could not find test '{path}'") + if not path.is_file(): + raise TypeError(f"Test '{path}' is not a file") + + result: CaseResult = CaseResult() + + checker = TypeChecker() + types_path: Path = path.with_suffix(".midas") + if types_path.exists(): + checker.import_midas(types_path) + + typed_ast: TypedAST = checker.type_check(path) + + if not any(d.type == DiagnosticType.ERROR for d in checker.diagnostics): + generator = Generator(workdir=path.parent) + result.compiled_ast = generator.generate_ast(typed_ast, path) + + return result + + +if __name__ == "__main__": + GeneratorTester.main()