#![allow(clippy::too_many_lines)]
use std::collections::{BTreeSet, HashMap};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use grex_core::pack::{parse, PackManifest};
use grex_core::tree::{sync_meta, SyncMetaOptions};
use grex_core::{ClonedRepo, GitBackend, GitError, PackLoader, TreeError};
use tempfile::TempDir;
struct InMemLoader {
manifests: HashMap<PathBuf, PackManifest>,
}
impl InMemLoader {
fn new() -> Self {
Self { manifests: HashMap::new() }
}
fn with(mut self, dir: impl Into<PathBuf>, m: PackManifest) -> Self {
self.manifests.insert(dir.into(), m);
self
}
}
impl PackLoader for InMemLoader {
fn load(&self, path: &Path) -> Result<PackManifest, TreeError> {
self.manifests
.get(path)
.cloned()
.ok_or_else(|| TreeError::ManifestNotFound(path.to_path_buf()))
}
}
#[derive(Debug, Clone)]
enum BackendCall {
Clone {
url: String,
dest: PathBuf,
},
#[allow(dead_code)]
Fetch {
dest: PathBuf,
},
#[allow(dead_code)]
Checkout {
dest: PathBuf,
r#ref: String,
},
}
struct InMemGit {
calls: Mutex<Vec<BackendCall>>,
}
impl InMemGit {
fn new() -> Self {
Self { calls: Mutex::new(Vec::new()) }
}
fn calls(&self) -> Vec<BackendCall> {
self.calls.lock().unwrap().clone()
}
}
impl GitBackend for InMemGit {
fn name(&self) -> &'static str {
"v1_2_1-stress-mock-git"
}
fn clone(&self, url: &str, dest: &Path, _ref: Option<&str>) -> Result<ClonedRepo, GitError> {
std::fs::create_dir_all(dest.join(".git")).unwrap();
self.calls
.lock()
.unwrap()
.push(BackendCall::Clone { url: url.to_string(), dest: dest.to_path_buf() });
Ok(ClonedRepo { path: dest.to_path_buf(), head_sha: "0".repeat(40) })
}
fn fetch(&self, dest: &Path) -> Result<(), GitError> {
self.calls.lock().unwrap().push(BackendCall::Fetch { dest: dest.to_path_buf() });
Ok(())
}
fn checkout(&self, dest: &Path, r#ref: &str) -> Result<(), GitError> {
self.calls
.lock()
.unwrap()
.push(BackendCall::Checkout { dest: dest.to_path_buf(), r#ref: r#ref.to_string() });
Ok(())
}
fn head_sha(&self, _dest: &Path) -> Result<String, GitError> {
Ok("0".repeat(40))
}
}
fn meta_with_children(name: &str, kids: &[(String, String)]) -> PackManifest {
let mut yaml = format!("schema_version: \"1\"\nname: {name}\ntype: meta\nchildren:\n");
for (url, path) in kids {
yaml.push_str(&format!(" - url: {url}\n path: {path}\n"));
}
parse(&yaml).expect("fixture yaml must parse")
}
fn build_fan_out_loader(meta_dir: &Path) -> InMemLoader {
let kids: Vec<(String, String)> = (0..16)
.map(|i| (format!("https://example.com/leaf-{i:02}.git"), format!("leaf-{i:02}")))
.collect();
InMemLoader::new().with(meta_dir.to_path_buf(), meta_with_children("fan-out-root", &kids))
}
#[test]
fn rayon_fan_out_50_runs_byte_identical_state() {
const RUNS: usize = 50;
const KIDS: usize = 16;
let mut all_clone_sets: Vec<BTreeSet<(String, PathBuf)>> = Vec::with_capacity(RUNS);
let mut all_class_orders: Vec<Vec<PathBuf>> = Vec::with_capacity(RUNS);
for _ in 0..RUNS {
let tmp = TempDir::new().unwrap();
let meta_dir = tmp.path().to_path_buf();
let loader = build_fan_out_loader(&meta_dir);
let backend = InMemGit::new();
let mut opts = SyncMetaOptions::default();
opts.parallel = Some(8);
opts.recurse = false; let report = sync_meta(&meta_dir, &backend, &loader, &opts, &[]).expect("ok");
assert_eq!(report.phase1_classifications.len(), KIDS, "every child classified once");
assert!(report.errors.is_empty(), "no errors on a clean fan-out");
let calls = backend.calls();
let clone_set: BTreeSet<(String, PathBuf)> = calls
.iter()
.filter_map(|c| match c {
BackendCall::Clone { url, dest } => Some((url.clone(), dest.clone())),
_ => None,
})
.collect();
assert_eq!(clone_set.len(), KIDS, "rayon must clone each child exactly once");
all_clone_sets.push(clone_set);
let dest_order: Vec<PathBuf> = report
.phase1_classifications
.iter()
.map(|(_meta, dest, _class)| dest.clone())
.collect();
all_class_orders.push(dest_order);
}
let normalise = |s: &BTreeSet<(String, PathBuf)>| -> BTreeSet<(String, String)> {
s.iter()
.map(|(url, dest)| {
(url.clone(), dest.file_name().unwrap().to_string_lossy().into_owned())
})
.collect()
};
let first = normalise(&all_clone_sets[0]);
for (i, set) in all_clone_sets.iter().enumerate().skip(1) {
assert_eq!(
normalise(set),
first,
"run {i} clone set must equal run 0 clone set (rayon non-determinism caught)"
);
}
let basenames = |v: &Vec<PathBuf>| -> Vec<String> {
v.iter().map(|p| p.file_name().unwrap().to_string_lossy().into_owned()).collect()
};
let first_order = basenames(&all_class_orders[0]);
let expected: Vec<String> = (0..KIDS).map(|i| format!("leaf-{i:02}")).collect();
assert_eq!(first_order, expected, "rayon par_iter().collect() must preserve source order");
for (i, order) in all_class_orders.iter().enumerate().skip(1) {
assert_eq!(
basenames(order),
first_order,
"run {i} classification order must equal run 0 order"
);
}
}
#[test]
fn rayon_nested_3_level_correctness_matches_sequential() {
const SIBLINGS: usize = 4;
fn build_nested_loader(root: &Path) -> InMemLoader {
let mut loader = InMemLoader::new();
let mid_paths: Vec<String> = (0..SIBLINGS).map(|i| format!("lvl1-{i}")).collect();
let root_kids: Vec<(String, String)> =
mid_paths.iter().map(|p| (format!("https://example.com/{p}.git"), p.clone())).collect();
loader = loader.with(root.to_path_buf(), meta_with_children("root", &root_kids));
for mp in &mid_paths {
let mid_dir = root.join(mp);
let leaf_kids: Vec<(String, String)> = (0..SIBLINGS)
.map(|j| {
let lp = format!("lvl2-{j}");
(format!("https://example.com/{mp}-{lp}.git"), lp)
})
.collect();
loader = loader.with(mid_dir.clone(), meta_with_children(mp, &leaf_kids));
for j in 0..SIBLINGS {
let lp = format!("lvl2-{j}");
let leaf_dir = mid_dir.join(&lp);
loader = loader.with(leaf_dir, meta_with_children(&lp, &[]));
}
}
loader
}
fn pre_materialise(root: &Path) {
for i in 0..SIBLINGS {
let mid = root.join(format!("lvl1-{i}"));
std::fs::create_dir_all(mid.join(".grex")).unwrap();
std::fs::create_dir_all(mid.join(".git")).unwrap();
std::fs::write(
mid.join(".grex/pack.yaml"),
format!("schema_version: \"1\"\nname: lvl1-{i}\ntype: meta\n"),
)
.unwrap();
for j in 0..SIBLINGS {
let leaf = mid.join(format!("lvl2-{j}"));
std::fs::create_dir_all(leaf.join(".grex")).unwrap();
std::fs::create_dir_all(leaf.join(".git")).unwrap();
std::fs::write(
leaf.join(".grex/pack.yaml"),
format!("schema_version: \"1\"\nname: lvl2-{j}\ntype: meta\n"),
)
.unwrap();
}
}
}
fn run_once(parallel: Option<usize>) -> (usize, BTreeSet<String>) {
let tmp = TempDir::new().unwrap();
let root = tmp.path().to_path_buf();
pre_materialise(&root);
let loader = build_nested_loader(&root);
let backend = InMemGit::new();
let mut opts = SyncMetaOptions::default();
opts.parallel = parallel;
let report = sync_meta(&root, &backend, &loader, &opts, &[]).expect("ok");
let touched: BTreeSet<String> = backend
.calls()
.iter()
.filter_map(|c| match c {
BackendCall::Fetch { dest } => {
Some(dest.strip_prefix(&root).ok()?.to_string_lossy().into_owned())
}
_ => None,
})
.collect();
(report.metas_visited, touched)
}
let (par4_visited, par4_touched) = run_once(Some(4));
let (seq_visited, seq_touched) = run_once(Some(1));
assert_eq!(par4_visited, 21, "parallel=4 must visit every meta");
assert_eq!(seq_visited, 21, "sequential must visit every meta");
assert_eq!(
par4_touched, seq_touched,
"fetch set under parallel=4 must equal fetch set under sequential"
);
assert_eq!(par4_touched.len(), 20, "expected 4 mid + 16 leaf fetches");
}
#[test]
fn rayon_parallel_one_is_sequential_equivalent() {
let tmp = TempDir::new().unwrap();
let meta_dir = tmp.path().to_path_buf();
let loader = build_fan_out_loader(&meta_dir); let backend = InMemGit::new();
let mut opts = SyncMetaOptions::default();
opts.parallel = Some(1);
opts.recurse = false;
let _report = sync_meta(&meta_dir, &backend, &loader, &opts, &[]).expect("ok");
let urls: Vec<String> = backend
.calls()
.iter()
.filter_map(|c| match c {
BackendCall::Clone { url, .. } => Some(url.clone()),
_ => None,
})
.collect();
let expected: Vec<String> =
(0..16).map(|i| format!("https://example.com/leaf-{i:02}.git")).collect();
assert_eq!(urls, expected, "parallel=1 must clone in source order (rayon 1-thread pool)");
}