use std::path::Path;
use std::sync::Arc;
use gunnar_store::conformance::{self, Harness, HASH_KINDS};
use gunnar_store::git_storage_trait::{Caps, GitOps, GitServe};
use gunnar_store::RepoStore;
fn run<H: Harness>(harness: &H) -> usize {
let outcomes = conformance::run_all(harness);
let expected = conformance::check_names::<H>().len() * HASH_KINDS.len();
assert_eq!(
outcomes.len(),
expected,
"[{}] the runner produced {} outcomes for {expected} expected check/hash-kind pairs",
harness.name(),
outcomes.len()
);
for hash_kind in HASH_KINDS {
let ran = outcomes.iter().filter(|o| o.hash_kind == hash_kind).count();
assert_eq!(
ran,
expected / HASH_KINDS.len(),
"[{}] only {ran} checks ran against {hash_kind}",
harness.name()
);
}
conformance::assert_all_pass(&outcomes)
}
const EXPECTED: usize = 62;
#[test]
fn the_in_memory_control_conforms() {
assert_eq!(run(&conformance::MemHarness::default()), EXPECTED);
}
#[test]
fn the_fixture_pack_is_a_real_packfile() {
for hash_kind in HASH_KINDS {
let repo = conformance::repo(hash_kind).expect("build the fixture");
let dir = tempfile::tempdir().expect("temporary directory");
let mut bytes = std::io::Cursor::new(repo.fixture.bytes.clone());
let indexed = gunnar_pack::index_pack(
&mut bytes,
Some(dir.path()),
&gunnar_pack::IndexOptions::new(hash_kind).thread_limit(1),
None,
)
.unwrap_or_else(|err| {
panic!("gitoxide refused a pack the conformance fixture calls valid: {err:#}")
});
assert_eq!(
indexed.num_objects as usize,
repo.fixture.entries.len(),
"[{hash_kind}] the oracle found {} objects and the fixture claims {}",
indexed.num_objects,
repo.fixture.entries.len()
);
}
}
#[test]
fn the_seam_is_usable_as_a_trait_object() {
struct OneRepo {
git_dir: std::path::PathBuf,
git: Arc<dyn GitServe>,
}
impl RepoStore for OneRepo {
fn git_dir(&self) -> &Path {
&self.git_dir
}
fn hash_kind(&self) -> gunnar_store::gix_hash::Kind {
gunnar_store::gix_hash::Kind::Sha256
}
fn git(&self) -> Arc<dyn GitServe> {
Arc::clone(&self.git)
}
}
let hash_kind = gunnar_store::gix_hash::Kind::Sha256;
let repo = conformance::repo(hash_kind).expect("build the fixture");
let store: Arc<dyn RepoStore> = Arc::new(OneRepo {
git_dir: std::path::PathBuf::from("/does/not/have/to/exist"),
git: Arc::new(gunnar_store::mem::MemGitServe::new(hash_kind)),
});
let git = store.git();
drop(store);
git.put_pack(&repo.fixture.bytes).expect("put_pack");
assert!(git.has(repo.commit1.as_slice()).expect("has"));
git.set_head("refs/heads/main").expect("set_head");
assert!(
git.refs().expect("refs").iter().all(|r| r.name != "HEAD"),
"HEAD is a pseudo-ref and must not be advertised as a row"
);
assert_eq!(
git.head()
.expect("head")
.and_then(|r| r.symref_target)
.as_deref(),
Some("refs/heads/main")
);
}
#[test]
fn an_emitted_pack_is_a_real_packfile() {
for hash_kind in HASH_KINDS {
let repo = conformance::repo(hash_kind).expect("build the fixture");
let git = gunnar_store::mem::MemGitServe::new(hash_kind);
git.put_pack(&repo.fixture.bytes).expect("put_pack");
let mut out = Vec::new();
let stats = git
.emit_pack(
&[
repo.delta_blob.as_slice(),
repo.blob.as_slice(),
repo.commit2.as_slice(),
],
&[],
&Caps::modern(),
&mut out,
)
.expect("emit_pack");
let dir = tempfile::tempdir().expect("temporary directory");
let mut bytes = std::io::Cursor::new(out.clone());
let indexed = gunnar_pack::index_pack(
&mut bytes,
Some(dir.path()),
&gunnar_pack::IndexOptions::new(hash_kind).thread_limit(1),
None,
)
.unwrap_or_else(|err| {
panic!("gitoxide refused a pack the in-memory emitter produced: {err:#}")
});
assert_eq!(
indexed.num_objects as u64, stats.objects,
"[{hash_kind}] the oracle found {} objects and PackStats claims {}",
indexed.num_objects, stats.objects
);
let stored = git
.get(repo.delta_blob.as_slice())
.expect("get")
.expect("the store holds the fixture's delta object");
assert_eq!(
stored.obj_type,
gunnar_store::git_storage_trait::ObjType::OfsDelta,
"[{hash_kind}] premise: the fixture's delta object is not stored as an OFS_DELTA, so \
this pack carries no delta and asserts nothing about rewriting a base distance"
);
assert_eq!(
stats.recompressed, 0,
"[{hash_kind}] the in-memory emitter re-deflated {} entries; every base this request \
names is in the request, so every entry must be a copy",
stats.recompressed
);
assert_eq!(
stats.copied, stats.objects,
"[{hash_kind}] {} of {} entries were copied",
stats.copied, stats.objects
);
}
}