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
13use std::ffi::OsString;
14use std::process::ExitCode;
15
16use bynk_driver::test_runner::TestArgs;
17
18use crate::compiler::Compiler;
19
20/// Run `bynk test` by shelling the resolved `bynkc`. When no `bynkc` could be
21/// located, point the developer at `bynk doctor` rather than emitting a raw
22/// spawn error.
23///
24/// Wave 5 §5.4 (findings #40/#72): `args` is the one contract `bynkc test`
25/// and `bynk test` both flatten (`bynk_driver::test_runner::TestArgs`) —
26/// this function's whole body is re-spelling its fields back out as argv
27/// tokens for the shell-out, so a field added there needs no matching change
28/// here beyond this list.
29pub fn run(compiler: &Compiler, args: TestArgs) -> ExitCode {
30 let Some(bynkc) = compiler.path.as_deref() else {
31 eprintln!(
32 "bynk test: no `bynkc` compiler found (looked at $BYNK_BYNKC, PATH, and next to `bynk`)."
33 );
34 eprintln!(" Run `bynk doctor --only test` for the exact remedy.");
35 return ExitCode::FAILURE;
36 };
37
38 let mut argv: Vec<OsString> = vec!["test".into(), args.input.into_os_string()];
39 if let Some(output) = args.output {
40 argv.push("--output".into());
41 argv.push(output.into_os_string());
42 }
43 if args.no_run {
44 argv.push("--no-run".into());
45 }
46 argv.push("--format".into());
47 argv.push(args.format.as_bynkc_arg().into());
48 if args.inspect {
49 argv.push("--inspect".into());
50 }
51 if let Some(seed) = args.seed {
52 argv.push("--seed".into());
53 argv.push(seed.into());
54 }
55 if let Some(case) = args.case {
56 argv.push("--case".into());
57 argv.push(case.into());
58 }
59 if args.coverage {
60 argv.push("--coverage".into());
61 }
62
63 crate::shell::delegate(bynkc, argv)
64}