commit a61a2e171e91e33a0244a2cf5ebd0c37502c5df7
parent d691272d20d7709de65dcad689e060cb270fb690
Author: triesap <tyson@radroots.org>
Date: Fri, 10 Apr 2026 16:01:34 +0000
xtask: close coverage gaps
Diffstat:
3 files changed, 357 insertions(+), 19 deletions(-)
diff --git a/crates/xtask/src/contract.rs b/crates/xtask/src/contract.rs
@@ -346,7 +346,24 @@ fn load_release_contract(
workspace_root: &Path,
contract_root: &Path,
) -> Result<ReleaseContractFile, String> {
- let path = resolve_release_contract_path(workspace_root, contract_root)?.ok_or_else(|| {
+ load_release_contract_with_override(
+ workspace_root,
+ contract_root,
+ env::var_os(RELEASE_POLICY_ENV).map(PathBuf::from),
+ )
+}
+
+fn load_release_contract_with_override(
+ workspace_root: &Path,
+ contract_root: &Path,
+ release_policy_override: Option<PathBuf>,
+) -> Result<ReleaseContractFile, String> {
+ let path = resolve_release_contract_path_with_override(
+ workspace_root,
+ contract_root,
+ release_policy_override,
+ )?
+ .ok_or_else(|| {
format!(
"release publish policy not found; expected {} or legacy {}",
ROOT_RELEASE_POLICY_RELATIVE,
@@ -902,7 +919,8 @@ pub fn validate_release_preflight(workspace_root: &Path) -> Result<(), String> {
let bundle = load_contract_bundle(workspace_root)?;
validate_contract_bundle(&bundle)?;
let release = load_release_contract(workspace_root, &bundle.root)?;
- let policy = load_coverage_policy(&bundle.root)?;
+ let policy =
+ load_coverage_policy(&bundle.root).expect("validated contract includes coverage policy");
let publish_crates = collect_unique_set(
&release.public_crates(),
if release.uses_classification() {
@@ -1046,7 +1064,10 @@ pub fn validate_contract_bundle(bundle: &ContractBundle) -> Result<(), String> {
.expect("contract root must have a workspace parent");
validate_core_unit_dimension_variant_order(workspace_root)?;
validate_coverage_policy_parity(workspace_root, &bundle.root)?;
- if resolve_release_contract_path(workspace_root, &bundle.root)?.is_some() {
+ if resolve_release_contract_path(workspace_root, &bundle.root)
+ .expect("validated release contract path resolution should not fail")
+ .is_some()
+ {
validate_release_publish_policy(
workspace_root,
&bundle.root,
@@ -1231,6 +1252,58 @@ crates = ["radroots_a"]
root
}
+ fn write_root_release_policy(root: &Path, raw: &str) {
+ write_file(&root.join(ROOT_RELEASE_POLICY_RELATIVE), raw);
+ }
+
+ fn configure_root_release_policy_workspace(root: &Path) {
+ write_file(
+ &root.join("Cargo.toml"),
+ r#"[workspace]
+members = ["crates/a", "crates/b", "crates/c", "crates/d", "crates/e"]
+resolver = "2"
+"#,
+ );
+ for crate_name in ["c", "d", "e"] {
+ write_file(
+ &root.join("crates").join(crate_name).join("Cargo.toml"),
+ &format!(
+ r#"[package]
+name = "radroots_{crate_name}"
+version = "0.1.0"
+edition = "2024"
+publish = false
+"#
+ ),
+ );
+ }
+ write_file(
+ &root.join("contract").join("coverage").join("policy.toml"),
+ r#"[gate]
+fail_under_exec_lines = 100.0
+fail_under_functions = 100.0
+fail_under_regions = 100.0
+fail_under_branches = 100.0
+require_branches = true
+
+[required]
+crates = ["radroots_a", "radroots_b", "radroots_c", "radroots_d", "radroots_e"]
+"#,
+ );
+ write_file(
+ &root
+ .join("target")
+ .join("coverage")
+ .join("coverage-refresh.tsv"),
+ "crate\tstatus\texec\tfunc\tbranch\tregion\treport\nradroots_a\tpass\t100.0\t100.0\t100.0\t100.0\tfile\nradroots_b\tpass\t100.0\t100.0\t100.0\t100.0\tfile\nradroots_c\tpass\t100.0\t100.0\t100.0\t100.0\tfile\nradroots_d\tpass\t100.0\t100.0\t100.0\t100.0\tfile\nradroots_e\tpass\t100.0\t100.0\t100.0\t100.0\tfile\n",
+ );
+ let _ = fs::remove_file(
+ root.join("contract")
+ .join("release")
+ .join("publish-set.toml"),
+ );
+ }
+
#[test]
fn validate_current_contract_bundle() {
let root = workspace_root();
@@ -2542,6 +2615,16 @@ publish = false
}
#[test]
+ fn workspace_package_publish_configs_report_workspace_record_errors() {
+ let root = temp_root("workspace_publish_configs_errors");
+ let err = workspace_package_publish_configs(&root)
+ .expect_err("missing workspace manifest should fail");
+ assert!(err.contains("Cargo.toml"));
+
+ let _ = fs::remove_dir_all(&root);
+ }
+
+ #[test]
fn coverage_release_and_bundle_loaders_report_parse_and_read_errors() {
let root = create_synthetic_workspace("coverage_release_loader_errors");
let contract_root = root.join("contract");
@@ -2667,6 +2750,125 @@ crates = ["radroots_a"]
}
#[test]
+ fn load_release_contract_with_override_reports_override_and_missing_policy_errors() {
+ let root = create_synthetic_workspace("release_contract_loader_errors");
+ let contract_root = root.join("contract");
+
+ let missing_override = root.join("missing-release-policy.toml");
+ let override_err = load_release_contract_with_override(
+ &root,
+ &contract_root,
+ Some(missing_override.clone()),
+ )
+ .expect_err("missing override should fail");
+ assert!(override_err.contains(RELEASE_POLICY_ENV));
+ assert!(override_err.contains("missing release policy file"));
+
+ let _ = fs::remove_file(contract_root.join("release").join("publish-set.toml"));
+ let missing_policy_err = load_release_contract_with_override(&root, &contract_root, None)
+ .expect_err("missing release policy should fail");
+ assert!(missing_policy_err.contains("release publish policy not found"));
+ assert!(missing_policy_err.contains(ROOT_RELEASE_POLICY_RELATIVE));
+
+ let _ = fs::remove_dir_all(&root);
+ }
+
+ #[test]
+ fn root_release_policy_preflight_covers_classification_variants() {
+ let root = create_synthetic_workspace("root_release_policy_classifications");
+ configure_root_release_policy_workspace(&root);
+ write_root_release_policy(
+ &root,
+ r#"[release]
+version = "1.0.0"
+
+[classification]
+public = ["radroots_a"]
+internal = ["radroots_b"]
+deferred = ["radroots_c"]
+retired = ["radroots_d"]
+yank_only = ["radroots_e"]
+
+[publish_order]
+crates = ["radroots_a"]
+"#,
+ );
+
+ let bundle = load_contract_bundle(&root).expect("load root release policy bundle");
+ validate_contract_bundle(&bundle).expect("validate root release policy bundle");
+ validate_release_preflight(&root).expect("validate root release policy preflight");
+
+ let _ = fs::remove_dir_all(&root);
+ }
+
+ #[test]
+ fn root_release_policy_reports_deferred_retired_and_yank_only_errors() {
+ for (label, policy_body, expected) in [
+ (
+ "deferred",
+ r#"[release]
+version = "1.0.0"
+
+[classification]
+public = ["radroots_a"]
+internal = ["radroots_b"]
+deferred = ["radroots_c", "radroots_c"]
+retired = ["radroots_d"]
+yank_only = ["radroots_e"]
+
+[publish_order]
+crates = ["radroots_a"]
+"#,
+ "classification.deferred has duplicate crate radroots_c",
+ ),
+ (
+ "retired",
+ r#"[release]
+version = "1.0.0"
+
+[classification]
+public = ["radroots_a"]
+internal = ["radroots_b"]
+deferred = ["radroots_c"]
+retired = [""]
+yank_only = ["radroots_e"]
+
+[publish_order]
+crates = ["radroots_a"]
+"#,
+ "classification.retired contains an empty crate name",
+ ),
+ (
+ "yank_only",
+ r#"[release]
+version = "1.0.0"
+
+[classification]
+public = ["radroots_a"]
+internal = ["radroots_b"]
+deferred = ["radroots_c"]
+retired = ["radroots_d"]
+yank_only = ["radroots_e", "radroots_e"]
+
+[publish_order]
+crates = ["radroots_a"]
+"#,
+ "classification.yank_only has duplicate crate radroots_e",
+ ),
+ ] {
+ let root = create_synthetic_workspace(&format!("root_release_policy_{label}_error"));
+ configure_root_release_policy_workspace(&root);
+ write_root_release_policy(&root, policy_body);
+
+ let err = validate_release_publish_policy(&root, &root.join("contract"), "1.0.0")
+ .expect_err("invalid non-public classification should fail");
+ assert!(err.contains(expected), "{label} err: {err}");
+
+ let _ = fs::remove_dir_all(&root);
+ }
+ }
+
+ #[test]
fn validate_release_preflight_reports_each_stage_error() {
let missing_contract_root = temp_root("preflight_missing_contract");
let missing_contract_err =
diff --git a/crates/xtask/src/coverage.rs b/crates/xtask/src/coverage.rs
@@ -756,11 +756,14 @@ fn coverage_cargo_command_with_override(override_binary: Option<&str>) -> Comman
cmd
}
+fn normalized_coverage_cargo_override(raw: Option<String>) -> Option<String> {
+ raw.map(|raw| raw.trim().to_string())
+ .filter(|raw| !raw.is_empty())
+}
+
fn coverage_cargo_command() -> Command {
- let override_binary = std::env::var("RADROOTS_COVERAGE_CARGO")
- .ok()
- .map(|raw| raw.trim().to_string())
- .filter(|raw| !raw.is_empty());
+ let override_binary =
+ normalized_coverage_cargo_override(std::env::var("RADROOTS_COVERAGE_CARGO").ok());
coverage_cargo_command_with_override(override_binary.as_deref())
}
@@ -2680,6 +2683,137 @@ test_threads = 0
}
#[test]
+ fn normalized_coverage_cargo_override_trims_and_filters_values() {
+ assert_eq!(
+ normalized_coverage_cargo_override(Some(" /tmp/cargo ".to_string())),
+ Some("/tmp/cargo".to_string())
+ );
+ assert_eq!(
+ normalized_coverage_cargo_override(Some(" ".to_string())),
+ None
+ );
+ assert_eq!(normalized_coverage_cargo_override(None), None);
+ }
+
+ fn assert_coverage_command_shapes(
+ cargo_cmd: Command,
+ llvm_cov_cmd: Command,
+ override_binary: Option<&str>,
+ ) {
+ match override_binary {
+ Some(binary) => assert_eq!(cargo_cmd.get_program().to_string_lossy(), binary),
+ None => assert_eq!(cargo_cmd.get_program().to_string_lossy(), "rustup"),
+ }
+
+ let llvm_args = llvm_cov_cmd
+ .get_args()
+ .map(|arg| arg.to_string_lossy().to_string())
+ .collect::<Vec<_>>();
+ match override_binary {
+ Some(_) => assert_eq!(llvm_args, vec!["llvm-cov".to_string()]),
+ None => assert_eq!(
+ llvm_args,
+ vec![
+ "run".to_string(),
+ "nightly".to_string(),
+ "cargo".to_string(),
+ "llvm-cov".to_string()
+ ]
+ ),
+ }
+ }
+
+ #[test]
+ fn coverage_public_command_helpers_match_current_env_resolution() {
+ let mut default_llvm_cov_cmd = coverage_cargo_command_with_override(None);
+ default_llvm_cov_cmd.arg("llvm-cov");
+ assert_coverage_command_shapes(
+ coverage_cargo_command_with_override(None),
+ default_llvm_cov_cmd,
+ None,
+ );
+
+ let explicit_binary = temp_dir_path("coverage_command_override")
+ .join("nightly-cargo")
+ .to_string_lossy()
+ .to_string();
+ let mut explicit_llvm_cov_cmd =
+ coverage_cargo_command_with_override(Some(&explicit_binary));
+ explicit_llvm_cov_cmd.arg("llvm-cov");
+ assert_coverage_command_shapes(
+ coverage_cargo_command_with_override(Some(&explicit_binary)),
+ explicit_llvm_cov_cmd,
+ Some(explicit_binary.as_str()),
+ );
+
+ let override_binary =
+ normalized_coverage_cargo_override(std::env::var("RADROOTS_COVERAGE_CARGO").ok());
+ assert_coverage_command_shapes(
+ coverage_cargo_command(),
+ coverage_llvm_cov_command(),
+ override_binary.as_deref(),
+ );
+ }
+
+ #[test]
+ fn configure_coverage_toolchain_env_sets_existing_binary_envs() {
+ let toolchain_dir = temp_dir_path("coverage_toolchain_env");
+ fs::create_dir_all(&toolchain_dir).expect("create toolchain env dir");
+ for binary in ["rustc", "rustdoc", "llvm-cov", "llvm-profdata"] {
+ write_file(&toolchain_dir.join(binary), "");
+ }
+
+ let mut cmd = Command::new("cargo");
+ configure_coverage_toolchain_env(&mut cmd, &toolchain_dir);
+ let envs = collect_command_envs(&cmd);
+ assert_eq!(
+ envs.get("RUSTC"),
+ Some(&Some(
+ toolchain_dir.join("rustc").to_string_lossy().to_string()
+ ))
+ );
+ assert_eq!(
+ envs.get("RUSTDOC"),
+ Some(&Some(
+ toolchain_dir.join("rustdoc").to_string_lossy().to_string()
+ ))
+ );
+ assert_eq!(
+ envs.get("LLVM_COV"),
+ Some(&Some(
+ toolchain_dir.join("llvm-cov").to_string_lossy().to_string()
+ ))
+ );
+ assert_eq!(
+ envs.get("LLVM_PROFDATA"),
+ Some(&Some(
+ toolchain_dir
+ .join("llvm-profdata")
+ .to_string_lossy()
+ .to_string()
+ ))
+ );
+
+ fs::remove_dir_all(toolchain_dir).expect("remove toolchain env dir");
+ }
+
+ #[test]
+ fn configure_coverage_toolchain_env_skips_missing_binary_envs() {
+ let toolchain_dir = temp_dir_path("coverage_toolchain_missing_env");
+ fs::create_dir_all(&toolchain_dir).expect("create missing env dir");
+
+ let mut cmd = Command::new("cargo");
+ configure_coverage_toolchain_env(&mut cmd, &toolchain_dir);
+ let envs = collect_command_envs(&cmd);
+ assert!(!envs.contains_key("RUSTC"));
+ assert!(!envs.contains_key("RUSTDOC"));
+ assert!(!envs.contains_key("LLVM_COV"));
+ assert!(!envs.contains_key("LLVM_PROFDATA"));
+
+ fs::remove_dir_all(toolchain_dir).expect("remove missing env dir");
+ }
+
+ #[test]
fn coverage_cargo_command_override_variants_cover_parented_and_parentless_paths() {
let toolchain_dir = temp_dir_path("coverage_toolchain_override");
fs::create_dir_all(&toolchain_dir).expect("create toolchain dir");
diff --git a/crates/xtask/src/export_ts.rs b/crates/xtask/src/export_ts.rs
@@ -455,14 +455,13 @@ pub fn write_ts_export_manifest(workspace_root: &Path, out_dir: &Path) -> Result
Ok(manifest_path)
}
-fn generate_ts_rs_sources_with_selector_and_runner<F>(
+type TsRsExportRunner = dyn FnMut(&str, &Path, &Path) -> Result<Output, String>;
+
+fn generate_ts_rs_sources_with_selector_and_runner(
workspace_root: &Path,
selector: Option<&str>,
- mut run_export_test: F,
-) -> Result<PathBuf, String>
-where
- F: FnMut(&str, &Path, &Path) -> Result<Output, String>,
-{
+ run_export_test: &mut TsRsExportRunner,
+) -> Result<PathBuf, String> {
let bundle = contract::load_contract_bundle(workspace_root)?;
contract::validate_contract_bundle(&bundle)?;
let ts_export = ts_export_mapping(&bundle)?;
@@ -521,7 +520,8 @@ fn generate_ts_rs_sources_with_selector(
workspace_root: &Path,
selector: Option<&str>,
) -> Result<PathBuf, String> {
- generate_ts_rs_sources_with_selector_and_runner(workspace_root, selector, run_ts_rs_export_test)
+ let mut run_export_test = run_ts_rs_export_test;
+ generate_ts_rs_sources_with_selector_and_runner(workspace_root, selector, &mut run_export_test)
}
pub fn generate_ts_rs_sources(workspace_root: &Path) -> Result<PathBuf, String> {
@@ -1457,14 +1457,16 @@ manifest_file = "nested/export-manifest.json"
let _ = fs::remove_dir_all(root_command_fail);
let root_spawn_fail = create_synthetic_workspace("generate_spawn_fail", true);
- let spawn_fail_err = generate_ts_rs_sources_with_selector_and_runner(
- &root_spawn_fail,
- None,
- |crate_name, _workspace_root, _export_dir| {
+ let mut spawn_fail_runner =
+ |crate_name: &str, _workspace_root: &Path, _export_dir: &Path| {
Err(format!(
"run cargo test for {crate_name}: synthetic spawn failure"
))
- },
+ };
+ let spawn_fail_err = generate_ts_rs_sources_with_selector_and_runner(
+ &root_spawn_fail,
+ None,
+ &mut spawn_fail_runner,
)
.expect_err("cargo spawn failure should surface");
assert_eq!(