tests/clippy_gate.rs
//! **`cargo clippy --all-targets -- -D warnings` is green for this repo.**
//!
//! Run the gate with:
//!
//! ```text
//! cargo test --test clippy_gate -- --ignored
//! ```
//!
//! It runs three clippy passes and fails on the first that warns:
//!
//! 1. the workspace with default features,
//! 2. the workspace with every member feature that does not pull `draupnir`,
//! 3. the detached `probe/` crate (it is not a workspace member).
//!
//! **Why not `--all-features`.** `msi-oci`, `pack-oci`, `oci`, `service-drive`, `kvm`
//! and `matrix` link the sibling `../draupnir`, which is its own repo with its own
//! gate. When that sibling does not build (it did not on 2026-10-10: draupnir
//! against tunnr), `--all-features` reports draupnir's red, not ours. The feature
//! set is computed from `cargo metadata`, not written down, so a new feature is
//! linted as soon as it exists, unless it reaches draupnir.
//!
//! The non-ignored tests are cheap and run on a plain `cargo test`: the feature
//! set is non-empty and holds back exactly the draupnir features, and no source
//! file silences clippy for a whole crate (`#![allow(clippy::…)]`). A lint gets a
//! real fix, or a scoped `#[allow]` on the one item with a reason next to it.
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::process::Command;
/// The sibling repo whose features are held back from the feature pass.
const HELD_BACK: &str = "draupnir";
fn repo_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
fn cargo() -> String {
std::env::var("CARGO").unwrap_or_else(|_| "cargo".into())
}
/// `true` if `feature` of a package with `features` turns on `dep` (directly, or
/// through another feature of the same package).
fn pulls(
features: &BTreeMap<String, Vec<String>>,
feature: &str,
dep: &str,
seen: &mut Vec<String>,
) -> bool {
if seen.iter().any(|s| s == feature) {
return false;
}
seen.push(feature.to_string());
let Some(values) = features.get(feature) else {
return feature == dep;
};
values.iter().any(|v| {
let v = v.as_str();
v == format!("dep:{dep}")
|| v.starts_with(&format!("{dep}/"))
|| v.starts_with(&format!("{dep}?/"))
|| pulls(features, v, dep, seen)
})
}
/// `pkg/feature` for every workspace member feature that does not pull `dep`.
fn features_without(members: &[(String, BTreeMap<String, Vec<String>>)], dep: &str) -> Vec<String> {
let mut out = Vec::new();
for (pkg, features) in members {
for f in features.keys() {
if f != "default" && !pulls(features, f, dep, &mut Vec::new()) {
out.push(format!("{pkg}/{f}"));
}
}
}
out
}
fn workspace_members() -> Vec<(String, BTreeMap<String, Vec<String>>)> {
let out = Command::new(cargo())
.args([
"metadata",
"--format-version",
"1",
"--no-deps",
"--offline",
])
.current_dir(repo_root())
.output()
.expect("cargo metadata did not start");
assert!(
out.status.success(),
"cargo metadata failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let meta: serde_json::Value = serde_json::from_slice(&out.stdout).expect("cargo metadata json");
meta["packages"]
.as_array()
.expect("packages")
.iter()
.map(|p| {
let name = p["name"].as_str().expect("package name").to_string();
let features = serde_json::from_value(p["features"].clone()).expect("features map");
(name, features)
})
.collect()
}
fn rust_files(dir: &Path, out: &mut Vec<PathBuf>) {
let Ok(rd) = std::fs::read_dir(dir) else {
return;
};
for e in rd.flatten() {
let p = e.path();
let name = e.file_name();
if p.is_dir() {
if name != "target" && name != ".git" {
rust_files(&p, out);
}
} else if p.extension().is_some_and(|x| x == "rs") {
out.push(p);
}
}
}
/// The lines of `src` that silence a clippy lint for the whole crate, in the
/// single-line and the multi-line `#![allow(\n clippy::…` spelling.
fn crate_wide_clippy_allows(src: &str) -> Vec<String> {
let mut hits = Vec::new();
let lines: Vec<&str> = src.lines().collect();
for (i, l) in lines.iter().enumerate() {
let t = l.trim_start();
if !t.starts_with("#![allow(") {
continue;
}
let mut attr = t.to_string();
let mut j = i;
while !attr.contains(")]") && j + 1 < lines.len() {
j += 1;
attr.push_str(lines[j].trim());
}
if attr.contains("clippy::") {
hits.push(format!("{}: {attr}", i + 1));
}
}
hits
}
fn clippy(args: &[&str]) -> Result<(), String> {
let out = Command::new(cargo())
.arg("clippy")
.args(args)
.args(["--all-targets", "--", "-D", "warnings"])
.current_dir(repo_root())
.output()
.map_err(|e| format!("cargo clippy did not start: {e}"))?;
if out.status.success() {
return Ok(());
}
let err = String::from_utf8_lossy(&out.stderr);
let tail: Vec<&str> = err.lines().rev().take(60).collect();
Err(format!(
"cargo clippy {} --all-targets -- -D warnings is RED:\n{}",
args.join(" "),
tail.into_iter().rev().collect::<Vec<_>>().join("\n")
))
}
#[test]
fn pulls_follows_features_and_dep_spellings() {
let f: BTreeMap<String, Vec<String>> = [
("a", vec!["dep:x"]),
("b", vec!["x/seed"]),
("c", vec!["x?/seed"]),
("d", vec!["b"]),
("e", vec!["dep:y", "f"]),
("f", vec!["e"]),
]
.into_iter()
.map(|(k, v)| (k.to_string(), v.into_iter().map(String::from).collect()))
.collect();
for yes in ["a", "b", "c", "d", "x"] {
assert!(pulls(&f, yes, "x", &mut Vec::new()), "{yes} pulls x");
}
for no in ["e", "f", "y"] {
assert!(!pulls(&f, no, "x", &mut Vec::new()), "{no} does not pull x");
}
}
#[test]
fn crate_wide_clippy_allow_is_seen_in_both_spellings() {
assert_eq!(
crate_wide_clippy_allows("#![allow(clippy::type_complexity)]\n").len(),
1
);
assert_eq!(
crate_wide_clippy_allows("#![allow(\n dead_code,\n clippy::too_many_arguments\n)]\n")
.len(),
1
);
assert!(
crate_wide_clippy_allows("#![allow(dead_code)]\n#[allow(clippy::x)]\nfn f() {}\n")
.is_empty()
);
}
#[test]
fn the_feature_pass_holds_back_exactly_the_draupnir_features() {
let members = workspace_members();
let lint = features_without(&members, HELD_BACK);
assert!(
lint.iter().any(|f| f == "skidbladnir/pki"),
"feature pass is missing pki: {lint:?}"
);
let root = members
.iter()
.find(|(n, _)| n == "skidbladnir")
.expect("root package");
for held in ["msi-oci", "pack-oci", "service-drive", "kvm"] {
assert!(
root.1.contains_key(held),
"{held} is no longer a feature; update the doc"
);
assert!(
!lint.iter().any(|f| f == &format!("skidbladnir/{held}")),
"{held} pulls {HELD_BACK}"
);
}
let total: usize = members
.iter()
.map(|(_, f)| f.keys().filter(|k| *k != "default").count())
.sum();
assert!(
lint.len() * 2 > total,
"most features must be linted: {} of {total}",
lint.len()
);
}
#[test]
fn no_source_file_silences_clippy_crate_wide() {
let root = repo_root();
let mut files = Vec::new();
for d in [
"src", "tests", "crates", "probe", "benches", "examples", "build.rs",
] {
let p = root.join(d);
if p.is_file() {
files.push(p);
} else {
rust_files(&p, &mut files);
}
}
assert!(
files.len() > 50,
"scanned only {} files; the walk is broken",
files.len()
);
let mut bad = Vec::new();
for f in &files {
let src = std::fs::read_to_string(f).unwrap();
for hit in crate_wide_clippy_allows(&src) {
bad.push(format!(
"{}:{hit}",
f.strip_prefix(&root).unwrap().display()
));
}
}
assert!(
bad.is_empty(),
"crate-wide clippy allow — fix the lints instead:\n{}",
bad.join("\n")
);
}
#[test]
#[ignore = "runs cargo clippy three times; run with --ignored"]
fn clippy_is_clean() {
let features = features_without(&workspace_members(), HELD_BACK).join(" ");
let probe = repo_root().join("probe/Cargo.toml");
let probe = probe.to_str().unwrap();
let runs: [&[&str]; 3] = [
&["--workspace"],
&["--workspace", "--features", &features],
&["--manifest-path", probe],
];
let red: Vec<String> = runs.iter().filter_map(|a| clippy(a).err()).collect();
assert!(red.is_empty(), "{}", red.join("\n\n"));
}