Module crashtest.run
Functions to run tests, collect results, and produce run reports.
Functions
def file_to_name(file: str) ‑> str
-
Expand source code
def file_to_name(file: str) -> str: return " ".join( re.sub("([A-Z][a-z]+)", r" \1", re.sub("([A-Z]+)", r" \1", file.replace(".hx", "").replace("_", " "))).split() ).title()
def gen_id() ‑> str
-
Expand source code
def gen_id() -> str: """ Generate a unique ID for a test run. """ return datetime.datetime.now().strftime("%Y%m%d%H%M%S")
Generate a unique ID for a test run.
def gen_status(results: List[TestCase]) ‑> Tuple[str, str]
-
Expand source code
def gen_status(results: List[TestCase]) -> Tuple[str, str]: """ Generate a status message and color based on test results. Returns a tuple of (status_message, color_hex). Colors: - Green (#22C55E): All tests passed - Yellow (#EAB308): < 10% failures - Orange (#F97316): 10-20% failures - Red-Orange (#EF4444): 20-50% failures - Red (#DC2626): > 50% failures - Dark Red (#991B1B): All tests failed """ if not results: return "No Tests Run", "#6B7280" # Gray for no tests total = len(results) failed = sum(1 for case in results if case.failed) failure_rate = (failed / total) * 100 if failed == 0: return "All tests passed", "#22C55E" elif failed == total: return "All tests failed", "#991B1B" else: if failure_rate < 10: return f"Partial failure ({failure_rate:.1f}%)", "#EAB308" elif failure_rate < 20: return f"Partial failure ({failure_rate:.1f}%)", "#F97316" elif failure_rate < 50: return f"Major failures ({failure_rate:.1f}%)", "#EF4444" else: return f"Critical failures ({failure_rate:.1f}%)", "#DC2626"
Generate a status message and color based on test results. Returns a tuple of (status_message, color_hex).
Colors: - Green (#22C55E): All tests passed - Yellow (#EAB308): < 10% failures - Orange (#F97316): 10-20% failures - Red-Orange (#EF4444): 20-50% failures - Red (#DC2626): > 50% failures - Dark Red (#991B1B): All tests failed
def get_repo_info() ‑> GitInfo
-
Expand source code
def get_repo_info() -> GitInfo: """ Get the git branch and commit hash, if available. """ try: original_dir = os.getcwd() script_dir = os.path.dirname(os.path.abspath(__file__)) os.chdir(script_dir) try: branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).strip().decode("utf-8") commit = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip().decode("utf-8") dirty = subprocess.check_output(["git", "status", "--porcelain"]).strip().decode("utf-8") != "" return GitInfo( is_release=False, branch=branch, commit=commit[:8], dirty=dirty, github=f"https://github.com/N3rdL0rd/crashlink/commit/{commit}", ) finally: os.chdir(original_dir) except (subprocess.CalledProcessError, FileNotFoundError): return GitInfo(is_release=True, dirty=False)
Get the git branch and commit hash, if available.
def run() ‑> None
-
Expand source code
def run() -> None: """ Run all tests. """ print("Getting repo info...") git = get_repo_info() if git.is_release: print( "Cannot run tests from a release build (eg. installed fro PyPI). Please clone the repo and run from there." ) return # TODO: add support for autodownloading and building test samples print("Finding test cases...") files = os.listdir(os.path.join(os.path.dirname(__file__), "..", "tests", "haxe")) cases = [f for f in files if f.endswith(".hx")] for case in cases: if not case.replace(".hx", ".hl") in files: print(f"Warning: no compiled bytecode found for {case}. Skipping.") cases.remove(case) print("Running tests...") results = [] for i, case in enumerate(cases): print(f"Running {case}...") result = run_case(case, i) results.append(result) print("Generating run...") status, status_color = gen_status(results) r = Run( git=git, context=TestContext(version=globals.VERSION), cases=results, id=gen_id(), timestamp=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), status=status, status_color=status_color, ) os.makedirs(os.path.join(os.path.dirname(__file__), "runs"), exist_ok=True) save_run(r, os.path.join(os.path.dirname(__file__), "runs", f"{gen_id()}.json"))
Run all tests.
def run_case(case: str, id: int) ‑> TestCase
-
Expand source code
def run_case(case: str, id: int) -> TestCase: """ Runs a single test case. """ try: code = Bytecode.from_path( os.path.join(os.path.dirname(__file__), "..", "tests", "haxe", case.replace(".hx", ".hl")) ) irf = decomp.IRFunction(code, code.get_test_main()) # TODO: pseudo output return TestCase( original=TestFile( name=case, content=open(os.path.join(os.path.dirname(__file__), "..", "tests", "haxe", case), "r").read(), ), decompiled=TestFile( name=f"{case.replace('.hx', '')} (Decompiled)", content="Failed to produce pseudocode." ), ir=TestFile(name=f"{case.replace('.hx', '')} (IR)", content=str(irf.block)), failed=False, test_name=file_to_name(case), test_id=id, ) except Exception as e: return TestCase( original=TestFile( name=case, content=open(os.path.join(os.path.dirname(__file__), "..", "tests", "haxe", case), "r").read(), ), decompiled=TestFile( name=f"{case.replace('.hx', '')} (Decompiled)", content="Failed to produce pseudocode." ), ir=TestFile(name=f"{case.replace('.hx', '')} (IR)", content="Failed to produce IR."), failed=True, test_name=case.replace(".hx", "").replace("_", " ").title(), test_id=file_to_name(case), error=str(e), )
Runs a single test case.