sdk

Radroots SDK and bindings
git clone https://radroots.dev/git/sdk.git
Log | Files | Refs | README

commit f27f3092e4e9387316da601fe847fc7aa75ac7a3
parent 24873f0d3536092a88d54c56da370da05675eeab
Author: triesap <tyson@radroots.org>
Date:   Tue, 23 Jun 2026 01:41:58 +0000

coverage: add sdk coverage command

- add an SDK coverage contract with explicit generated and bootstrap exclusions
- wire cargo xtask coverage run through rustup, llvm-cov, generation, and package checks
- require llvm-tools-preview in the pinned SDK Rust toolchain
- cover coverage contract parsing, metric validation, and pending gate behavior

Diffstat:
Acontracts/coverage.toml | 35+++++++++++++++++++++++++++++++++++
Mrust-toolchain.toml | 1+
Atools/xtask/src/coverage.rs | 413+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/xtask/src/main.rs | 15++++++++++++++-
4 files changed, 463 insertions(+), 1 deletion(-)

diff --git a/contracts/coverage.toml b/contracts/coverage.toml @@ -0,0 +1,35 @@ +[policy] +threshold = 100.0 +enforce = false +require_regions = true +require_functions = true +require_lines = true + +[toolchain] +rust = "1.92.0" +wasm_target = "wasm32-unknown-unknown" + +[report] +output = "target/sdk-coverage/summary.json" +ignore_filename_regex = "(/target/|/\\.cargo/registry/|/Cellar/rust/|/tools/xtask/|/crates/.+_bindings/|/crates/binding_model/|/crates/replica_(db|sync)_wasm/src/lib.rs|/crates/replica_db_wasm/src/wasm_impl.rs|/crates/replica_sync_wasm/src/lib.rs)" + +[generated] +typescript = "generated TypeScript is excluded from line coverage and verified by generator tests, generated-output reproducibility, package export checks, and pnpm typecheck" +binding_crates = "generated Rust binding crates are excluded from line coverage and verified by generator unit tests plus generated TypeScript reproducibility" +wasm_glue = "wasm-bindgen glue is excluded only while pure SDK behavior is covered by native extracted-logic tests and generated package checks" + +[exclusions.generated_typescript] +paths = ["packages/*/src/generated/**", "packages/*/dist/**"] +reason = "generated package output is checked through cargo xtask check, cargo xtask generate ts, cargo xtask generate wasm, and pnpm typecheck" + +[exclusions.generated_binding_crates] +paths = ["crates/*_bindings/**", "crates/binding_model/**"] +reason = "binding crates are generator-owned source facades with behavior covered by xtask generator tests" + +[exclusions.wasm_glue_bootstrap] +paths = ["crates/replica_db_wasm/src/wasm_impl.rs", "crates/replica_sync_wasm/src/lib.rs"] +reason = "temporary bootstrap exclusion until WASM behavior is extracted and covered by the WASM coverage refactor slice" + +[exclusions.xtask_bootstrap] +paths = ["tools/xtask/**"] +reason = "temporary bootstrap exclusion until coverage command behavior is stable and the final gate includes xtask coverage" diff --git a/rust-toolchain.toml b/rust-toolchain.toml @@ -1,3 +1,4 @@ [toolchain] channel = "1.92.0" +components = ["llvm-tools-preview"] targets = ["wasm32-unknown-unknown"] diff --git a/tools/xtask/src/coverage.rs b/tools/xtask/src/coverage.rs @@ -0,0 +1,413 @@ +use std::{collections::BTreeMap, fs, path::Path, process::Command}; + +use serde::Deserialize; + +use crate::{check, fs::workspace_root, generate, wasm}; + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct CoverageContract { + policy: CoveragePolicy, + toolchain: CoverageToolchain, + report: CoverageReport, + generated: GeneratedCoveragePolicy, + exclusions: BTreeMap<String, CoverageExclusion>, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct CoveragePolicy { + threshold: f64, + enforce: bool, + require_regions: bool, + require_functions: bool, + require_lines: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct CoverageToolchain { + rust: String, + wasm_target: String, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct CoverageReport { + output: String, + ignore_filename_regex: String, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct GeneratedCoveragePolicy { + typescript: String, + binding_crates: String, + wasm_glue: String, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct CoverageExclusion { + paths: Vec<String>, + reason: String, +} + +#[derive(Debug, Deserialize)] +struct LlvmCovReport { + data: Vec<LlvmCovData>, +} + +#[derive(Debug, Deserialize)] +struct LlvmCovData { + totals: LlvmCovSummary, +} + +#[derive(Debug, Deserialize)] +struct LlvmCovSummary { + lines: LlvmCovMetric, + functions: LlvmCovMetric, + regions: LlvmCovMetric, +} + +#[derive(Debug, Deserialize)] +struct LlvmCovMetric { + count: u64, + covered: u64, + percent: f64, +} + +pub fn run(args: &[String]) -> Result<(), String> { + match args { + [command] if command == "run" => run_coverage(), + [] => Err(usage()), + _ => Err(usage()), + } +} + +fn usage() -> String { + "usage: cargo xtask coverage run".to_owned() +} + +fn run_coverage() -> Result<(), String> { + let root = workspace_root()?; + let contract = load_contract(&root)?; + validate_contract(&contract)?; + preflight(&contract)?; + generate::generate_ts()?; + wasm::generate(&[])?; + check::check()?; + run_llvm_cov(&root, &contract)?; + evaluate_report(&root.join(&contract.report.output), &contract) +} + +fn load_contract(root: &Path) -> Result<CoverageContract, String> { + let path = root.join("contracts").join("coverage.toml"); + let raw = fs::read_to_string(&path) + .map_err(|error| format!("failed to read {}: {error}", path.display()))?; + toml::from_str(&raw).map_err(|error| format!("failed to parse {}: {error}", path.display())) +} + +fn validate_contract(contract: &CoverageContract) -> Result<(), String> { + if !(0.0..=100.0).contains(&contract.policy.threshold) { + return Err( + "contracts/coverage.toml policy.threshold must be between 0 and 100".to_owned(), + ); + } + if contract.policy.threshold != 100.0 { + return Err("contracts/coverage.toml policy.threshold must be 100.0".to_owned()); + } + validate_non_empty(&contract.toolchain.rust, "toolchain.rust")?; + validate_non_empty(&contract.toolchain.wasm_target, "toolchain.wasm_target")?; + validate_non_empty(&contract.report.output, "report.output")?; + validate_non_empty( + &contract.report.ignore_filename_regex, + "report.ignore_filename_regex", + )?; + validate_non_empty(&contract.generated.typescript, "generated.typescript")?; + validate_non_empty( + &contract.generated.binding_crates, + "generated.binding_crates", + )?; + validate_non_empty(&contract.generated.wasm_glue, "generated.wasm_glue")?; + if contract.exclusions.is_empty() { + return Err("contracts/coverage.toml exclusions must not be empty".to_owned()); + } + for (name, exclusion) in &contract.exclusions { + validate_non_empty(name, "exclusion name")?; + validate_non_empty(&exclusion.reason, &format!("exclusions.{name}.reason"))?; + if exclusion.paths.is_empty() { + return Err(format!("exclusions.{name}.paths must not be empty")); + } + for path in &exclusion.paths { + validate_non_empty(path, &format!("exclusions.{name}.paths entry"))?; + } + } + Ok(()) +} + +fn validate_non_empty(value: &str, field: &str) -> Result<(), String> { + if value.trim().is_empty() { + Err(format!("contracts/coverage.toml {field} must not be empty")) + } else { + Ok(()) + } +} + +fn preflight(contract: &CoverageContract) -> Result<(), String> { + require_command("rustup", &["--version"], "install rustup")?; + require_command( + "rustup", + &[ + "run", + &contract.toolchain.rust, + "cargo", + "llvm-cov", + "--version", + ], + "install cargo-llvm-cov for the SDK Rust toolchain", + )?; + let component_output = output( + "rustup", + &["component", "list", "--toolchain", &contract.toolchain.rust], + ) + .map_err(|error| format!("failed to inspect Rust components: {error}"))?; + if !component_output + .lines() + .any(|line| line.starts_with("llvm-tools") && line.contains("(installed)")) + { + return Err(format!( + "missing llvm-tools-preview for Rust toolchain {}; run `rustup component add llvm-tools-preview --toolchain {}`", + contract.toolchain.rust, contract.toolchain.rust + )); + } + let target_output = output( + "rustup", + &["target", "list", "--toolchain", &contract.toolchain.rust], + ) + .map_err(|error| format!("failed to inspect Rust targets: {error}"))?; + let expected_target = format!("{} (installed)", contract.toolchain.wasm_target); + if !target_output.lines().any(|line| line == expected_target) { + return Err(format!( + "missing Rust target {}; run `rustup target add {} --toolchain {}`", + contract.toolchain.wasm_target, contract.toolchain.wasm_target, contract.toolchain.rust + )); + } + require_command("wasm-pack", &["--version"], "install wasm-pack")?; + require_command("pnpm", &["--version"], "install pnpm")?; + Ok(()) +} + +fn require_command(command: &str, args: &[&str], install_hint: &str) -> Result<(), String> { + output(command, args) + .map(|_| ()) + .map_err(|error| format!("missing {command}: {error}; {install_hint}")) +} + +fn output(command: &str, args: &[&str]) -> Result<String, String> { + let output = Command::new(command) + .args(args) + .output() + .map_err(|error| error.to_string())?; + if !output.status.success() { + return Err(format!( + "{}", + String::from_utf8_lossy(&output.stderr).trim() + )); + } + Ok(String::from_utf8_lossy(&output.stdout).to_string()) +} + +fn run_llvm_cov(root: &Path, contract: &CoverageContract) -> Result<(), String> { + let output_path = root.join(&contract.report.output); + if let Some(parent) = output_path.parent() { + fs::create_dir_all(parent) + .map_err(|error| format!("failed to create {}: {error}", parent.display()))?; + } + let status = Command::new("rustup") + .current_dir(root) + .args([ + "run", + &contract.toolchain.rust, + "cargo", + "llvm-cov", + "--workspace", + "--all-features", + "--summary-only", + "--json", + "--output-path", + &contract.report.output, + "--ignore-filename-regex", + &contract.report.ignore_filename_regex, + "--no-clean", + "--no-fail-fast", + ]) + .status() + .map_err(|error| format!("failed to run cargo llvm-cov: {error}"))?; + if !status.success() { + return Err(format!("cargo llvm-cov failed with status {status}")); + } + Ok(()) +} + +fn evaluate_report(report_path: &Path, contract: &CoverageContract) -> Result<(), String> { + let raw = fs::read_to_string(report_path) + .map_err(|error| format!("failed to read {}: {error}", report_path.display()))?; + let report = serde_json::from_str::<LlvmCovReport>(&raw) + .map_err(|error| format!("failed to parse {}: {error}", report_path.display()))?; + let data = report + .data + .first() + .ok_or_else(|| format!("{} did not include coverage data", report_path.display()))?; + validate_metric("lines", &data.totals.lines, contract.policy.require_lines)?; + validate_metric( + "functions", + &data.totals.functions, + contract.policy.require_functions, + )?; + validate_metric( + "regions", + &data.totals.regions, + contract.policy.require_regions, + )?; + if !contract.policy.enforce { + println!( + "coverage policy parsed and measured; enforcement pending final hardening gate at {}", + report_path.display() + ); + return Ok(()); + } + enforce_metric("lines", &data.totals.lines, contract.policy.threshold)?; + enforce_metric( + "functions", + &data.totals.functions, + contract.policy.threshold, + )?; + enforce_metric("regions", &data.totals.regions, contract.policy.threshold)?; + println!( + "coverage policy passed at {:.1}% using {}", + contract.policy.threshold, + report_path.display() + ); + Ok(()) +} + +fn validate_metric(name: &str, metric: &LlvmCovMetric, required: bool) -> Result<(), String> { + if required && metric.count == 0 { + return Err(format!( + "coverage report did not include required {name} records" + )); + } + if metric.covered > metric.count { + return Err(format!("coverage report has invalid {name} counts")); + } + Ok(()) +} + +fn enforce_metric(name: &str, metric: &LlvmCovMetric, threshold: f64) -> Result<(), String> { + if metric.percent < threshold { + return Err(format!( + "coverage {name} {:.3}% is below required {:.1}%", + metric.percent, threshold + )); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::{ + CoverageContract, enforce_metric, evaluate_report, validate_contract, validate_metric, + }; + + const CONTRACT: &str = r#" +[policy] +threshold = 100.0 +enforce = false +require_regions = true +require_functions = true +require_lines = true + +[toolchain] +rust = "1.92.0" +wasm_target = "wasm32-unknown-unknown" + +[report] +output = "target/sdk-coverage/summary.json" +ignore_filename_regex = "generated" + +[generated] +typescript = "excluded because generated TypeScript is owned by Rust source generators" +binding_crates = "excluded because binding crates are generated source facades" +wasm_glue = "excluded because wasm-bindgen glue is verified through generated package checks" + +[exclusions.generated] +paths = ["packages/*/src/generated/**"] +reason = "generated package output is checked through reproducibility" +"#; + + #[test] + fn validates_contract_shape() { + let contract = toml::from_str::<CoverageContract>(CONTRACT).expect("contract parses"); + validate_contract(&contract).expect("contract validates"); + } + + #[test] + fn rejects_non_100_thresholds() { + let raw = CONTRACT.replace("threshold = 100.0", "threshold = 99.0"); + let contract = toml::from_str::<CoverageContract>(&raw).expect("contract parses"); + assert!(validate_contract(&contract).is_err()); + } + + #[test] + fn accepts_required_metric_counts() { + let metric = super::LlvmCovMetric { + count: 10, + covered: 10, + percent: 100.0, + }; + validate_metric("lines", &metric, true).expect("metric validates"); + enforce_metric("lines", &metric, 100.0).expect("metric passes"); + } + + #[test] + fn rejects_missing_required_metric_counts() { + let metric = super::LlvmCovMetric { + count: 0, + covered: 0, + percent: 0.0, + }; + assert!(validate_metric("lines", &metric, true).is_err()); + } + + #[test] + fn rejects_under_threshold_metric() { + let metric = super::LlvmCovMetric { + count: 10, + covered: 9, + percent: 90.0, + }; + assert!(enforce_metric("lines", &metric, 100.0).is_err()); + } + + #[test] + fn pending_enforcement_accepts_measured_report() { + let dir = std::env::temp_dir().join(format!( + "radroots_sdk_xtask_coverage_{}", + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("time") + .as_nanos() + )); + std::fs::create_dir_all(&dir).expect("dir"); + let report_path = dir.join("summary.json"); + std::fs::write( + &report_path, + r#"{"data":[{"totals":{"lines":{"count":1,"covered":0,"percent":0.0},"functions":{"count":1,"covered":0,"percent":0.0},"regions":{"count":1,"covered":0,"percent":0.0}}}]}"#, + ) + .expect("report"); + let contract = toml::from_str::<CoverageContract>(CONTRACT).expect("contract parses"); + evaluate_report(&report_path, &contract).expect("pending report passes"); + std::fs::remove_dir_all(dir).expect("cleanup"); + } +} diff --git a/tools/xtask/src/main.rs b/tools/xtask/src/main.rs @@ -1,5 +1,6 @@ mod check; mod contracts; +mod coverage; mod fs; mod generate; mod manifest; @@ -11,6 +12,7 @@ mod wasm; enum CommandAction<'a> { GenerateTs, GenerateWasm(&'a [String]), + Coverage(&'a [String]), Check, } @@ -26,6 +28,7 @@ fn run(args: impl IntoIterator<Item = String>) -> Result<(), String> { match command_action(&args)? { CommandAction::GenerateTs => generate::generate_ts(), CommandAction::GenerateWasm(rest) => wasm::generate(rest), + CommandAction::Coverage(rest) => coverage::run(rest), CommandAction::Check => check::check(), } } @@ -38,6 +41,7 @@ fn command_action(args: &[String]) -> Result<CommandAction<'_>, String> { [command, target, rest @ ..] if command == "generate" && target == "wasm" => { Ok(CommandAction::GenerateWasm(rest)) } + [command, rest @ ..] if command == "coverage" => Ok(CommandAction::Coverage(rest)), [command] if command == "check" => Ok(CommandAction::Check), [] => Err(usage()), _ => Err(usage()), @@ -45,7 +49,7 @@ fn command_action(args: &[String]) -> Result<CommandAction<'_>, String> { } fn usage() -> String { - "usage: cargo xtask generate ts | cargo xtask generate wasm [--package <key>] | cargo xtask check" + "usage: cargo xtask generate ts | cargo xtask generate wasm [--package <key>] | cargo xtask check | cargo xtask coverage run" .to_owned() } @@ -81,6 +85,15 @@ mod tests { } #[test] + fn accepts_coverage_run() { + let args = ["coverage".to_owned(), "run".to_owned()]; + assert!(matches!( + command_action(&args).expect("action"), + CommandAction::Coverage(rest) if rest == ["run"] + )); + } + + #[test] fn rejects_unknown_command() { let args = ["generate".to_owned(), "swift".to_owned()]; assert!(command_action(&args).is_err());