241 lines · 8.6 KB · Rust
RawDownload
/// Because the `TryFrom` implementations don't return proper errors/// on failure#[derive(Debug, thiserror::Error)]enum Error {    #[error("")]    TryFromError,} /// Needed for roundtripping object types that take a `object_hash` parameter./// This is the same as `round_trip`, but for types that have `from_bytes()` with `object_hash`.macro_rules! round_trip_with_hash_kind {    ($owned:ty, $borrowed:ty, $( $files:literal ), +) => {        #[test]        fn round_trip() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {            use std::convert::TryFrom;            use std::io::Write;            use crate::object_fixture;            use gix_object::{ObjectRef, Object, WriteTo};            use bstr::ByteSlice;            let hash_kind = crate::fixture_hash_kind();             for input_name in &[                $( $files ),*            ] {                let input = object_fixture(input_name)?;                // Test the parse->borrowed->owned->write chain for an object kind                let mut output = Vec::new();                let item = <$borrowed>::from_bytes(&input, hash_kind)?;                item.write_to(&mut output)?;                assert_eq!(output.as_bstr(), input.as_bstr(), "borrowed: {input_name}");                 let item: $owned = item.try_into()?;                output.clear();                item.write_to(&mut output)?;                assert_eq!(output.as_bstr(), input.as_bstr());                 // Test the parse->borrowed->owned->write chain for the top-level objects                let item = ObjectRef::from(<$borrowed>::from_bytes(&input, hash_kind)?);                output.clear();                item.write_to(&mut output)?;                assert_eq!(output.as_bstr(), input.as_bstr(), "object-ref");                 let item: Object = Object::try_from(item)?;                output.clear();                item.write_to(&mut output)?;                assert_eq!(output.as_bstr(), input.as_bstr(), "owned");                 // Test the loose serialisation -> parse chain for an object kind                let item = <$borrowed>::from_bytes(&input, hash_kind)?;                // serialise a borowed item to a tagged loose object                output.clear();                {                    let w = &mut output;                    w.write_all(&item.loose_header())?;                    item.write_to(w)?;                    let parsed = ObjectRef::from_loose(&output, hash_kind)?;                    let item2 = <$borrowed>::try_from(parsed).or(Err(super::Error::TryFromError))?;                    assert_eq!(item2, item, "object-ref loose: {input_name} {:?}\n{:?}", output.as_bstr(), input.as_bstr());                }                 let item: $owned = item.try_into()?;                // serialise an owned to a tagged loose object                output.clear();                let w = &mut output;                w.write_all(&item.loose_header())?;                item.write_to(w)?;                let parsed = ObjectRef::from_loose(&output, hash_kind)?;                let parsed_borrowed = <$borrowed>::try_from(parsed).or(Err(super::Error::TryFromError))?;                let item2: $owned = parsed_borrowed.try_into().or(Err(super::Error::TryFromError))?;                assert_eq!(item2, item, "object-ref loose owned: {input_name} {:?}\n{:?}", output.as_bstr(), input.as_bstr());            }            Ok(())        }    };} mod tag {    round_trip_with_hash_kind!(        gix_object::Tag,        gix_object::TagRef,        "tag/empty_missing_nl.txt",        "tag/empty.txt",        "tag/no-tagger.txt",        "tag/whitespace.txt",        "tag/with-newlines.txt",        "tag/signed.txt"    );} mod commit {    round_trip_with_hash_kind!(        gix_object::Commit,        gix_object::CommitRef,        "commit/email-with-space.txt",        "commit/signed-whitespace.txt",        "commit/two-multiline-headers.txt",        "commit/mergetag.txt",        "commit/merge.txt",        "commit/signed.txt",        "commit/signed-singleline.txt",        "commit/signed-with-encoding.txt",        "commit/unsigned.txt",        "commit/whitespace.txt",        "commit/with-encoding.txt",        "commit/subtle.txt"    );} mod tree {    use gix_object::{WriteTo, tree, tree::EntryKind};     #[test]    fn write_to_does_not_validate() {        let hash_kind = crate::fixture_hash_kind();        let mut tree = gix_object::Tree::empty();        tree.entries.push(tree::Entry {            mode: EntryKind::Blob.into(),            filename: "".into(),            oid: hash_kind.null(),        });        tree.entries.push(tree::Entry {            mode: EntryKind::Tree.into(),            filename: "something\nwith\newlines\n".into(),            oid: hash_kind.empty_tree(),        });        tree.write_to(&mut std::io::sink())            .expect("write succeeds, no validation is performed");    }     #[test]    fn write_to_does_not_allow_separator() {        let hash_kind = crate::fixture_hash_kind();        let mut tree = gix_object::Tree::empty();        tree.entries.push(tree::Entry {            mode: EntryKind::Blob.into(),            filename: "hi\0ho".into(),            oid: hash_kind.null(),        });        let err = tree.write_to(&mut std::io::sink()).unwrap_err();        assert_eq!(            err.to_string(),            r#"Nullbytes are invalid in file paths as they are separators: "hi\0ho""#        );    }     round_trip_with_hash_kind!(gix_object::Tree, gix_object::TreeRef, "tree/everything.tree");} mod blob {    use std::{convert::TryFrom, io::Write};     use bstr::ByteSlice;    use gix_object::{Blob, BlobRef, Object, ObjectRef, WriteTo};     use crate::fixture_bytes;     #[test]    fn round_trip() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {        let input_name = "tree/everything.tree";        let input = fixture_bytes(input_name);        // It doesn't matter which data we use - it's not interpreted.         let mut output = Vec::new();        let item = BlobRef::from_bytes(&input)?;        item.write_to(&mut output)?;        assert_eq!(output.as_bstr(), input.as_bstr(), "borrowed: {input_name}");         let item: Blob = item.into();        output.clear();        item.write_to(&mut output)?;        assert_eq!(output.as_bstr(), input.as_bstr());         let item = ObjectRef::from(BlobRef::from_bytes(&input)?);        output.clear();        item.write_to(&mut output)?;        assert_eq!(output.as_bstr(), input.as_bstr(), "object-ref");         let item: Object = Object::try_from(item)?;        output.clear();        item.write_to(&mut output)?;        assert_eq!(output.as_bstr(), input.as_bstr(), "owned");         let item = BlobRef::from_bytes(&input)?;        output.clear();        {            let w = &mut output;            w.write_all(&item.loose_header())?;            item.write_to(w)?;            let parsed = ObjectRef::from_loose(&output, gix_testtools::object_hash())?;            let item2 = BlobRef::try_from(parsed).or(Err(super::Error::TryFromError))?;            assert_eq!(                item2,                item,                "object-ref loose: {input_name} {:?}\n{:?}",                output.as_bstr(),                input.as_bstr()            );        }         let item: Blob = item.into();        output.clear();        let w = &mut output;        w.write_all(&item.loose_header())?;        item.write_to(w)?;        let parsed = ObjectRef::from_loose(&output, gix_testtools::object_hash())?;        let parsed_borrowed = BlobRef::try_from(parsed).or(Err(super::Error::TryFromError))?;        let item2: Blob = parsed_borrowed.into();        assert_eq!(            item2,            item,            "object-ref loose owned: {input_name} {:?}\n{:?}",            output.as_bstr(),            input.as_bstr()        );         Ok(())    }} mod loose_header {    use bstr::ByteSlice;    use gix_object::{Kind, decode, encode};     #[test]    fn round_trip() -> Result<(), Box<dyn std::error::Error>> {        for (kind, size, expected) in &[            (Kind::Tree, 1234, "tree 1234\0".as_bytes()),            (Kind::Blob, 0, b"blob 0\0"),            (Kind::Commit, 24241, b"commit 24241\0"),            (Kind::Tag, 9999999999, b"tag 9999999999\0"),        ] {            let buf = encode::loose_header(*kind, *size);            assert_eq!(buf.as_bstr(), expected.as_bstr());            let (actual_kind, actual_size, actual_read) = decode::loose_header(&buf)?;            assert_eq!(actual_kind, *kind);            assert_eq!(actual_size, *size);            assert_eq!(actual_read, buf.len());        }        Ok(())    }}