* test(shell): backend-lifecycle fault-injection harness Runs spawn_backend_and_wait/supervise_backend against REAL dying child processes and asserts the user receives the correct NAMED diagnosis — not merely that recovery happens. Wrong/missing explanation was 61% of the historical "can't reach the backend" class; this rig is the permanent regression harness for every future lifecycle fix. Seam: OMNIVOICE_BACKEND_CMD (JSON argv or whitespace form) runs any command as "the backend" — venv bootstrap and ffmpeg resolution are skipped, everything else (err-log run offsets, drainer threads, env pinning, real OS pipes, spawn-failure diagnostics) stays real. Plus OMNIVOICE_LOG_DIR (per-test log+marker dirs, also a support tool) and harness-only timing overrides OMNIVOICE_STARTUP_BUDGET_S / OMNIVOICE_SUPERVISOR_POLL_MS whose production defaults are pinned by unit tests. Lifecycle fns genericized over tauri::Runtime for the MockRuntime app; behavior-neutral with the env unset (unit-pinned). Scenarios (tests/backend_lifecycle.rs, scenario children = this test binary re-invoking itself; serial by mutex + CI --test-threads=1): - port conflict (exit 78) → the detectHints-matchable port phrasing - generic chained traceback → root cause survives into the diagnosis and the crash marker - spawn failure → spawn diagnostic reaches the user, NO bogus marker - slow start past budget → timeout names the budget + last stderr - post-Ready crash loop → 3 restarts announced, markers before restarts, "kept crashing" diagnosis naming the last exit - SIGKILL (unix) → named as signal 9 - deliberate kill → supervisor yields silently, no marker, never Failed - deferred-startup FATAL → the named step reaches the user, forensics, and the splash narration CI: harness added to the 3-OS tauri-cross-platform matrix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(ci): temporary Windows loader bisect probe for the harness binary Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(shell): embed Common-Controls v6 manifest into Windows test binaries Bisected on #1551: EVERY integration-test binary of this crate died at load on Windows with STATUS_ENTRYPOINT_NOT_FOUND (0xc0000139) — cargo gives test binaries no manifest, so the loader resolves comctl32 v5, which lacks the TaskDialogIndirect entry point tauri's dialog/tray stack imports. build.rs now embeds tests/windows-test.manifest via rustc-link-arg-tests on Windows targets. Bisect probe removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): scenario gate is PID-valued — the parent can't self-inject CodeRabbit on #1551: in a parallel local `cargo test`, the parent's own scenario_child test could observe the armed env and start playing the backend in-process (binding the port, idling 600s). The gate value is now the arming process's PID; a matching PID stays inert, so only the spawned child — a different process — runs the scenario. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
65 lines
3.0 KiB
Rust
65 lines
3.0 KiB
Rust
use std::path::PathBuf;
|
|
|
|
// Create a placeholder `binaries/<name>-<target-triple>` file if one doesn't
|
|
// already exist for the current target. Tauri's `bundle.externalBin`
|
|
// config is validated at every build (including `cargo check`), and it
|
|
// hard-errors when the source binary is missing — which it is in dev,
|
|
// because the real binaries are only fetched during release builds in CI.
|
|
// The placeholder is empty (zero bytes) and cannot actually be run;
|
|
// `find_bundled_*()` at runtime falls back to PATH or pip-bundled binaries
|
|
// when the bundled file isn't a real executable. CI overwrites these files
|
|
// with the real binaries before the tauri-action bundle step.
|
|
fn ensure_sidecar_placeholder(name: &str) {
|
|
let triple = std::env::var("TARGET").unwrap_or_default();
|
|
if triple.is_empty() {
|
|
return;
|
|
}
|
|
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into());
|
|
let binaries_dir = PathBuf::from(&manifest_dir).join("binaries");
|
|
let _ = std::fs::create_dir_all(&binaries_dir);
|
|
let suffix = if triple.contains("windows") { ".exe" } else { "" };
|
|
let target_path = binaries_dir.join(format!("{}-{}{}", name, triple, suffix));
|
|
if !target_path.exists() {
|
|
let _ = std::fs::write(&target_path, b"");
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
if let Ok(meta) = std::fs::metadata(&target_path) {
|
|
let mut perms = meta.permissions();
|
|
perms.set_mode(0o755);
|
|
let _ = std::fs::set_permissions(&target_path, perms);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
// backend.rs bakes the analytics destination in with option_env!, which cargo
|
|
// resolves at COMPILE time — so without these, a cached build would keep the
|
|
// token it was first compiled with (in practice: none), and the secret would
|
|
// appear to be ignored. Tell cargo the build depends on them.
|
|
println!("cargo:rerun-if-env-changed=VITE_POSTHOG_KEY");
|
|
println!("cargo:rerun-if-env-changed=VITE_POSTHOG_HOST");
|
|
|
|
ensure_sidecar_placeholder("uv");
|
|
ensure_sidecar_placeholder("ffmpeg");
|
|
ensure_sidecar_placeholder("ffprobe");
|
|
|
|
// Windows test binaries need the Common-Controls v6 manifest that
|
|
// tauri-build embeds into the app binary but cargo gives tests none of:
|
|
// without it the loader resolves comctl32 v5 (no TaskDialogIndirect —
|
|
// imported by tauri's dialog/tray stack) and every integration-test
|
|
// binary dies at load with STATUS_ENTRYPOINT_NOT_FOUND (0xc0000139).
|
|
// See tests/windows-test.manifest.
|
|
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
|
|
let manifest = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()))
|
|
.join("tests")
|
|
.join("windows-test.manifest");
|
|
println!("cargo:rerun-if-changed={}", manifest.display());
|
|
println!("cargo:rustc-link-arg-tests=/MANIFEST:EMBED");
|
|
println!("cargo:rustc-link-arg-tests=/MANIFESTINPUT:{}", manifest.display());
|
|
}
|
|
|
|
tauri_build::build();
|
|
}
|