xtask/tests/cli_verbs.rs
//! `cargo xtask <task>`: what the binary answers before any task does real work.
//! A task with no name, an unknown task and every malformed argument stop with a
//! failing exit code and a sentence that names what was wrong; a mistyped flag
//! must never run a different task than the operator asked for.
use std::path::PathBuf;
use std::process::{Command, Output};
const EXE: &str = env!("CARGO_BIN_EXE_xtask");
fn xtask(args: &[&str]) -> Output {
Command::new(EXE)
.args(args)
.output()
.expect("the xtask binary runs")
}
/// The run failed, and its stderr says `want`.
fn refused(args: &[&str], want: &str) {
let out = xtask(args);
let err = String::from_utf8_lossy(&out.stderr);
assert!(
!out.status.success(),
"`xtask {}` succeeded",
args.join(" ")
);
assert!(
err.contains(want),
"`xtask {}` did not say {want:?}:\n{err}",
args.join(" ")
);
}
#[test]
fn no_task_and_an_unknown_task_print_the_usage_and_fail() {
refused(&[], "usage: cargo xtask <task>");
refused(&["frobnicate"], "xtask: unknown task `frobnicate`");
refused(&["frobnicate"], "usage: cargo xtask <task>");
}
#[test]
fn siblings_is_a_successful_no_op_that_says_why() {
let out = xtask(&["siblings"]);
assert!(out.status.success());
assert!(String::from_utf8_lossy(&out.stderr).contains("nothing to switch on"));
}
/// `fmt --all` is refused BY NAME: it follows path dependencies out of the repository.
#[test]
fn fmt_refuses_all_and_unknown_arguments() {
refused(&["fmt", "--all"], "refusing `--all`");
refused(
&["fmt", "--all"],
"`cargo xtask fmt` with no argument already covers",
);
refused(&["fmt", "--diff"], "xtask fmt: unknown argument `--diff`");
}
#[test]
fn malformed_arguments_are_refused_by_each_task() {
refused(
&["trailer-guard", "--commits", "many"],
"--commits needs a number",
);
refused(&["trailer-guard", "--commits"], "--commits needs a number");
refused(
&["trailer-guard", "--all"],
"xtask trailer-guard: unknown argument `--all`",
);
refused(
&["publish", "--force"],
"usage: cargo xtask publish [--push]",
);
refused(
&["bench-build", "--fast"],
"usage: cargo xtask bench-build [--check | --build]",
);
refused(
&["roundtrip", "--no-mutat"],
"roundtrip: unknown flag `--no-mutat`",
);
refused(
&["roundtrip", "--seed", "abc"],
"--seed `abc` is not a number",
);
refused(
&["roundtrip", "--donor", "no-equals-sign"],
"--donor wants `<name>=<url>`",
);
refused(&["roundtrip", "--work"], "--work needs a value");
refused(&["wireproxy", "--to"], "--to needs a value");
refused(
&["wireproxy", "--bogus"],
"wireproxy: unknown argument `--bogus`",
);
refused(
&["wireproxy", "--listen", "127.0.0.1:1"],
"--to is required",
);
refused(&["payload"], "--variant <installer cell> is required");
refused(&["payload", "--variant"], "--variant needs a value");
refused(
&["payload", "--variant", "x", "--frob"],
"unknown flag `--frob`",
);
}
/// `bench` with no suite lists the suites; an unknown one is named with the known ones.
#[test]
fn bench_lists_its_suites_and_refuses_an_unknown_one() {
let first = xtask::benchrun::SUITES
.first()
.expect("a declared suite")
.name;
refused(&["bench"], "usage: cargo xtask bench <suite>");
refused(&["bench"], first);
refused(
&["bench", "no-such-suite"],
"xtask bench: unknown suite `no-such-suite`",
);
refused(&["bench", "no-such-suite"], &format!("known: {first}"));
}
/// `iso-installer --help` answers without building anything.
#[test]
fn iso_installer_help_answers_without_building() {
let out = xtask(&["iso-installer", "--help"]);
assert!(out.status.success());
let text = String::from_utf8_lossy(&out.stdout);
assert!(
text.contains("cargo xtask iso-installer — build gunnar's INSTALLER ISO"),
"{text}"
);
assert!(text.contains("--unenrolled '<why>'"), "{text}");
}
/// `payload --no-build --out <dir>` writes what the medium would boot: the kernel
/// command line, the payload row, and each staged file at its runtime path.
#[test]
fn payload_writes_the_start_of_a_medium_without_building_it() {
// a linked worktree cannot stamp what it builds, so the verb refuses there first
let root = xtask::guard::workspace_root();
if xtask::isoinstaller::worktree_refusal(&root).is_some() {
refused(
&[
"payload",
"--variant",
"gunnar-installer-metered",
"--no-build",
],
"REFUSED [iso-installer-worktree]",
);
return;
}
refused(
&["payload", "--variant", "no-such-cell", "--no-build"],
"no [[build.installer]] cell named 'no-such-cell'",
);
let dir = std::env::temp_dir().join(format!("gunnar-cli-payload-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let seed: PathBuf = dir.join("seed.toml");
std::fs::write(&seed, "estate = \"cli\"\n").unwrap();
let out_dir = dir.join("out");
let param = format!("pairing_seed=@{}", seed.display());
let out = xtask(&[
"payload",
"--variant",
"gunnar-installer-metered",
"--param",
¶m,
"--no-build",
"--out",
out_dir.to_str().unwrap(),
]);
assert!(
out.status.success(),
"{}",
String::from_utf8_lossy(&out.stderr)
);
let cmdline = std::fs::read_to_string(out_dir.join("cmdline")).unwrap();
assert!(
cmdline.contains("korp.env.GUNNAR_DATA_ROOT=/data"),
"{cmdline}"
);
assert_eq!(
std::fs::read_to_string(out_dir.join("payload-variant")).unwrap(),
"gunnar-server-metered\n"
);
assert_eq!(
std::fs::read_to_string(out_dir.join("staged/data/pairing/seed.toml")).unwrap(),
"estate = \"cli\"\n"
);
assert!(
!out_dir.join("binary").exists(),
"--no-build named a binary"
);
let out = xtask(&[
"payload",
"--variant",
"gunnar-front-installer-metered",
"--no-build",
]);
assert!(
out.status.success(),
"{}",
String::from_utf8_lossy(&out.stderr)
);
assert!(String::from_utf8_lossy(&out.stdout).contains("korp.entry=gunnar-front"));
let _ = std::fs::remove_dir_all(&dir);
}