284 lines · 8.9 KB · Rust
RawDownload
use std::{path::PathBuf, sync::atomic::AtomicBool}; use gix_hash::ObjectId;use gix_object::{Find, FindHeader, Write, WriteTo}; mod commit;mod encode;mod object_ref;mod tag;mod tree; #[test]fn compute_hash() {    for hk in gix_hash::Kind::all() {        assert_eq!(            gix_object::compute_hash(*hk, gix_object::Kind::Blob, &[]).expect("empty hash doesn’t collide"),            gix_hash::ObjectId::empty_blob(*hk)        );        assert_eq!(            gix_object::compute_hash(*hk, gix_object::Kind::Tree, &[]).expect("empty hash doesn’t collide"),            gix_hash::ObjectId::empty_tree(*hk)        );    }} #[test]fn compute_stream_hash() {    for hk in gix_hash::Kind::all() {        assert_eq!(            gix_object::compute_stream_hash(                *hk,                gix_object::Kind::Blob,                &mut &[][..],                0,                &mut gix_features::progress::Discard,                &AtomicBool::default()            )            .expect("in-memory works"),            gix_hash::ObjectId::empty_blob(*hk)        );        assert_eq!(            gix_object::compute_stream_hash(                *hk,                gix_object::Kind::Tree,                &mut &[][..],                0,                &mut gix_features::progress::Discard,                &AtomicBool::default()            )            .expect("in-memory works"),            gix_hash::ObjectId::empty_tree(*hk)        );    }} #[test]fn never_writes_to_nowhere_and_finds_nothing() -> Result {    let db = gix_object::find::Never;    let data = b"content";    let expected = gix_object::compute_hash(gix_hash::Kind::default(), gix_object::Kind::Blob, data)?;     assert_eq!(        db.write_buf(gix_object::Kind::Blob, data)?,        expected,        "discarded buffer writes still return the computed object id"    );    assert_eq!(        db.write_stream(gix_object::Kind::Blob, data.len() as u64, &mut data.as_slice())?,        expected,        "discarded stream writes still return the computed object id"    );    assert_eq!(        db.write_buf_with_known_id(gix_object::Kind::Blob, data, expected)?,        expected,        "known-id buffer writes preserve the caller-provided id"    );    assert_eq!(        db.write_stream_with_known_id(            gix_object::Kind::Blob,            data.len() as u64,            &mut data.as_slice(),            expected        )?,        expected,        "known-id stream writes preserve the caller-provided id"    );     let mut buf = Vec::new();    assert!(        db.try_find(&expected, &mut buf)?.is_none(),        "discarded writes must not become readable"    );    assert!(        db.try_header(&expected)?.is_none(),        "discarded writes must not make headers readable"    );     Ok(())} use gix_testtools::Result; #[cfg(not(windows))]fn fixup(v: Vec<u8>) -> Vec<u8> {    v} #[cfg(windows)]fn fixup(v: Vec<u8>) -> Vec<u8> {    // Git checks out text files with line ending conversions, git itself will of course not put '\r\n' anywhere,    // so that wouldn't be expected in an object and doesn't have to be parsed.    use bstr::ByteSlice;    v.replace(b"\r\n", "\n")} pub fn fixture(path: &str) -> PathBuf {    PathBuf::from("tests/fixtures").join(path)} pub fn fixture_hash_kind() -> gix_hash::Kind {    gix_testtools::object_hash()} fn fixture_bytes(path: &str) -> Vec<u8> {    fixup(std::fs::read(fixture(path)).unwrap())} pub(crate) fn fuzz_artifact_paths(target: &str) -> Vec<PathBuf> {    let mut paths = std::fs::read_dir(        PathBuf::from(env!("CARGO_MANIFEST_DIR"))            .join("fuzz/artifacts")            .join(target),    )    .expect("artifact directory exists")    .filter_map(|entry| entry.ok().map(|entry| entry.path()))    .collect::<Vec<_>>();    paths.sort();    paths} fn fixture_name(kind: &str, path: &str) -> Vec<u8> {    fixup(fixture_bytes(PathBuf::from(kind).join(path).to_str().unwrap()))} /// Return the object id expected in fixture assertions for the active fixture hash kind.////// Object fixtures in this test module are authored as SHA-1 data and are rewritten on demand for/// SHA-256 runs. This helper mirrors that rewrite on the expectation side so parsing tests can use/// one set of source ids for both hash kinds.pub fn fixture_oid(hex: &str) -> ObjectId {    let oid = hex_to_id(hex);    translate_fixture_oid(oid, fixture_hash_kind())} fn translate_fixture_oid(oid: ObjectId, kind: gix_hash::Kind) -> ObjectId {    match kind {        gix_hash::Kind::Sha1 => oid,        kind => {            let mut hasher = gix_hash::hasher(kind);            hasher.update(oid.as_bytes());            hasher.try_finalize().expect("sha256 hashing is available for tests")        }    }} /// Load an object fixture and, if needed, rewrite its SHA-1 object ids for the active fixture hash kind.pub fn object_fixture(path: &str) -> Result<Vec<u8>> {    if let Some(path) = path.strip_prefix("tree/") {        tree_fixture(path)    } else if let Some(path) = path.strip_prefix("commit/") {        commit_fixture(path)    } else if let Some(path) = path.strip_prefix("tag/") {        tag_fixture(path)    } else {        Ok(fixture_bytes(path))    }} /// Load a tree fixture and, if needed, rewrite its embedded entry ids for the active fixture hash kind.////// The on-disk `tree/*.tree` fixtures contain SHA-1-sized ids. For SHA-256 test runs we parse the/// SHA-1 fixture, rewrite each entry id into the synthetic SHA-256 ids produced by [`fixture_oid()`],/// and re-encode the tree so parsers see correctly-sized object ids.pub fn tree_fixture(path: &str) -> Result<Vec<u8>> {    let fixture = fixture_name("tree", path);    match fixture_hash_kind() {        gix_hash::Kind::Sha1 => Ok(fixture),        kind => {            let mut tree: gix_object::Tree = gix_object::TreeRef::from_bytes(&fixture, gix_hash::Kind::Sha1)?.into();            for entry in &mut tree.entries {                entry.oid = translate_fixture_oid(entry.oid, kind);            }            let mut out = Vec::with_capacity(                fixture.len() + tree.entries.len() * (kind.len_in_bytes() - gix_hash::Kind::Sha1.len_in_bytes()),            );            tree.write_to(&mut out)?;            Ok(out)        }    }} fn commit_fixture(path: &str) -> Result<Vec<u8>> {    let fixture = fixture_name("commit", path);    match fixture_hash_kind() {        gix_hash::Kind::Sha1 => Ok(fixture),        kind => {            let mut commit = gix_object::CommitRef::from_bytes(&fixture, gix_hash::Kind::Sha1)?.into_owned()?;            commit.tree = translate_fixture_oid(commit.tree, kind);            for parent in &mut commit.parents {                *parent = translate_fixture_oid(*parent, kind);            }             let mut out = Vec::with_capacity(                fixture.len()                    + (1 + commit.parents.len()) * (kind.len_in_bytes() - gix_hash::Kind::Sha1.len_in_bytes()),            );            commit.write_to(&mut out)?;            Ok(out)        }    }} fn tag_fixture(path: &str) -> Result<Vec<u8>> {    let fixture = fixture_name("tag", path);    match fixture_hash_kind() {        gix_hash::Kind::Sha1 => Ok(fixture),        kind => {            let mut tag = gix_object::TagRef::from_bytes(&fixture, gix_hash::Kind::Sha1)?.into_owned()?;            tag.target = translate_fixture_oid(tag.target, kind);             let mut out = Vec::with_capacity(fixture.len() + kind.len_in_bytes() - gix_hash::Kind::Sha1.len_in_bytes());            tag.write_to(&mut out)?;            Ok(out)        }    }} pub fn generated_tree_root_id() -> Result<ObjectId> {    let root = gix_testtools::scripted_fixture_read_only("make_trees.sh")?;    let tree = std::fs::read(root.join("tree.baseline"))?;    Ok(gix_object::compute_hash(        fixture_hash_kind(),        gix_object::Kind::Tree,        &tree,    )?)} pub fn normalize_tree_snapshot(input: &str) -> String {    gix_testtools::normalize_hashes(input).0} #[test]fn size_in_memory() {    let actual = std::mem::size_of::<gix_object::Object>();    let sha1 = 272;    let sha256_extra = 16;    let expected = sha1 + sha256_extra;    assert!(        actual <= expected,        "{actual} <= {expected}: Prevent unexpected growth of what should be lightweight objects"    );} fn hex_to_id(hex: &str) -> ObjectId {    ObjectId::from_hex(hex.as_bytes()).expect("40 bytes hex")} fn signature(time: &str) -> gix_actor::SignatureRef<'_> {    use gix_object::bstr::ByteSlice;    gix_actor::SignatureRef {        name: b"Sebastian Thiel".as_bstr(),        email: b"sebastian.thiel@icloud.com".as_bstr(),        time,    }} fn linus_signature(time: &str) -> gix_actor::SignatureRef<'_> {    use gix_object::bstr::ByteSlice;    gix_actor::SignatureRef {        name: b"Linus Torvalds".as_bstr(),        email: b"torvalds@linux-foundation.org".as_bstr(),        time,    }}