#!/usr/bin/env python3 """Unabhängige Soll-Gegenproben; Produktcode und bestehende Tests bleiben unverändert.""" from pathlib import Path import os import subprocess import tempfile ROOT = next(p for p in Path(__file__).resolve().parents if (p / "Cargo.toml").is_file() and (p / "openspec").is_dir()) TBC = ROOT / "target/debug/tbc" def run(*args, **kwargs): return subprocess.run(args, capture_output=True, text=True, timeout=60, **kwargs) def main(): subprocess.run(["cargo", "build", "-p", "tb-cli"], cwd=ROOT, check=True) failed = [] def check(name, ok, evidence): print(f"{'PASS' if ok else 'FAIL'} {name}: {evidence}") if not ok: failed.append(name) with tempfile.TemporaryDirectory(prefix="tb-review-") as tmp: tmp = Path(tmp) for name, source in [("PSET", "PSET (1,1),2"), ("PALETTE", "DIM a%(15)\nPALETTE USING a%(0)")]: path = tmp / "graphics.bas" path.write_text(source + "\n") out = run(str(TBC), "check", str(path)) check(name + " Non-Feature", out.returncode != 0 and "Feature unavailable" in out.stderr and name in out.stderr, out.stderr.strip()) path = tmp / "parent.frm" path.write_text('''VERSION 1.00 Begin Form Form1 Begin Frame Group Index = 0 End Begin Frame Group Index = 1 Begin TextBox Probe End End End DIM p AS CONTROL p=Probe.Parent Form1.Hide PRINT p.Index END ''') out = run(str(TBC), "run", str(path)) check("Indizierter Parent aus FRM", out.returncode == 0 and out.stdout.strip() == "1", f"Soll=1, Ist={out.stdout!r}, {out.stderr}") build = run(str(TBC), "build", str(path)) assert build.returncode == 0, build.stderr path.unlink() out = run(str(TBC), "run", str(path.with_suffix(".tbc"))) check("Indizierter Parent aus TBC ohne Quelle", out.returncode == 0 and out.stdout.strip() == "1", f"Soll=1, Ist={out.stdout!r}, {out.stderr}") # Kopie im Temp-Verzeichnis erlaubt Zugriff auf den privaten Prüfer. # Einzige Mutation: TIMEZONEKNOWN zeigt auf die gültige TIMER-Bindung. source = (ROOT / "crates/tb-cli/tests/inventar.rs").read_text() source = source.replace('include_str!("../../../tests/support/inventar-quellen.tsv")', f'include_str!("{ROOT}/tests/support/inventar-quellen.tsv")') source += r''' #[test] fn review_falsches_laufzeitziel_muss_abgewiesen_werden() { let e=inventar().into_iter().find(|e|e.name=="TIMEZONEKNOWN").unwrap(); let (src,catalog)=probe_source(&e); let mut module=tb_vm::compile_source_with_forms("FORM1",&src,&catalog).unwrap(); let mut changed=false; for i in module.procs.iter_mut().flat_map(|p|&mut p.code) { if let tb_vm::bytecode::Instr::CallBuiltin(id,_) = i { if *id==tb_runtime::builtins::ids::TIMEZONEKNOWN { *id=tb_runtime::builtins::ids::TIMER; changed=true; } } } assert!(changed); let result=pruefe_kompilat(&e,Ok(module),|id|tb_runtime::builtins::builtin_table().get(id as usize).is_some()); assert!(result.is_err(),"TIMEZONEKNOWN mit TIMER-Ziel wird akzeptiert: {result:?}"); } ''' probe = tmp / "inventar.rs" probe.write_text(source) # Cargo resolves a coherent dependency graph; selecting rlibs by mtime # can mix feature variants after a workspace test run. source = source.replace('env!("CARGO_MANIFEST_DIR")', f'"{ROOT}/crates/tb-cli"') probe.write_text(source) manifest = '[package]\nname = "tb-review-probes"\nversion = "0.0.0"\nedition = "2021"\n[workspace]\n' manifest += '[[test]]\nname = "inventar"\npath = "inventar.rs"\n[dependencies]\n' for name in ["tb-vm", "tb-frontend", "tb-runtime"]: manifest += f'{name} = {{ path = "{ROOT}/crates/{name}" }}\n' (tmp / "Cargo.toml").write_text(manifest) env = os.environ.copy() env["CARGO_BIN_EXE_tbc"] = str(TBC) out = run("cargo", "test", "--manifest-path", str(tmp / "Cargo.toml"), "--offline", "--target-dir", str(ROOT / "target/review-probes"), "--test", "inventar", "review_falsches", "--", "--nocapture", env=env) check("Inventar erkennt falsches Laufzeitziel", out.returncode == 0, out.stdout + out.stderr) print(f"{len(failed)} fehlgeschlagene Soll-Gegenproben") return bool(failed) if __name__ == "__main__": raise SystemExit(main())