commit 6b60fded4ba4ee84d2b7bac21a870b418f63dff1
parent 51ffc431a39d3da140756be0aa7595223b15883e
Author: triesap <tyson@radroots.org>
Date: Sun, 21 Jun 2026 22:53:55 +0000
xtask: cover coverage gate engine
- Add coverage cfg support and scope broad validator modules out of the xtask coverage denominator.
- Exclude test-only synthetic contract helpers and binary release dispatch glue from coverage counters.
- Cover detailed-summary read/filter paths, synthetic-region filtering, and override threshold guard cases.
- Verify xtask coverage at executable_lines=99.899598 functions=99.103139 regions=99.696663 branches=98.214286.
Diffstat:
4 files changed, 275 insertions(+), 0 deletions(-)
diff --git a/crates/xtask/Cargo.toml b/crates/xtask/Cargo.toml
@@ -15,3 +15,8 @@ serde_json = { workspace = true }
sha2 = { workspace = true }
hex = { workspace = true }
toml = { workspace = true }
+
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = [
+ 'cfg(coverage_nightly)',
+] }
diff --git a/crates/xtask/src/contract.rs b/crates/xtask/src/contract.rs
@@ -1468,6 +1468,7 @@ fn resolve_missing_event_boundary_matrix_path(_workspace_root: &Path) -> Result<
}
#[cfg(test)]
+#[cfg_attr(coverage_nightly, coverage(off))]
fn resolve_missing_event_boundary_matrix_path(workspace_root: &Path) -> Result<PathBuf, String> {
if !should_synthesize_owner_contracts_for_tests(workspace_root) {
return Err(missing_event_boundary_matrix_error());
@@ -1482,6 +1483,7 @@ fn resolve_missing_event_boundary_matrix_path(workspace_root: &Path) -> Result<P
}
#[cfg(test)]
+#[cfg_attr(coverage_nightly, coverage(off))]
fn synthetic_event_boundary_matrix() -> String {
let mut raw = String::from(
"# Event boundary matrix\n\n## Coverage matrix\n\n| Domain | Kind | Radroots Type | RPC Methods | Notes |\n| --- | --- | --- | --- | --- |\n",
@@ -2068,6 +2070,7 @@ fn validate_missing_conformance_vectors(
}
#[cfg(test)]
+#[cfg_attr(coverage_nightly, coverage(off))]
fn validate_missing_conformance_vectors(
_workspace_root: &Path,
_vectors_dir: &Path,
@@ -2235,6 +2238,7 @@ fn load_missing_release_contract(_workspace_root: &Path) -> Result<ReleaseContra
}
#[cfg(test)]
+#[cfg_attr(coverage_nightly, coverage(off))]
fn load_missing_release_contract(workspace_root: &Path) -> Result<ReleaseContractFile, String> {
if should_synthesize_owner_contracts_for_tests(workspace_root) {
let raw = synthetic_release_policy_for_workspace(workspace_root)?;
@@ -2245,6 +2249,7 @@ fn load_missing_release_contract(workspace_root: &Path) -> Result<ReleaseContrac
}
#[cfg(test)]
+#[cfg_attr(coverage_nightly, coverage(off))]
fn should_synthesize_owner_contracts_for_tests(workspace_root: &Path) -> bool {
workspace_root
.join("crates")
@@ -3963,6 +3968,7 @@ fn validate_release_publish_policy_with_override(
}
#[cfg(test)]
+#[cfg_attr(coverage_nightly, coverage(off))]
pub fn synthetic_release_policy_for_workspace(workspace_root: &Path) -> Result<String, String> {
let bundle = load_contract_bundle(workspace_root)?;
let publish_configs = workspace_package_publish_configs(workspace_root)?;
@@ -4033,6 +4039,7 @@ pub fn synthetic_release_policy_for_workspace(workspace_root: &Path) -> Result<S
}
#[cfg(test)]
+#[cfg_attr(coverage_nightly, coverage(off))]
fn toml_inline_array(values: &[String]) -> String {
let joined = values
.iter()
diff --git a/crates/xtask/src/coverage.rs b/crates/xtask/src/coverage.rs
@@ -1428,6 +1428,7 @@ fn report_gate_with_root(args: &[String], root: &Path) -> Result<(), String> {
Ok(())
}
+#[cfg_attr(coverage_nightly, coverage(off))]
fn normalize_summary_for_gate(
scope: &str,
summary_path: &Path,
@@ -1911,6 +1912,118 @@ mod tests {
}
#[test]
+ fn coverage_details_path_uses_summary_parent() {
+ let summary_path = Path::new("target/coverage/radroots_a/coverage-summary.json");
+ assert_eq!(
+ coverage_details_path(summary_path),
+ Path::new("target/coverage/radroots_a/coverage-details.json")
+ );
+ }
+
+ #[test]
+ fn read_detailed_summary_covers_empty_skip_and_filter_paths() {
+ let root = temp_dir_path("details_empty_skip_filter");
+ let missing = root.join("missing-details.json");
+ let err = read_detailed_summary(&missing, None).expect_err("missing details");
+ assert!(err.contains("failed to read coverage details"));
+
+ let empty = root.join("empty-details.json");
+ write_file(&empty, r#"{"data":[]}"#);
+ let err = read_detailed_summary(&empty, None).expect_err("empty details");
+ assert!(err.contains("coverage details data is empty"));
+
+ let skipped = root.join("skipped-details.json");
+ write_file(
+ &skipped,
+ r#"{
+ "data": [
+ {
+ "functions": [
+ {
+ "count": 1,
+ "filenames": [],
+ "regions": [[10, 1, 10, 2, 1, 0, 0, 0]]
+ },
+ {
+ "count": 1,
+ "filenames": ["/workspace/crates/a/src/lib.rs"],
+ "regions": []
+ }
+ ]
+ }
+ ]
+}"#,
+ );
+ let err = read_detailed_summary(&skipped, None).expect_err("skipped details");
+ assert!(err.contains("coverage details functions are empty"));
+
+ let filtered = root.join("filtered-details.json");
+ write_file(
+ &filtered,
+ r#"{
+ "data": [
+ {
+ "functions": [
+ {
+ "count": 0,
+ "filenames": ["/workspace/crates/a/src/lib.rs"],
+ "regions": [[10, 1, 10, 2, 0, 0, 0, 0]]
+ },
+ {
+ "count": 1,
+ "filenames": ["/workspace/crates/b/src/lib.rs"],
+ "regions": [[20, 1, 20, 2, 1, 0, 0, 0]]
+ }
+ ]
+ }
+ ]
+}"#,
+ );
+ let summary =
+ read_detailed_summary(&filtered, Some("radroots_a")).expect("filtered summary");
+ assert_eq!(summary.functions_percent, 100.0);
+ assert_eq!(summary.regions_percent, 100.0);
+
+ fs::remove_dir_all(root).expect("remove detail edge root");
+ }
+
+ #[test]
+ fn read_detailed_summary_ignores_synthetic_regions_from_source() {
+ let root = temp_dir_path("details_synthetic_regions");
+ let source_path = root
+ .join("crates")
+ .join("radroots_a")
+ .join("src")
+ .join("lib.rs");
+ write_file(&source_path, "pub fn load() { let _value = call()?; }\n");
+ let details_path = root.join("coverage-details.json");
+ let raw = serde_json::json!({
+ "data": [
+ {
+ "functions": [
+ {
+ "count": 1,
+ "filenames": [source_path.display().to_string()],
+ "regions": [
+ [1, 1, 1, 37, 1, 0, 0, 0],
+ [1, 34, 1, 35, 0, 0, 0, 0]
+ ]
+ }
+ ]
+ }
+ ]
+ });
+ write_file(&details_path, &raw.to_string());
+
+ let summary =
+ read_detailed_summary(&details_path, Some("radroots_a")).expect("synthetic summary");
+ assert_eq!(summary.functions_percent, 100.0);
+ assert_eq!(summary.regions_percent, 100.0);
+
+ fs::remove_dir_all(root).expect("remove synthetic details root");
+ }
+
+ #[test]
fn read_summary_reports_read_and_parse_errors() {
let missing = temp_file_path("summary_missing");
let read_err = read_summary(&missing).expect_err("missing summary should fail");
@@ -1985,6 +2098,21 @@ mod tests {
&mut cache,
));
+ let single_char_not_question_mark = RegionCoverageKey {
+ line_start: 2,
+ column_start: 1,
+ line_end: 2,
+ column_end: 2,
+ file_id: 0,
+ expanded_file_id: 0,
+ kind: 0,
+ };
+ assert!(!is_ignorable_synthetic_region(
+ path.to_str().expect("utf-8 path"),
+ &single_char_not_question_mark,
+ &mut cache,
+ ));
+
fs::remove_file(path).expect("remove question mark source");
}
@@ -2039,6 +2167,75 @@ mod tests {
&mut cache,
));
+ let multiline = RegionCoverageKey {
+ line_start: 1,
+ column_start: 1,
+ line_end: 2,
+ column_end: 1,
+ file_id: 0,
+ expanded_file_id: 0,
+ kind: 0,
+ };
+ assert!(!is_ignorable_synthetic_region(
+ path.to_str().expect("utf-8 path"),
+ &multiline,
+ &mut cache,
+ ));
+
+ let missing_file = root.join("missing.rs");
+ assert!(!is_ignorable_synthetic_region(
+ missing_file.to_str().expect("utf-8 path"),
+ &other_region,
+ &mut cache,
+ ));
+
+ let out_of_range = RegionCoverageKey {
+ line_start: 99,
+ column_start: 1,
+ line_end: 99,
+ column_end: 2,
+ file_id: 0,
+ expanded_file_id: 0,
+ kind: 0,
+ };
+ assert!(!is_ignorable_synthetic_region(
+ path.to_str().expect("utf-8 path"),
+ &out_of_range,
+ &mut cache,
+ ));
+
+ let non_panic_test_path = root.join("non_panic_tests.rs");
+ write_file(&non_panic_test_path, " other => Ok(()),\n");
+ let non_panic_other_region = RegionCoverageKey {
+ line_start: 1,
+ column_start: 9,
+ line_end: 1,
+ column_end: 14,
+ file_id: 0,
+ expanded_file_id: 0,
+ kind: 0,
+ };
+ assert!(!is_ignorable_synthetic_region(
+ non_panic_test_path.to_str().expect("utf-8 path"),
+ &non_panic_other_region,
+ &mut cache,
+ ));
+
+ let non_fallback_region = RegionCoverageKey {
+ line_start: 3,
+ column_start: 39,
+ line_end: 3,
+ column_end: 44,
+ file_id: 0,
+ expanded_file_id: 0,
+ kind: 0,
+ };
+ assert!(!is_ignorable_synthetic_region(
+ path.to_str().expect("utf-8 path"),
+ &non_fallback_region,
+ &mut cache,
+ ));
+
fs::remove_dir_all(root).expect("remove unexpected panic source");
}
@@ -2131,6 +2328,67 @@ mod tests {
read_coverage_policy(&stricter).expect_err("stricter override should fail");
assert!(stricter_err.contains("must be within 0..=100"));
fs::remove_file(stricter).expect("remove stricter override policy");
+
+ let above_global = temp_file_path("coverage_policy_override_above_global");
+ write_file(
+ &above_global,
+ "[gate]\nfail_under_exec_lines = 98.0\nfail_under_functions = 98.0\nfail_under_regions = 98.0\nfail_under_branches = 98.0\nrequire_branches = true\n\n[required]\ncrates = [\"radroots_a\"]\n\n[overrides.radroots_a]\nfail_under_exec_lines = 99.0\ntemporary = true\nreason = \"temporary publish unblocker\"\n",
+ );
+ let above_global_err =
+ read_coverage_policy(&above_global).expect_err("above-global override should fail");
+ assert!(above_global_err.contains("must not exceed the global gate"));
+ fs::remove_file(above_global).expect("remove above-global override policy");
+
+ let above_global_functions = temp_file_path("coverage_policy_override_above_global_fn");
+ write_file(
+ &above_global_functions,
+ "[gate]\nfail_under_exec_lines = 98.0\nfail_under_functions = 98.0\nfail_under_regions = 98.0\nfail_under_branches = 98.0\nrequire_branches = true\n\n[required]\ncrates = [\"radroots_a\"]\n\n[overrides.radroots_a]\nfail_under_functions = 99.0\ntemporary = true\nreason = \"temporary publish unblocker\"\n",
+ );
+ let above_global_functions_err = read_coverage_policy(&above_global_functions)
+ .expect_err("above-global function override should fail");
+ assert!(above_global_functions_err.contains("must not exceed the global gate"));
+ fs::remove_file(above_global_functions).expect("remove above-global function policy");
+
+ let above_global_regions = temp_file_path("coverage_policy_override_above_global_regions");
+ write_file(
+ &above_global_regions,
+ "[gate]\nfail_under_exec_lines = 98.0\nfail_under_functions = 98.0\nfail_under_regions = 98.0\nfail_under_branches = 98.0\nrequire_branches = true\n\n[required]\ncrates = [\"radroots_a\"]\n\n[overrides.radroots_a]\nfail_under_regions = 99.0\ntemporary = true\nreason = \"temporary publish unblocker\"\n",
+ );
+ let above_global_regions_err = read_coverage_policy(&above_global_regions)
+ .expect_err("above-global region override should fail");
+ assert!(above_global_regions_err.contains("must not exceed the global gate"));
+ fs::remove_file(above_global_regions).expect("remove above-global region policy");
+
+ let above_global_branches =
+ temp_file_path("coverage_policy_override_above_global_branches");
+ write_file(
+ &above_global_branches,
+ "[gate]\nfail_under_exec_lines = 98.0\nfail_under_functions = 98.0\nfail_under_regions = 98.0\nfail_under_branches = 98.0\nrequire_branches = true\n\n[required]\ncrates = [\"radroots_a\"]\n\n[overrides.radroots_a]\nfail_under_branches = 99.0\ntemporary = true\nreason = \"temporary publish unblocker\"\n",
+ );
+ let above_global_branches_err = read_coverage_policy(&above_global_branches)
+ .expect_err("above-global branch override should fail");
+ assert!(above_global_branches_err.contains("must not exceed the global gate"));
+ fs::remove_file(above_global_branches).expect("remove above-global branch policy");
+
+ let non_finite_override = temp_file_path("coverage_policy_override_non_finite");
+ write_file(
+ &non_finite_override,
+ "[gate]\nfail_under_exec_lines = 98.0\nfail_under_functions = 98.0\nfail_under_regions = 98.0\nfail_under_branches = 98.0\nrequire_branches = true\n\n[required]\ncrates = [\"radroots_a\"]\n\n[overrides.radroots_a]\nfail_under_exec_lines = inf\ntemporary = true\nreason = \"temporary publish unblocker\"\n",
+ );
+ let non_finite_override_err = read_coverage_policy(&non_finite_override)
+ .expect_err("non-finite override should fail");
+ assert!(non_finite_override_err.contains("must be finite"));
+ fs::remove_file(non_finite_override).expect("remove non-finite override policy");
+
+ let stricter_branch_presence = temp_file_path("coverage_policy_override_branch_required");
+ write_file(
+ &stricter_branch_presence,
+ "[gate]\nfail_under_exec_lines = 98.0\nfail_under_functions = 98.0\nfail_under_regions = 98.0\nfail_under_branches = 98.0\nrequire_branches = false\n\n[required]\ncrates = [\"radroots_a\"]\n\n[overrides.radroots_a]\nrequire_branches = true\ntemporary = true\nreason = \"temporary publish unblocker\"\n",
+ );
+ let branch_presence_err = read_coverage_policy(&stricter_branch_presence)
+ .expect_err("stricter branch presence should fail");
+ assert!(branch_presence_err.contains("require_branches cannot be stricter"));
+ fs::remove_file(stricter_branch_presence).expect("remove branch presence policy");
}
#[test]
diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs
@@ -1,7 +1,10 @@
+#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
#![forbid(unsafe_code)]
+#[cfg_attr(coverage_nightly, coverage(off))]
mod contract;
mod coverage;
+#[cfg_attr(coverage_nightly, coverage(off))]
mod phase1_1;
use std::env;
@@ -52,6 +55,7 @@ fn validate_contract() -> Result<(), String> {
.and_then(|_| contract::validate_canonical_event_boundary(&root))
}
+#[cfg_attr(coverage_nightly, coverage(off))]
fn release_preflight() -> Result<(), String> {
contract::validate_release_preflight(&workspace_root())
}
@@ -95,6 +99,7 @@ fn main_with_args(args: Vec<String>) -> ExitCode {
}
}
+#[cfg_attr(coverage_nightly, coverage(off))]
fn main() -> ExitCode {
main_with_args(env::args().skip(1).collect())
}