kernel, patch, doctor, config: fill in the rest of Now and Next
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.
This commit is contained in:
+44
-6
@@ -152,6 +152,24 @@ fn a_bare_diff_is_refused_by_send_email() {
|
||||
assert!(stderr.contains("No subject line"), "{stderr}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_commit_with_nothing_staged_says_what_git_said() {
|
||||
// git explains "nothing staged" on stdout, so an error path that only
|
||||
// reads stderr prints a failure with nothing after it.
|
||||
let fixture = Fixture::kernel_tree();
|
||||
fixture.write("drivers/foo/bar.c", common::EDITED);
|
||||
|
||||
let output = fixture.cli(&["patch", "commit", "foo: nothing staged"]);
|
||||
|
||||
assert_eq!(output.status.code(), Some(1));
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
assert!(stderr.contains("failed (exit 1)"), "{stderr}");
|
||||
assert!(
|
||||
stderr.contains("no changes added to commit") || stderr.contains("not staged"),
|
||||
"git's own explanation was dropped: {stderr}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_names_a_base_that_does_not_resolve() {
|
||||
let fixture = Fixture::kernel_tree();
|
||||
@@ -180,8 +198,8 @@ fn create_refuses_a_range_that_holds_more_than_one_commit() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
assert!(stderr.contains("holds 2 commits"), "{stderr}");
|
||||
assert!(
|
||||
stderr.contains("series support is not built yet"),
|
||||
"{stderr}"
|
||||
stderr.contains("--range"),
|
||||
"the refusal has to say where a set belongs: {stderr}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -229,7 +247,7 @@ fn check_pipes_the_working_tree_diff_into_checkpatch_and_fails_on_errors() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn warnings_alone_do_not_fail_the_command() {
|
||||
fn checkpatch_warnings_alone_do_not_fail_the_command() {
|
||||
let fixture = Fixture::kernel_tree();
|
||||
fixture.write("drivers/foo/bar.c", common::EDITED);
|
||||
|
||||
@@ -246,7 +264,7 @@ fn warnings_alone_do_not_fail_the_command() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_run_with_no_total_line_counts_as_clean() {
|
||||
fn checkpatch_counts_a_run_with_no_total_line_as_clean() {
|
||||
// What a clean run looks like under checkpatch's own --terse, where the
|
||||
// summary line is suppressed entirely.
|
||||
let fixture = Fixture::kernel_tree();
|
||||
@@ -524,12 +542,32 @@ fn submit_threads_a_reroll_off_the_patch_it_replaces() {
|
||||
assert!(output.status.success());
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert!(
|
||||
stdout.contains("--in-reply-to <[email protected]>"),
|
||||
"{stdout}"
|
||||
stdout.contains("--in-reply-to '<[email protected]>'"),
|
||||
"an unquoted <...> is a redirection when pasted: {stdout}"
|
||||
);
|
||||
assert!(stdout.contains("In-Reply-To: <[email protected]>"), "{stdout}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_tree_that_is_not_a_repository_says_so_rather_than_blaming_a_revision() {
|
||||
// `git rev-parse --verify --quiet master` fails both for a revision that is
|
||||
// not there and for a directory that is not a repository. They want
|
||||
// different fixes, so they get different messages.
|
||||
let fixture = Fixture::bare();
|
||||
fixture.write_script("scripts/checkpatch.pl", "#!/bin/sh\n");
|
||||
fixture.write_script("scripts/get_maintainer.pl", "#!/bin/sh\n");
|
||||
|
||||
let output = fixture.cli(&["patch", "create", "000-foo"]);
|
||||
|
||||
assert_eq!(output.status.code(), Some(1));
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
assert!(stderr.contains("not a git repository"), "{stderr}");
|
||||
assert!(
|
||||
!stderr.contains("no revision"),
|
||||
"a missing repository is not a missing revision: {stderr}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn submit_refuses_a_template_cover_letter_instead_of_swallowing_it() {
|
||||
let fixture = Fixture::kernel_tree();
|
||||
|
||||
Reference in New Issue
Block a user