crashtest.behavior
Differential execution on the real HashLink runtime, never an emulated fallback.
Fixtures must expose return values, mutations and caught exceptions through output. Only terminating, repeatable observations can pass; unhandled errors, missing tools, output floods and timeouts fail closed. This is a corpus oracle, not equivalence proof for unexecuted branches or nondeterministic/interactive programs.
class BehavioralComparison
passed: bool
original: Execution
recompiled: Execution
error: str | None
error = None
runtime: str
runtime = 'HashLink'
exemption_reason: str | None
exemption_reason = None
to_json
def to_json() -> dict
from_json
classmethod
@classmethod
def from_json(data: dict) -> BehavioralComparison
init
def __init__(passed: bool, original: Execution, recompiled: Execution, error: str | None = None, runtime: str = 'HashLink', exemption_reason: str | None = None) -> None
class Execution
stdout: str
stdout = ''
stderr: str
stderr = ''
returncode: int | None
returncode = None
error: str | None
error = None
init
def __init__(stdout: str = '', stderr: str = '', returncode: int | None = None, error: str | None = None) -> None
compare_programs
def compare_programs(original: Path, recompiled: Path, class_name: str, timeout: float = 5.0) -> BehavioralComparison
compile_haxe
def compile_haxe(source: str, class_name: str, directory: Path) -> tuple[Path, str | None]
Compile one self-contained Haxe module for HashLink, retaining its artifact.
execute
def execute(command: list[str], cwd: str, timeout: float = 5.0) -> Execution
Run with no input, isolated cwd, bounded output, and a hard deadline.
normalize_positions
def normalize_positions(text: str, class_name: str) -> str
Strip source positions that only encode the module's own layout.
The fixture is compiled from tests/haxe/<Class>.hx and the decompiled
program from <Class>.hx in a sandbox, and the decompiled source has its
own line numbering, so neither trace prefixes (<Class>.hx:12: ) nor stack
frames (Called from $C.main(<Class>.hx:12)) are comparable. Everything
else — values, messages, frame order, function names — is.
resolve_hl_runtime
def resolve_hl_runtime() -> str | None
Find the HashLink runtime to execute against.
HL_RUNTIME (an absolute path) always wins when set, e.g. for CI or a
non-standard install. Otherwise, hl on PATH is used directly - no env
var required for the common case of a normal local HashLink install.