crates/gunnar-delta/tests/donation_shape.rs
//! This crate must stay liftable out of gunnar's tree with `cp -r`.
//!
//! That is decision D21: anything that plainly belongs in gitoxide is built as a
//! standalone crate from the first line, because extraction after the fact never
//! happens; by then the code has grown the host project's types through it. The
//! manifest already says so in a comment. A comment is not a check, and the
//! coupling this forbids arrives one convenient `version.workspace = true` at a
//! time, in a commit whose diff looks like tidying.
//!
//! # Every check here is proved red before it is trusted
//!
//! The checks are pure functions over text, and each one is run against a
//! deliberately-bad input in this same file. A guard that has only ever seen the
//! passing case is a guard nobody has tested, and the failure mode is silence,
//! forever. So `a_gunnar_dependency_is_detected`, `workspace_inheritance_is_detected`
//! and `a_rayon_dependency_is_detected` exist to watch the watchers.
use std::path::{Path, PathBuf};
fn crate_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
fn manifest() -> String {
let p = crate_root().join("Cargo.toml");
std::fs::read_to_string(&p).unwrap_or_else(|e| panic!("read {p:?}: {e}"))
}
/// Dependency table entries: `(section, crate name, the whole line)`.
///
/// Deliberately literal: it walks section headers and takes the identifier
/// before the first `=`. A TOML parser is a dependency this crate does not have
/// and does not want for one test.
fn dependency_lines(manifest: &str) -> Vec<(String, String, String)> {
let mut section = String::new();
let mut out = Vec::new();
for raw in manifest.lines() {
let line = raw.trim();
if let Some(name) = line.strip_prefix('[').and_then(|l| l.strip_suffix(']')) {
section = name.to_owned();
continue;
}
if line.is_empty() || line.starts_with('#') || !section.contains("dependencies") {
continue;
}
let Some((lhs, _)) = line.split_once('=') else {
continue;
};
out.push((section.clone(), lhs.trim().to_owned(), line.to_owned()));
}
out
}
/// Names of dependencies that would tie this crate to its host project.
fn host_dependencies(manifest: &str) -> Vec<String> {
dependency_lines(manifest)
.into_iter()
.filter(|(_, name, _)| name.starts_with("gunnar"))
.map(|(section, name, _)| format!("[{section}] {name}"))
.collect()
}
/// Dependency lines that inherit from a workspace, which is what makes a
/// manifest meaningless outside the tree it was written in.
fn workspace_inherited(manifest: &str) -> Vec<String> {
dependency_lines(manifest)
.into_iter()
.filter(|(_, _, line)| line.contains("workspace") && line.contains("true"))
.map(|(section, name, _)| format!("[{section}] {name}"))
.collect()
}
/// LAW 3, restated where it can be enforced: concurrency here is
/// `gix_parallel`, never rayon.
fn rayon_dependencies(manifest: &str) -> Vec<String> {
dependency_lines(manifest)
.into_iter()
.filter(|(_, name, _)| name.contains("rayon"))
.map(|(section, name, _)| format!("[{section}] {name}"))
.collect()
}
fn rust_sources(dir: &Path) -> Vec<PathBuf> {
let mut out = Vec::new();
let entries = std::fs::read_dir(dir).unwrap_or_else(|e| panic!("read {dir:?}: {e}"));
for entry in entries {
let path = entry.expect("dir entry").path();
if path.is_dir() {
out.extend(rust_sources(&path));
} else if path.extension().is_some_and(|x| x == "rs") {
out.push(path);
}
}
out.sort();
out
}
#[test]
fn the_manifest_names_no_dependency_from_the_host_project() {
let found = host_dependencies(&manifest());
assert!(
found.is_empty(),
"gunnar-delta depends on {found:?}. D21: this crate carries no gunnar types and \
no gunnar dependency, ever. If it needs one, the design is wrong"
);
}
#[test]
fn no_dependency_inherits_from_the_workspace() {
let found = workspace_inherited(&manifest());
assert!(
found.is_empty(),
"{found:?} inherit from the workspace, which makes this manifest meaningless \
outside gunnar's tree. That is the exact coupling D21 exists to prevent"
);
}
#[test]
fn concurrency_is_not_rayon() {
let found = rayon_dependencies(&manifest());
assert!(
found.is_empty(),
"rayon appears in the manifest as {found:?}"
);
for path in rust_sources(&crate_root().join("src")) {
let text = std::fs::read_to_string(&path).expect("read source");
for (n, line) in text.lines().enumerate() {
let code = line.split("//").next().unwrap_or("");
assert!(
!code.contains("rayon"),
"{}:{} reaches for rayon: {line}",
path.display(),
n + 1
);
}
}
}
#[test]
fn no_source_file_imports_the_host_project() {
let mut checked = 0usize;
for path in rust_sources(&crate_root().join("src")) {
let text = std::fs::read_to_string(&path).expect("read source");
for (n, line) in text.lines().enumerate() {
let code = line.split("//").next().unwrap_or("");
assert!(
!code.contains("gunnar_") && !code.contains("gunnar::"),
"{}:{} reaches into the host project: {line}",
path.display(),
n + 1
);
}
checked += 1;
}
assert!(checked >= 5, "only {checked} source files were scanned");
}
#[test]
fn the_licence_matches_gitoxides() {
let m = manifest();
assert!(
m.contains(r#"license = "MIT OR Apache-2.0""#),
"the licence must match gitoxide's or the code cannot be offered to them"
);
}
// ── the negative controls: every check above, proved red ─────────────────────
#[test]
fn a_gunnar_dependency_is_detected() {
let bad = "[dependencies]\ngix-hash = \"0.26\"\ngunnar-core = { path = \"../gunnar-core\" }\n";
assert_eq!(host_dependencies(bad), vec!["[dependencies] gunnar-core"]);
assert!(host_dependencies("[dependencies]\ngix-hash = \"0.26\"\n").is_empty());
}
#[test]
fn workspace_inheritance_is_detected() {
let bad = "[package]\nversion.workspace = true\n[dependencies]\nthiserror.workspace = true\n";
// Only dependency sections are policed; `[package]` inheritance is a
// different (and lesser) sin, so the check must not fire on it.
assert_eq!(
workspace_inherited(bad),
vec!["[dependencies] thiserror.workspace"]
);
assert!(workspace_inherited("[dependencies]\nthiserror = \"2\"\n").is_empty());
}
#[test]
fn a_rayon_dependency_is_detected() {
let bad = "[dev-dependencies]\nrayon = \"1\"\n";
assert_eq!(rayon_dependencies(bad), vec!["[dev-dependencies] rayon"]);
assert!(rayon_dependencies("[dependencies]\ngix-features = \"0.49\"\n").is_empty());
}
#[test]
fn a_commented_out_dependency_is_not_mistaken_for_a_real_one() {
// The manifest carries a long comment block about D21 that names gunnar
// repeatedly. If the scanner counted comments it would fire on the very
// paragraph explaining why it exists, and someone would delete it.
let m = "[dependencies]\n# gunnar-core = { path = \"..\" }\ngix-hash = \"0.26\"\n";
assert!(host_dependencies(m).is_empty());
}