Skip to main content

bynk/
test.rs

1//! `bynk test` — discover and run a project's test declarations.
2//!
3//! Delegates to the `bynkc` the driver resolves (`BYNK_BYNKC` → PATH →
4//! sibling-of-`bynk`) and shells `bynkc test`, forwarding every flag verbatim
5//! (v0.138, #487). Unlike `check`/`fmt`, `test` delegates *always*, not only
6//! under an override: it orchestrates external `tsc`/`node`, so it is a
7//! subprocess regardless, and routing it through the driver's resolution is the
8//! whole point — an editor or developer inherits that resolution instead of
9//! locating `bynkc` themselves (the fix direction for #486). The trade-off (a
10//! `bynkc` binary must be present, and the driver↔compiler skew surface stays)
11//! is accepted; the driver at least resolves it more richly than any editor.
12//! #1675: and it acts on that skew before delegating — see
13//! [`crate::compiler::skew_gate`].
14
15use std::ffi::OsString;
16use std::process::ExitCode;
17
18use bynk_driver::test_runner::TestArgs;
19
20use crate::compiler::{Compiler, SkewAsker, skew_gate};
21
22/// Run `bynk test` by shelling the resolved `bynkc`. When no `bynkc` could be
23/// located, point the developer at `bynk doctor` rather than emitting a raw
24/// spawn error.
25///
26/// Wave 5 §5.4 (findings #40/#72): `args` is the one contract `bynkc test`
27/// and `bynk test` both flatten (`bynk_driver::test_runner::TestArgs`) —
28/// this function's whole body is re-spelling its fields back out as argv
29/// tokens for the shell-out, so a field added there needs no matching change
30/// here beyond this list.
31pub fn run(compiler: &Compiler, args: TestArgs, allow_skew: bool) -> ExitCode {
32    let Some(bynkc) = compiler.path.as_deref() else {
33        eprintln!(
34            "bynk test: no `bynkc` compiler found (looked at $BYNK_BYNKC, PATH, and next to `bynk`)."
35        );
36        eprintln!("  Run `bynk doctor --only test` for the exact remedy.");
37        return ExitCode::FAILURE;
38    };
39    // #1675: `test` always runs a second compiler, so act on its skew from the
40    // driver: a minor skew warns, a major one refuses unless allowed.
41    let asker = SkewAsker {
42        command: Some("test"),
43        has_flag: true,
44    };
45    if !skew_gate(compiler, asker, allow_skew) {
46        return ExitCode::FAILURE;
47    }
48
49    let mut argv: Vec<OsString> = vec!["test".into(), args.input.into_os_string()];
50    if let Some(output) = args.output {
51        argv.push("--output".into());
52        argv.push(output.into_os_string());
53    }
54    if args.no_run {
55        argv.push("--no-run".into());
56    }
57    argv.push("--format".into());
58    argv.push(args.format.as_bynkc_arg().into());
59    if args.inspect {
60        argv.push("--inspect".into());
61    }
62    if let Some(seed) = args.seed {
63        argv.push("--seed".into());
64        argv.push(seed.into());
65    }
66    if let Some(case) = args.case {
67        argv.push("--case".into());
68        argv.push(case.into());
69    }
70    if args.coverage {
71        argv.push("--coverage".into());
72    }
73
74    crate::shell::delegate(bynkc, argv)
75}