//! Registry freshness report (`SMH-SPEC-SPEC0001`). use std::{ collections::{BTreeMap, BTreeSet}, env, fs, io::Read, path::{Path, PathBuf}, process::{Child, Command, ExitCode, Stdio}, sync::atomic::{AtomicUsize, Ordering}, thread, time::{Duration, Instant}, }; type DirectDependencyKey = (Option, String, String, String); type DirectDependencyRequirements = (BTreeSet, bool); const CRATES_IO_SOURCE: &str = "registry+https://github.com/rust-lang/crates.io-index"; const INPUT_LIMIT: usize = 16 * 1024 * 1024; const OUTPUT_LIMIT: usize = 64 * 1024 * 1024; const COMMAND_TIMEOUT: Duration = Duration::from_secs(120); static TEMP_COUNTER: AtomicUsize = AtomicUsize::new(0); pub(super) fn run(args: &[std::ffi::OsString]) -> ExitCode { if args.first().is_some_and(|argument| argument == "--verify") { return verify(&args[1..]); } let offline = match args { [] => false, [argument] if argument == "--offline" => true, _ => { eprintln!( "usage: cargo x deps [--offline] | --verify [-p package] [--target triple]" ); return ExitCode::FAILURE; } }; match report(offline) { Ok(text) => { if offline { println!("Offline registry-cache evidence only; upstream freshness is unverified."); } print!("{text}"); ExitCode::SUCCESS } Err(error) => { eprintln!("dependency freshness: {error}"); ExitCode::FAILURE } } } const RUNTIME_LIBRARIES: &[&str] = &[ "smith", "smith-core", "smith-ai", "smith-ui", "smith-tui", "smith-rpc", "smith-harness", "smith-alloc", ]; fn verify(args: &[std::ffi::OsString]) -> ExitCode { let Some(mode) = args.first().and_then(|mode| mode.to_str()) else { print_verify_usage(); return ExitCode::FAILURE; }; if !matches!(mode, "locked" | "fresh-compatible" | "direct-minimum") { print_verify_usage(); return ExitCode::FAILURE; } let mut package = None; let mut target = None; let mut index = 1; while index < args.len() { let Some(flag) = args[index].to_str() else { print_verify_usage(); return ExitCode::FAILURE; }; let Some(value) = args.get(index + 1).and_then(|value| value.to_str()) else { print_verify_usage(); return ExitCode::FAILURE; }; if value.is_empty() || value.starts_with('-') { print_verify_usage(); return ExitCode::FAILURE; } let slot = match flag { "-p" | "--package" => &mut package, "--target" => &mut target, _ => { print_verify_usage(); return ExitCode::FAILURE; } }; if slot.replace(value).is_some() { print_verify_usage(); return ExitCode::FAILURE; } index += 2; } match verify_resolution(mode, package, target) { Ok(()) => ExitCode::SUCCESS, Err(error) => { eprintln!("dependency resolution ({mode}): {error}"); ExitCode::FAILURE } } } fn print_verify_usage() { eprintln!( "usage: cargo x deps --verify [-p package] [--target triple]" ); } fn verify_resolution( mode: &str, package: Option<&str>, target: Option<&str>, ) -> Result<(), String> { let selected = match package { Some(name) if RUNTIME_LIBRARIES.contains(&name) => vec![name], Some(name) => return Err(format!("unknown runtime library package {name}")), None => RUNTIME_LIBRARIES.to_vec(), }; let target = target.map_or_else(host_target, |target| Ok(target.to_owned()))?; if mode == "locked" { let mut args = vec!["check", "--locked", "--target", &target]; for name in &selected { args.extend(["-p", name]); } cargo_output_at(Path::new("."), &args, INPUT_LIMIT)?; println!( "Locked verification checked target {} using checkout Cargo.lock for {}.", target, selected.join(", ") ); return Ok(()); } let root = Path::new(env!("CARGO_MANIFEST_DIR")) .parent() .ok_or("xtask manifest is not inside workspace")?; let metadata = cargo_output_at( root, &["metadata", "--locked", "--format-version=1"], INPUT_LIMIT, )?; let temporary = TempRoot::new()?; let consumer = temporary.new_directory()?; write_consumer(&consumer, &metadata, &selected, mode == "direct-minimum")?; if mode == "direct-minimum" { cargo_output_at( &consumer, &["+nightly", "generate-lockfile", "-Zdirect-minimal-versions"], INPUT_LIMIT, )?; } else { cargo_output_at(&consumer, &["generate-lockfile"], INPUT_LIMIT)?; } cargo_output_at( &consumer, &["+stable", "check", "--locked", "--target", &target], INPUT_LIMIT, )?; println!( "{} verification passed for target {} for {} in isolated consumer; checkout Cargo.lock unchanged.", mode, target, selected.join(", ") ); Ok(()) } fn host_target() -> Result { let output = cargo_output_at(Path::new("."), &["-vV"], INPUT_LIMIT)?; String::from_utf8(output) .map_err(|error| error.to_string())? .lines() .find_map(|line| line.strip_prefix("host: ")) .map(str::to_owned) .ok_or_else(|| "cargo -vV did not report a host target".into()) } fn write_consumer( directory: &Path, metadata: &[u8], selected: &[&str], minimum: bool, ) -> Result<(), String> { let value: serde_json::Value = serde_json::from_slice(metadata).map_err(|error| error.to_string())?; let packages = value["packages"] .as_array() .ok_or("metadata has no packages")?; let mut lines = vec![ "[workspace]\n\n[package]".to_owned(), "name = \"smith-downstream-check\"".to_owned(), "version = \"0.0.0\"".to_owned(), "edition = \"2024\"".to_owned(), "".to_owned(), "[dependencies]".to_owned(), ]; let mut direct_dependencies: BTreeMap = BTreeMap::new(); let mut direct_aliases: BTreeMap<(String, String), String> = BTreeMap::new(); let mut direct_dependency_index = 0; for name in selected { let package = packages .iter() .find(|package| package["name"].as_str() == Some(name) && package["source"].is_null()) .ok_or_else(|| format!("workspace package {name} missing from Cargo metadata"))?; let manifest = package["manifest_path"] .as_str() .ok_or("package has no manifest path")?; let crate_path = Path::new(manifest) .parent() .ok_or("package manifest has no parent")?; lines.push(format!( "{name} = {{ path = {} }}", toml_string(&crate_path.display().to_string()) )); if minimum { for dependency in package["dependencies"] .as_array() .ok_or("package has no dependencies")? { if dependency["kind"].as_str() == Some("dev") || dependency["optional"].as_bool() == Some(true) { continue; } let Some(source) = dependency["source"].as_str() else { continue; }; if !source.starts_with("registry+") { continue; } if source != CRATES_IO_SOURCE { return Err(format!( "direct registry dependency uses unsupported source {source}; direct-minimum supports crates.io only" )); } let dep_name = dependency["name"] .as_str() .ok_or("dependency has no name")?; let requirement = dependency["req"] .as_str() .ok_or("dependency has no requirement")?; let target = dependency["target"].as_str().map(str::to_owned); let kind = if dependency["kind"].as_str() == Some("build") { "build-dependencies" } else { "dependencies" }; direct_aliases .entry((dep_name.to_owned(), requirement.to_owned())) .or_insert_with(|| { let alias = format!("smith_direct_{direct_dependency_index}"); direct_dependency_index += 1; alias }); let features = dependency["features"] .as_array() .ok_or("dependency has no feature list")?; if !features.is_empty() { for feature in features { let feature = feature .as_str() .ok_or_else(|| "dependency feature is not a string".to_owned())?; direct_dependencies .entry(( target.clone(), kind.to_owned(), dep_name.to_owned(), requirement.to_owned(), )) .or_default() .0 .insert(feature.to_owned()); } } let requirements = direct_dependencies .entry(( target, kind.to_owned(), dep_name.to_owned(), requirement.to_owned(), )) .or_default(); requirements.1 |= dependency["uses_default_features"].as_bool() != Some(false); } } } let mut tables: BTreeMap<(Option, String), Vec> = BTreeMap::new(); for ((target, kind, dep_name, requirement), (features, default_features)) in direct_dependencies { let alias = &direct_aliases[&(dep_name.clone(), requirement.clone())]; let mut declaration = format!( "{alias} = {{ package = {}, version = {}", toml_string(&dep_name), toml_string(&requirement) ); if !features.is_empty() { let features = features .iter() .map(|feature| toml_string(feature)) .collect::>(); declaration.push_str(&format!(", features = [{}]", features.join(", "))); } if !default_features { declaration.push_str(", default-features = false"); } declaration.push_str(" }"); tables.entry((target, kind)).or_default().push(declaration); } for ((target, kind), declarations) in tables { if target.is_none() && kind == "dependencies" { lines.extend(declarations); continue; } if let Some(target) = target { lines.push(format!("[target.{}.{}]", toml_string(&target), kind)); } else { lines.push(format!("[{kind}]")); } lines.extend(declarations); } let manifest = lines.join("\n") + "\n"; fs::write(directory.join("Cargo.toml"), manifest).map_err(|error| error.to_string())?; fs::create_dir(directory.join("src")).map_err(|error| error.to_string())?; fs::write(directory.join("src/main.rs"), "fn main() {}\n") .map_err(|error| error.to_string())?; Ok(()) } fn toml_string(value: &str) -> String { serde_json::to_string(value).unwrap_or_else(|_| "\"invalid\"".to_owned()) } fn report(offline: bool) -> Result { let mut args = vec!["metadata", "--locked", "--format-version=1"]; if offline { args.push("--offline"); } let metadata = cargo_output_at(Path::new("."), &args, INPUT_LIMIT)?; let rationale = root_rationales(Path::new("Cargo.toml"))?; let temporary = TempRoot::new()?; let mut cache: BTreeMap<(String, String), String> = BTreeMap::new(); report_from_metadata_with_rationale(&metadata, &rationale, |name, requirement| { let key = (name.to_owned(), requirement.to_owned()); if let Some(version) = cache.get(&key) { return Ok(version.clone()); } let version = probe(&temporary, name, requirement, offline)?; cache.insert(key, version.clone()); Ok(version) }) } fn report_from_metadata_with_rationale( metadata: &[u8], rationale: &BTreeMap, mut resolve: F, ) -> Result where F: FnMut(&str, &str) -> Result, { let root: serde_json::Value = serde_json::from_slice(metadata).map_err(|error| error.to_string())?; let members: BTreeSet<&str> = root["workspace_members"] .as_array() .ok_or("metadata has no workspace_members")? .iter() .map(|item| item.as_str().ok_or("workspace member id is not a string")) .collect::>()?; let packages = root["packages"] .as_array() .ok_or("metadata has no packages")?; let nodes = root["resolve"]["nodes"] .as_array() .ok_or("metadata has no resolve graph")?; let mut rows = Vec::new(); for package in packages.iter().filter(|package| { package["id"] .as_str() .is_some_and(|id| members.contains(id)) }) { let id = package["id"].as_str().ok_or("package has no id")?; let node = nodes .iter() .find(|node| node["id"].as_str() == Some(id)) .ok_or("workspace package missing from resolve graph")?; let dependencies = package["dependencies"] .as_array() .ok_or("package has no dependencies")?; let edges = node["deps"].as_array().ok_or("resolve node has no deps")?; for dependency in dependencies { let Some(source) = dependency["source"].as_str() else { continue; }; if source != CRATES_IO_SOURCE { continue; } let name = dependency["name"] .as_str() .ok_or("dependency has no name")?; let normalized_name = name.replace('-', "_"); let edge_name = dependency["rename"].as_str().unwrap_or(&normalized_name); let requirement = dependency["req"] .as_str() .ok_or("dependency has no requirement")?; let locked_id = edges .iter() .find(|edge| edge["name"].as_str() == Some(edge_name)) .and_then(|edge| edge["pkg"].as_str()) .ok_or_else(|| { format!("registry dependency edge {edge_name} missing from resolve graph") })?; let locked_package = packages .iter() .find(|candidate| { candidate["id"].as_str() == Some(locked_id) && candidate["name"].as_str() == Some(name) && candidate["source"].as_str() == Some(source) }) .ok_or_else(|| { format!( "resolved package for {edge_name} does not match dependency source/name" ) })?; let locked = locked_package["version"] .as_str() .ok_or("locked package has no version")?; let compatible = resolve(name, requirement)?; let latest = resolve(name, "*")?; let kind = dependency["kind"].as_str().unwrap_or("normal"); let target = dependency["target"].as_str().unwrap_or("all-targets"); let reason = rationale .get(name) .map(String::as_str) .unwrap_or("not recorded in Cargo.toml inline comments"); rows.push(format!( "{} | {} | {} | {} | {} | {} | {} | {} | {}", package["name"].as_str().unwrap_or("?"), kind, target, name, requirement, locked, compatible, latest, reason )); } } rows.sort(); if rows.is_empty() { return Err("no registry dependencies found; evidence incomplete".into()); } Ok(format!( "Crates.io only: git, path, and alternate registries excluded; compatibility resolves with default features disabled.\nmember | scope | target | dependency | requirement | locked | latest compatible | latest stable | constraint reason\n{}\n", rows.join("\n") )) } #[cfg(test)] fn report_from_metadata(metadata: &[u8], mut resolve: F) -> Result where F: FnMut(&str) -> Result, { report_from_metadata_with_rationale(metadata, &BTreeMap::new(), |name, requirement| { resolve(&format!("{name} {requirement}")) }) } /// Reads comments for direct assignments in the root workspace dependency table only. fn root_rationales(path: &Path) -> Result, String> { let text = fs::read_to_string(path).map_err(|error| format!("read {}: {error}", path.display()))?; let mut in_workspace_dependencies = false; let mut pending_comments = Vec::new(); let mut declaration: Option<(String, usize, Vec)> = None; let mut rationales = BTreeMap::new(); for line in text.lines().map(str::trim) { if line.starts_with('[') && line.ends_with(']') { in_workspace_dependencies = line == "[workspace.dependencies]"; pending_comments.clear(); continue; } if !in_workspace_dependencies { continue; } let (code, comment) = line.split_once('#').unwrap_or((line, "")); let code = code.trim(); let comment = comment.trim(); let comment = if is_dependency_heading(comment) { "" } else { comment }; if code.is_empty() { if !comment.is_empty() { if let Some((_, _, comments)) = &mut declaration { comments.push(comment.to_owned()); } else { pending_comments.push(comment.to_owned()); } } else if line.is_empty() { pending_comments.clear(); } continue; } if !comment.is_empty() { if let Some((_, _, comments)) = &mut declaration { comments.push(comment.to_owned()); } else { pending_comments.push(comment.to_owned()); } } consume_rationale_line( code, &mut declaration, &mut pending_comments, &mut rationales, ); } finish_rationale(&mut declaration, &mut rationales); Ok(rationales) } fn is_dependency_heading(comment: &str) -> bool { [ "Dependency tiers", "vocabulary", "engines", "leaf utilities", "dev", "build tools", "xtask-installed", ] .iter() .any(|heading| comment.starts_with(heading)) } fn consume_rationale_line( line: &str, declaration: &mut Option<(String, usize, Vec)>, pending_comments: &mut Vec, rationales: &mut BTreeMap, ) { if let Some((_, depth, _)) = declaration { *depth = depth.saturating_add(line.matches('{').count()); *depth = depth.saturating_sub(line.matches('}').count()); if *depth == 0 { finish_rationale(declaration, rationales); } return; } let Some((name, value)) = line.split_once('=') else { return; }; let name = name.trim(); if name.is_empty() || !name .bytes() .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_')) { pending_comments.clear(); return; } let depth = value .matches('{') .count() .saturating_sub(value.matches('}').count()); *declaration = Some((name.to_owned(), depth, std::mem::take(pending_comments))); if depth == 0 { finish_rationale(declaration, rationales); } } fn finish_rationale( declaration: &mut Option<(String, usize, Vec)>, rationales: &mut BTreeMap, ) { if let Some((name, _, comments)) = declaration.take() && !comments.is_empty() { rationales.insert(name, comments.join(" ")); } } fn probe_manifest(name: &str, requirement: &str) -> Result { let name = serde_json::to_string(name).map_err(|error| error.to_string())?; let requirement = serde_json::to_string(requirement).map_err(|error| error.to_string())?; Ok(format!( "[workspace]\n\n[package]\nname = \"smith-dependency-probe\"\nversion = \"0.0.0\"\nedition = \"2024\"\n[dependencies]\nprobe = {{ package = {name}, version = {requirement}, default-features = false }}\n" )) } fn probe(root: &TempRoot, name: &str, requirement: &str, offline: bool) -> Result { if name.len() > 256 || requirement.len() > 256 { return Err("dependency input exceeds 256 bytes".into()); } let directory = root.new_directory()?; fs::create_dir(directory.join("src")).map_err(|error| error.to_string())?; fs::write( directory.join("Cargo.toml"), probe_manifest(name, requirement)?, ) .map_err(|error| error.to_string())?; fs::write(directory.join("src/main.rs"), "fn main() {}\n") .map_err(|error| error.to_string())?; let mut args = vec!["metadata", "--format-version=1"]; if offline { args.push("--offline"); } let metadata = cargo_output_at(&directory, &args, INPUT_LIMIT)?; let root: serde_json::Value = serde_json::from_slice(&metadata).map_err(|error| error.to_string())?; let packages = root["packages"] .as_array() .ok_or("probe metadata has no packages")?; packages .iter() .find(|package| { package["name"].as_str() == Some(name) && package["source"] .as_str() .is_some_and(|source| source == CRATES_IO_SOURCE) }) .and_then(|package| package["version"].as_str()) .map(str::to_owned) .ok_or_else(|| format!("no registry evidence for {name} {requirement}")) } struct TempRoot(PathBuf); impl TempRoot { fn new() -> Result { let base = env::temp_dir(); for _ in 0..100 { let attempt = TEMP_COUNTER.fetch_add(1, Ordering::Relaxed); let path = base.join(format!("smith-dependency-{}-{attempt}", std::process::id())); match fs::create_dir(&path) { Ok(()) => return Ok(Self(path)), Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => continue, Err(error) => return Err(format!("create {}: {error}", path.display())), } } Err("unable to exclusively create dependency scratch directory".into()) } fn new_directory(&self) -> Result { for _ in 0..100 { let attempt = TEMP_COUNTER.fetch_add(1, Ordering::Relaxed); let path = self.0.join(format!("probe-{attempt}")); match fs::create_dir(&path) { Ok(()) => return Ok(path), Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => continue, Err(error) => return Err(error.to_string()), } } Err("unable to exclusively create probe directory".into()) } } impl Drop for TempRoot { fn drop(&mut self) { let _ = fs::remove_dir_all(&self.0); } } fn cargo_output_at(path: &Path, args: &[&str], limit: usize) -> Result, String> { let mut child = Command::new("cargo") .args(args) .current_dir(path) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .map_err(|error| format!("start cargo: {error}"))?; let stdout = child.stdout.take().ok_or("cargo stdout unavailable")?; let stderr = child.stderr.take().ok_or("cargo stderr unavailable")?; let out_reader = thread::spawn(move || bounded_read(stdout, limit)); let err_reader = thread::spawn(move || bounded_read(stderr, OUTPUT_LIMIT)); let status = wait_deadline(&mut child)?; let output = out_reader .join() .map_err(|_| "cargo stdout reader panicked")??; let errors = err_reader .join() .map_err(|_| "cargo stderr reader panicked")??; if output.1 || errors.1 { return Err(format!( "cargo output exceeds configured limit ({limit} stdout bytes)" )); } if !status.success() { return Err(format!( "cargo {} failed: {}", args.first().copied().unwrap_or("command"), String::from_utf8_lossy(&errors.0) )); } Ok(output.0) } fn bounded_read(mut reader: impl Read, limit: usize) -> Result<(Vec, bool), String> { let mut bytes = Vec::with_capacity(limit.min(64 * 1024)); let mut exceeded = false; let mut buffer = [0; 8192]; loop { let count = reader .read(&mut buffer) .map_err(|error| error.to_string())?; if count == 0 { break; } let remaining = limit.saturating_sub(bytes.len()); bytes.extend_from_slice(&buffer[..count.min(remaining)]); exceeded |= count > remaining; } Ok((bytes, exceeded)) } fn wait_deadline(child: &mut Child) -> Result { let deadline = Instant::now() + COMMAND_TIMEOUT; loop { if let Some(status) = child.try_wait().map_err(|error| error.to_string())? { return Ok(status); } if Instant::now() >= deadline { let _ = child.kill(); let _ = child.wait(); return Err("cargo command exceeded 120-second deadline".into()); } thread::sleep(Duration::from_millis(20)); } } #[cfg(test)] mod tests { //! Tests prove `SMH-SPEC-SPEC0001`. use super::*; #[test] fn direct_minimum_rejects_too_low_path_fixture_then_accepts_declared_floor() { let scratch = TempRoot::new().unwrap(); let fixture = scratch.new_directory().unwrap(); let dependency = fixture.join("dep"); let smith = fixture.join("smith"); let consumer = fixture.join("consumer"); fs::create_dir_all(dependency.join("src")).unwrap(); fs::create_dir_all(smith.join("src")).unwrap(); fs::create_dir_all(consumer.join("src")).unwrap(); fs::write( dependency.join("Cargo.toml"), "[package]\nname='floor-dep'\nversion='1.1.0'\nedition='2024'\n", ) .unwrap(); fs::write(dependency.join("src/lib.rs"), "pub fn value() {}\n").unwrap(); fs::write(smith.join("Cargo.toml"), "[package]\nname='smith-fixture'\nversion='0.1.0'\nedition='2024'\n[dependencies]\nfloor-dep={path='../dep', version='>=1.1'}\n").unwrap(); fs::write(smith.join("src/lib.rs"), "pub fn value() {}\n").unwrap(); fs::write(consumer.join("src/main.rs"), "fn main() {}\n").unwrap(); let manifest = |floor: &str| { format!( "[workspace]\n\n[package]\nname='consumer-fixture'\nversion='0.1.0'\nedition='2024'\n[dependencies]\nsmith-fixture={{path='../smith'}}\nfloor-pin={{package='floor-dep', path='../dep', version='={floor}'}}\n" ) }; let manifest_path = consumer.join("Cargo.toml"); fs::write(&manifest_path, manifest("1.0.0")).unwrap(); assert!(cargo_output_at(&consumer, &["generate-lockfile"], INPUT_LIMIT).is_err()); fs::write(&manifest_path, manifest("1.1.0")).unwrap(); cargo_output_at(&consumer, &["generate-lockfile"], INPUT_LIMIT).unwrap(); cargo_output_at(&consumer, &["check", "--locked"], INPUT_LIMIT).unwrap(); } #[test] fn direct_minimum_merges_shared_registry_dependency_alias_and_features() { let scratch = TempRoot::new().unwrap(); let directory = scratch.new_directory().unwrap(); let metadata = br#"{"packages":[{"name":"smith-core","source":null,"manifest_path":"/unused/core/Cargo.toml","dependencies":[{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.31","kind":null,"optional":false,"target":null,"uses_default_features":false,"features":["std"]}]},{"name":"smith-ai","source":null,"manifest_path":"/unused/ai/Cargo.toml","dependencies":[{"name":"futures","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3.31","kind":null,"optional":false,"target":null,"uses_default_features":true,"features":["async-await"]}]}]}"#; write_consumer(&directory, metadata, &["smith-core", "smith-ai"], true).unwrap(); let manifest = fs::read_to_string(directory.join("Cargo.toml")).unwrap(); assert_eq!(manifest.matches(r#"package = "futures""#).count(), 1); assert!(manifest.contains(r#"features = ["async-await", "std"]"#)); assert!(!manifest.contains("default-features = false")); } #[test] fn direct_minimum_keeps_original_requirement_for_yanked_floor() { let scratch = TempRoot::new().unwrap(); let directory = scratch.new_directory().unwrap(); let metadata = br#"{"packages":[{"name":"smith-core","source":null,"manifest_path":"/unused/Cargo.toml","dependencies":[{"name":"crc32fast","source":"registry+https://github.com/rust-lang/crates.io-index","req":"^0.3","kind":null,"optional":false,"target":"cfg(unix)","uses_default_features":false,"features":["std"]}]}]}"#; write_consumer(&directory, metadata, &["smith-core"], true).unwrap(); let manifest = fs::read_to_string(directory.join("Cargo.toml")).unwrap(); assert!(manifest.contains("version = \"^0.3\"")); assert!(!manifest.contains("version = \"=0.3.0\"")); assert!(manifest.contains("[target.\"cfg(unix)\".dependencies]")); assert!(manifest.contains("features = [\"std\"], default-features = false")); } #[test] fn requirement_manifest_escapes_package_names_and_requirements() { let manifest = probe_manifest("quote\"name", "\" or true #").unwrap(); assert!(manifest.contains("package = \"quote\\\"name\"")); assert!(manifest.contains("version = \"\\\" or true #\"")); } #[test] fn report_excludes_noncanonical_registry_sources() { for source in [ "registry+https://mirror.example/crates.io/index", "registry+https://github.com/rust-lang/crates.io-index-extra", ] { let metadata = fixture_metadata().replace( "registry+https://github.com/rust-lang/crates.io-index", source, ); let mut probed = false; let result = report_from_metadata(metadata.as_bytes(), |_| { probed = true; Ok("1.0.0".into()) }); assert!(!probed, "unexpected probe for {source}"); assert!(result.is_err(), "unexpectedly reported {source}"); } } #[test] fn direct_minimum_rejects_noncanonical_registry_sources() { for source in [ "registry+https://mirror.example/crates.io/index", "registry+https://github.com/rust-lang/crates.io-index-extra", ] { let scratch = TempRoot::new().unwrap(); let directory = scratch.new_directory().unwrap(); let metadata = format!( r#"{{"packages":[{{"name":"smith-core","source":null,"manifest_path":"/unused/Cargo.toml","dependencies":[{{"name":"futures","source":"{source}","req":"^0.3","kind":null,"optional":false,"target":null,"uses_default_features":true,"features":[]}}]}}]}}"# ); let result = write_consumer(&directory, metadata.as_bytes(), &["smith-core"], true); assert!(result.is_err(), "accepted {source}"); } } #[test] fn resolver_selects_exact_package_for_alias_edge() { let metadata = r#"{"workspace_members":["member"],"packages":[{"id":"member","name":"member","dependencies":[{"name":"real","rename":"alias","req":"^1","kind":null,"target":null,"source":"registry+https://github.com/rust-lang/crates.io-index"}]},{"id":"old","name":"real","version":"1.0.0","source":"registry+https://github.com/rust-lang/crates.io-index"},{"id":"new","name":"real","version":"2.0.0","source":"registry+https://github.com/rust-lang/crates.io-index"}],"resolve":{"nodes":[{"id":"member","deps":[{"name":"alias","pkg":"new"}]}]}}"#; let result = report_from_metadata(metadata.as_bytes(), |_| Ok("2.4.0".into())); assert!(result.unwrap().contains("| 2.0.0 |")); } #[test] fn malformed_resolve_graph_is_reported_instead_of_skipped() { let metadata = r#"{"workspace_members":["member"],"packages":[{"id":"member","name":"member","dependencies":[]}],"resolve":{"nodes":[]}}"#; assert!(report_from_metadata(metadata.as_bytes(), |_| Ok("1.0.0".into())).is_err()); } #[test] fn report_columns_match_the_header() { let report = report_from_metadata(fixture_metadata().as_bytes(), |_| Ok("1.0.0".into())).unwrap(); let mut lines = report.lines().skip(1); let columns = lines.next().unwrap().split('|').count(); assert!(lines.all(|line| line.split('|').count() == columns)); } #[test] fn missing_registry_evidence_fails_the_report() { let result = report_from_metadata(fixture_metadata().as_bytes(), |_| { Err("registry unavailable".into()) }); assert_eq!(result.unwrap_err(), "registry unavailable"); } #[test] fn root_rationales_capture_multiline_and_trailing_comments() { let manifest = Path::new(env!("CARGO_MANIFEST_DIR")).join("../Cargo.toml"); let rationale = root_rationales(&manifest).unwrap(); assert_eq!( rationale.get("ureq").map(String::as_str), Some( "smith-harness, http stack; tilde pin: the chunk-gap timeout uses ureq::unversioned" ), "{rationale:?}" ); assert_eq!( rationale.get("wasmtime").map(String::as_str), Some( "smith-harness: Wasmtime hosts components; guest wit-bindgen must align with its component tooling release, not independently chase latest. Wasmi or Wasmer remain alternatives only after component compatibility and Smith workload benchmarks justify replacing Wasmtime. smith-harness, wasm plugin engine" ) ); assert_eq!( rationale.get("wit-bindgen").map(String::as_str), Some("plugins/echo: guest component bindings aligned with Wasmtime's release set.") ); } #[test] fn constraint_rationale_is_included_in_report() { let metadata = fixture_metadata(); let rationale = BTreeMap::from([("serde".to_owned(), "required serde API".to_owned())]); let report = report_from_metadata_with_rationale(metadata.as_bytes(), &rationale, |_, _| { Ok("1.0.0".into()) }) .unwrap(); assert!(report.contains("required serde API")); assert!(!report.contains("not recorded")); } #[test] fn scratch_root_is_unique_and_removed_on_drop() { let first = TempRoot::new().unwrap(); let first_path = first.0.clone(); let second = TempRoot::new().unwrap(); assert_ne!(first.0, second.0); let one = first.new_directory().unwrap(); let two = first.new_directory().unwrap(); assert_ne!(one, two); drop(first); assert!(!first_path.exists()); } fn fixture_metadata() -> String { r#"{"workspace_members":["member"],"packages":[{"id":"member","name":"member","dependencies":[{"name":"serde","req":"^1","kind":null,"target":null,"source":"registry+https://github.com/rust-lang/crates.io-index"}]},{"id":"serde-id","name":"serde","version":"1.0.0","source":"registry+https://github.com/rust-lang/crates.io-index"}],"resolve":{"nodes":[{"id":"member","deps":[{"name":"serde","pkg":"serde-id"}]}]}}"#.into() } #[test] fn registry_probe_ignores_enclosing_workspace() { let scratch = TempRoot::new().unwrap(); fs::write(scratch.0.join("Cargo.toml"), "[workspace]\nresolver='3'\n").unwrap(); let version = probe(&scratch, "serde", "1", true).unwrap(); assert!(version.starts_with("1.")); } #[test] fn consumer_ignores_enclosing_workspace() { let scratch = TempRoot::new().unwrap(); fs::write(scratch.0.join("Cargo.toml"), "[workspace]\nresolver='3'\n").unwrap(); let directory = scratch.new_directory().unwrap(); write_consumer(&directory, br#"{"packages":[]}"#, &[], false).unwrap(); let output = cargo_output_at( &directory, &["metadata", "--offline", "--format-version=1"], INPUT_LIMIT, ) .unwrap(); let metadata: serde_json::Value = serde_json::from_slice(&output).unwrap(); assert_eq!(metadata["workspace_root"], directory.to_str().unwrap()); } #[test] fn cargo_owned_metadata_resolves_real_registry_requirement_offline() { let scratch = TempRoot::new().unwrap(); let directory = scratch.new_directory().unwrap(); fs::create_dir(directory.join("src")).unwrap(); fs::write(directory.join("Cargo.toml"), "[workspace]\n\n[package]\nname='probe-test'\nversion='0.0.0'\nedition='2024'\n[dependencies]\nserde={version='1', default-features=false}\n").unwrap(); fs::write(directory.join("src/main.rs"), "fn main() {}\n").unwrap(); let output = cargo_output_at( &directory, &["metadata", "--offline", "--format-version=1"], INPUT_LIMIT, ) .unwrap(); let value: serde_json::Value = serde_json::from_slice(&output).unwrap(); assert!( value["packages"] .as_array() .unwrap() .iter() .any(|package| package["name"] == "serde") ); } }