kernel quest reads bugzilla's REST API and picks one bug through a seeded xorshift, so a pick is reproducible in a test and varied in a person's hands. kernel test builds under spawn_blocking and boots under qemu with the serial console streamed line by line, and it reports what qemu did instead of flattening a failed boot into a success. patch create writes a whole series when --range is given. git format-patch does the naming and the numbering because that is already its job, the cover letter comes from --cover-letter, and a series.toml beside the patches records the range with both ends as hashes, the revision, the files and the blurb. patch submit takes a directory or several files and sends them in one git send-email invocation, and patch update re-rolls a directory through --reroll-count, deleting the superseded revision only once the new one is complete. doctor runs its checks in the order a first run hits them, prints one line and a fix hint for each, and names the first hard failure in the summary. A missing qemu or a missing mail route warns and keeps exit 0, because --dry-run is where the loop actually stops on a machine without an SMTP route. ~/.config/spectral/config.toml holds the tree path, the patch directory, always-CC addresses and an identity, with the environment winning over the file and the file over the built-in default. spectral init verifies or clones a tree and writes it once: a second run with the same tree says so and writes nothing, and a run that would change an existing answer is refused until --force. tests/ grew a fixture for each of those. The harness controls PATH and git's configuration, and its PATH can hold a git that answers send-email either way, so a host without that support still passes rather than deciding the result. Two review lanes ran over this work and this commit carries their fixes. A re-roll regenerated base..HEAD, so a commit that landed after create would have silently joined the set and been mailed: the range is recorded now and a re-roll regenerates exactly it. A patch file that was already gone made update fail forever while the sidecar described a revision that was not on disk: a missing file is the state the re-roll wanted. An empty blurb aborted after the files were written: it is checked before anything is generated. A tree that is not a git repository was reported as a missing revision. init cloned before it refused. The bugzilla request had no timeout, a signal-ended child printed the bare word "signal", CommandSpawn dropped the io reason, and the printed send-email line quoted only arguments with spaces, so --in-reply-to <id@host> pasted as a redirection. Checked with cargo fmt --all -- --check, cargo clippy --all-targets --locked -- -D warnings, cargo test --locked (96 passed, 4 ignored) and cargo doc --no-deps. The four ignored tests want the host's own checkpatch, get_maintainer and bugzilla, and all four pass when asked for.
72 lines
1.9 KiB
Rust
72 lines
1.9 KiB
Rust
//! The command surface parses, and a bad invocation is a usage error rather
|
|
//! than a panic.
|
|
|
|
mod common;
|
|
|
|
use common::Fixture;
|
|
|
|
#[test]
|
|
fn the_top_level_help_names_both_command_groups() {
|
|
let fixture = Fixture::bare();
|
|
let output = fixture.cli(&["--help"]);
|
|
|
|
assert!(output.status.success());
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(stdout.contains("kernel"), "{stdout}");
|
|
assert!(stdout.contains("patch"), "{stdout}");
|
|
}
|
|
|
|
#[test]
|
|
fn every_verb_has_help_of_its_own() {
|
|
let fixture = Fixture::bare();
|
|
|
|
for verb in [
|
|
&["kernel", "quest"][..],
|
|
&["kernel", "test"],
|
|
&["patch", "check"],
|
|
&["patch", "format"],
|
|
&["patch", "commit"],
|
|
&["patch", "create"],
|
|
&["patch", "submit"],
|
|
&["patch", "update"],
|
|
&["doctor"],
|
|
&["init"],
|
|
] {
|
|
let mut args = verb.to_vec();
|
|
args.push("--help");
|
|
|
|
let output = fixture.cli(&args);
|
|
assert!(
|
|
output.status.success(),
|
|
"{} --help failed: {}",
|
|
verb.join(" "),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
assert!(
|
|
!String::from_utf8_lossy(&output.stdout).is_empty(),
|
|
"{} --help printed nothing",
|
|
verb.join(" ")
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn an_unknown_subcommand_exits_two() {
|
|
let fixture = Fixture::bare();
|
|
let output = fixture.cli(&["frobnicate"]);
|
|
|
|
assert_eq!(output.status.code(), Some(2));
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(stderr.contains("Usage"), "{stderr}");
|
|
}
|
|
|
|
#[test]
|
|
fn a_leaf_missing_its_argument_exits_two() {
|
|
let fixture = Fixture::bare();
|
|
let output = fixture.cli(&["patch", "commit"]);
|
|
|
|
assert_eq!(output.status.code(), Some(2));
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(stderr.contains("MESSAGE"), "{stderr}");
|
|
}
|