tests/required_features_law.rs
//! Guard test for the **required-features law**: a target nothing builds is a target
//! nothing checks.
//!
//! ## Why this is a law and not a style note
//!
//! `cargo check --workspace --all-targets` is the command everyone reaches for to answer
//! "is the tree clean". It does **not** mean all targets. A `[[bin]]` / `[[bench]]` /
//! `[[example]]` carrying `required-features` is skipped **silently** when those features
//! are off — no warning, no note, no non-zero exit. The target simply is not in the
//! build, and neither is anything wrong with it.
//!
//! MEASURED on this tree 2026-08-04: `ljar/src/bin/bench_inflate_cmp.rs:52` held
//!
//! ```text
//! error[E0282]: type annotations needed for `Vec<_>`
//! ```
//!
//! and `cargo check --workspace --all-targets` was **GREEN**, because the bin declares
//! `required-features = ["gen"]`. `--all-features` was green too, for a different and
//! worse reason: it turns `zlib-ng` on as well, and the `#[cfg(feature = "zlib-ng")]`
//! block is the only thing that ever pushes into that vector, so the inference succeeds
//! and the defect is invisible from that direction as well. Neither of the two commands
//! anyone actually runs could see it.
//!
//! This is the same defect class as the bench-target law next door
//! (`tests/bench_target_law.rs`): a target that no declared build point compiles.
//! There the target failed loudly and everybody learned to read past it; here it never
//! failed at all. This file IS the declared build point.
//!
//! ## The rule this test enforces
//!
//! Every target in the workspace that carries `required-features` must compile with
//! exactly those features enabled and nothing else. Checked by running `cargo check`,
//! and judged on its OUTPUT — compiler diagnostics — not on its exit code alone.
//!
//! ## The guard's own guard
//!
//! A test that shells out is the classic silent green: a malformed command fails, its
//! empty output looks exactly like a clean result, and the law passes for ever. So:
//!
//! * [`the_scan_finds_the_required_features_targets`] asserts the manifest walk reaches
//! real declarations and names one it must contain.
//! * [`the_runner_can_report_red`] drives the same runner at a target that cannot
//! build, and requires a failure with a diagnostic in it. A runner that always
//! answered "clean" fails there.
#[path = "law_common/mod.rs"]
mod law_common;
use std::path::PathBuf;
use std::process::Command;
/// One target that cargo will not build unless its features are asked for.
#[derive(Debug, Clone, PartialEq, Eq)]
struct Gated {
/// The package the target belongs to (`-p`).
package: String,
/// `bin` / `bench` / `example` — the flag cargo takes.
kind: String,
/// The target's name.
name: String,
/// The features it declares it needs.
features: Vec<String>,
}
/// The workspace root plus every member, as crate directories.
///
/// ONE reader, shared with the other two law guards — see [`law_common`]. The private
/// copy this replaces read `members = [...]` off a SINGLE line and narrowed to the root
/// crate, in silence, the moment that array wrapped.
fn crate_dirs() -> Vec<PathBuf> {
law_common::crate_dirs()
}
/// Parse a `["a", "b"]` inline array.
fn array(val: &str) -> Vec<String> {
let Some(a) = val.find('[') else {
return Vec::new();
};
let Some(b) = val.rfind(']') else {
return Vec::new();
};
val[a + 1..b]
.split(',')
.map(|s| s.trim().trim_matches('"').to_string())
.filter(|s| !s.is_empty())
.collect()
}
/// Every `required-features` target declared anywhere in the workspace.
fn gated_targets() -> Vec<Gated> {
let mut out = Vec::new();
for dir in crate_dirs() {
let Ok(manifest) = std::fs::read_to_string(dir.join("Cargo.toml")) else {
continue;
};
let mut package = String::new();
let mut section = String::new();
let mut cur: (Option<String>, Vec<String>) = (None, Vec::new());
let mut flush = |section: &str,
cur: &mut (Option<String>, Vec<String>),
out: &mut Vec<Gated>,
package: &str| {
let kind = match section {
"[[bin]]" => "bin",
"[[bench]]" => "bench",
"[[example]]" => "example",
"[[test]]" => "test",
_ => return,
};
if let (Some(name), false) = (cur.0.take(), cur.1.is_empty()) {
out.push(Gated {
package: package.to_string(),
kind: kind.to_string(),
name,
features: std::mem::take(&mut cur.1),
});
}
*cur = (None, Vec::new());
};
for line in manifest.lines() {
let t = line.trim();
if t.starts_with('[') {
flush(§ion, &mut cur, &mut out, &package);
section = t.to_string();
continue;
}
let Some((key, val)) = t.split_once('=') else {
continue;
};
let (key, val) = (key.trim(), val.trim());
match (section.as_str(), key) {
("[package]", "name") => package = val.trim_matches('"').to_string(),
(_, "name") => cur.0 = Some(val.trim_matches('"').to_string()),
(_, "required-features") => cur.1 = array(val),
_ => {}
}
}
flush(§ion, &mut cur, &mut out, &package);
}
out.sort_by(|a, b| (&a.package, &a.kind, &a.name).cmp(&(&b.package, &b.kind, &b.name)));
out
}
/// A target directory of this law's own, so a `cargo check` run from inside a test
/// cannot contend with the outer build's lock on the one it is running out of.
fn scratch_target_dir() -> PathBuf {
let base = std::env::var("CARGO_TARGET_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("target"));
base.join("required-features-law")
}
/// Run one `cargo check` and hand back `(success, combined output)`.
///
/// **The output, not the exit code.** A malformed argv fails in a way whose empty output
/// is shaped exactly like a clean result, and that is how a shell-out guard dies.
fn cargo_check(package: &str, features: &[String], targets: &[(String, String)]) -> (bool, String) {
let mut cmd = Command::new(std::env::var("CARGO").unwrap_or_else(|_| String::from("cargo")));
cmd.current_dir(env!("CARGO_MANIFEST_DIR"))
.env("CARGO_TARGET_DIR", scratch_target_dir())
// Inherited RUSTFLAGS would force a different fingerprint per caller and, worse,
// could carry a `-D warnings` that turns this law into a style gate.
.env_remove("RUSTFLAGS")
.arg("check")
.arg("-p")
.arg(package);
if !features.is_empty() {
cmd.arg("--features").arg(features.join(","));
}
for (kind, name) in targets {
cmd.arg(format!("--{kind}")).arg(name);
}
let out = cmd.output().expect("run cargo check");
let text = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
(out.status.success(), text)
}
/// Every compiler diagnostic line in a cargo run's output.
fn errors(text: &str) -> Vec<&str> {
text.lines()
.map(str::trim_start)
.filter(|l| l.starts_with("error[") || l.starts_with("error:"))
.collect()
}
/// **The law.** A target cargo will not build by default must still build.
#[test]
fn every_required_features_target_builds() {
let gated = gated_targets();
// Group by (package, feature set): one cargo invocation compiles the dependency
// graph once for all of them.
let mut groups: Vec<(String, Vec<String>, Vec<(String, String)>)> = Vec::new();
for g in &gated {
match groups
.iter_mut()
.find(|(p, f, _)| *p == g.package && *f == g.features)
{
Some((_, _, ts)) => ts.push((g.kind.clone(), g.name.clone())),
None => groups.push((
g.package.clone(),
g.features.clone(),
vec![(g.kind.clone(), g.name.clone())],
)),
}
}
let mut offenders: Vec<String> = Vec::new();
for (package, features, targets) in &groups {
let (ok, text) = cargo_check(package, features, targets);
let errs = errors(&text);
if !ok || !errs.is_empty() {
offenders.push(format!(
"{package} --features {} ({}): {}",
features.join(","),
targets
.iter()
.map(|(k, n)| format!("--{k} {n}"))
.collect::<Vec<_>>()
.join(" "),
if errs.is_empty() {
String::from("cargo failed with no diagnostic — the command itself is wrong")
} else {
errs.join(" | ")
},
));
}
}
assert!(
offenders.is_empty(),
"{} of {} required-features group(s) do not build. `cargo check --workspace \
--all-targets` skips these SILENTLY, so this test is the only thing that ever \
compiles them:\n {}",
offenders.len(),
groups.len(),
offenders.join("\n ")
);
}
/// **Anti-hollow arm one: the sweep must actually reach declarations.**
///
/// A manifest walk that finds nothing makes the law above green for ever. Assert a floor
/// and name the target that carried the defect, so a discovery that stops seeing it
/// fails here rather than passing there.
#[test]
fn the_scan_finds_the_required_features_targets() {
let gated = gated_targets();
assert!(
gated.len() >= 3,
"found only {} required-features target(s) — the walk is misrooted and the law \
would be vacuously green: {gated:?}",
gated.len()
);
let found = gated
.iter()
.find(|g| g.package == "ljar" && g.name == "bench_inflate_cmp")
.unwrap_or_else(|| panic!("discovery lost ljar's bench_inflate_cmp; it saw {gated:?}"));
assert_eq!(found.kind, "bin");
assert_eq!(
found.features,
vec![String::from("gen")],
"the features must be read off the manifest, not assumed",
);
}
/// **Anti-hollow arm two: the runner must be able to say no.**
///
/// Drives the identical machinery at a target that cannot exist. A runner that silently
/// reports clean — the way a shell-out guard usually dies — fails here.
#[test]
fn the_runner_can_report_red() {
let (ok, text) = cargo_check(
"ljar",
&[String::from("gen")],
&[(
String::from("bin"),
String::from("a_bin_that_does_not_exist"),
)],
);
assert!(
!ok,
"cargo reported success for a bin that does not exist:\n{text}"
);
assert!(
!errors(&text).is_empty(),
"cargo failed but printed no diagnostic — this runner would read a broken \
invocation as a clean tree:\n{text}"
);
}