//! Sylpheed boot-sequence regression oracles. //! //! These goldens trigger `xenia-rs check` against the Project Sylpheed ISO and //! compare the resulting digest to a checked-in JSON file via `--stable-digest`, //! which excludes timing-sensitive counters (`packets`, `interrupts_*`, //! `resolves`, `texture_decodes`). The remaining fields are deterministic in //! lockstep at a fixed instruction budget — verified empirically across 3 //! consecutive runs. //! //! Goldens are CIRCULAR per ORACBUG-001/002/003: they were captured by running //! the same code they validate. Treat them as **regression anchors** (catch //! drift from a known-good snapshot) not **correctness anchors** (no claim //! about absolute behavior). When a planned fix intentionally moves the //! digest (e.g. swap fix → `swaps` increments; renderer fix → `draws` becomes //! non-zero), re-baseline the golden as a separate commit. //! //! Tests are `#[ignore]`-gated because the runs take ~4 seconds each, which //! is unacceptable for the default `cargo test` cycle. Run explicitly: //! cargo test --release -p xenia-app --test sylpheed_oracles -- --ignored --nocapture //! //! ISO path is read from the `SYLPHEED_ISO` env var, falling back to the //! repo-relative default. CI/contributors without the ISO will see the test //! skip gracefully. use std::process::Command; const ISO_DEFAULT: &str = "/home/fabi/RE Project Sylpheed/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso"; fn iso_path() -> String { std::env::var("SYLPHEED_ISO").unwrap_or_else(|_| ISO_DEFAULT.to_string()) } fn run_oracle(label: &str, max_instr: u64, golden_rel: &str) { let bin = env!("CARGO_BIN_EXE_xenia-rs"); let iso = iso_path(); if !std::path::Path::new(&iso).exists() { eprintln!("{label}: iso not found at {iso}; set SYLPHEED_ISO to override. SKIPPING."); return; } // Resolve the golden path relative to the test's CARGO_MANIFEST_DIR so the // test runs correctly from any cwd. let manifest_dir = env!("CARGO_MANIFEST_DIR"); let golden = std::path::Path::new(manifest_dir).join(golden_rel); assert!( golden.exists(), "{label}: golden file missing at {}", golden.display() ); let max_instr_str = max_instr.to_string(); let golden_str = golden.to_string_lossy().to_string(); let out = Command::new(bin) .args([ "check", &iso, "-n", &max_instr_str, "--stable-digest", "--expect", &golden_str, ]) .output() .expect("failed to spawn xenia-rs"); if !out.status.success() { eprintln!( "{label}: STDOUT:\n{}\nSTDERR:\n{}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr), ); panic!("{label}: digest mismatch (exit {:?})", out.status.code()); } } /// Sylpheed boot to first VdSwap pair, captured at -n 50M lockstep. /// Catches regressions in: addi/addic semantics, kernel HLE for VdSwap path, /// thread spawning, file I/O for sound/config. With Phase A's swap fix landed, /// `swaps` should be 2 and `draws` 0 (Phase E gates draws>0). #[test] #[ignore = "long-running; run via `cargo test ... -- --ignored sylpheed_n50m`"] fn sylpheed_n50m() { run_oracle("sylpheed_n50m", 50_000_000, "tests/golden/sylpheed_n50m.json"); }