Add snapshot and git-verify-pack tests for low-level API
Diff
src/high_level.rs | 31 +-----------
src/lib.rs | 37 +++++++++++++-
src/low_level.rs | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/snapshots/packfile__low_level__test__packfile__is_readable_by_git.snap | 10 ++++-
src/snapshots/packfile__low_level__test__packfile__snapshot.snap | 5 ++-
src/snapshots/packfile__low_level__test__packfile_entry__blob__full.snap | 5 ++-
src/snapshots/packfile__low_level__test__packfile_entry__commit__full.snap | 5 ++-
src/snapshots/packfile__low_level__test__packfile_entry__tree__full.snap | 5 ++-
8 files changed, 292 insertions(+), 29 deletions(-)
@@ -183,8 +183,6 @@ enum TreeItem {
mod test {
use crate::{high_level::GitRepository, low_level::PackFile};
use bytes::{Bytes, BytesMut};
use std::process::{Command, Stdio};
use tempfile::TempDir;
#[test]
fn deterministic() {
@@ -198,7 +196,7 @@ mod test {
.unwrap();
assert_eq!(
hex::encode(&hash),
hex::encode(hash),
"6ba08bda5731edfb2a0a00e602d1dd4bbd9d341c"
);
insta::assert_debug_snapshot!(packfile);
@@ -217,36 +215,11 @@ mod test {
.commit("me", "me@example.com", "initial commit")
.unwrap();
let scratch_dir = TempDir::new().unwrap();
let packfile_path = scratch_dir.path().join("example.pack");
let mut output = BytesMut::new();
PackFile::new(&packfile).encode_to(&mut output).unwrap();
std::fs::write(&packfile_path, &output).unwrap();
let res = Command::new("git")
.arg("index-pack")
.arg(&packfile_path)
.stdout(Stdio::piped())
.spawn()
.unwrap()
.wait()
.unwrap();
assert!(res.success());
let command = Command::new("git")
.arg("verify-pack")
.arg("-v")
.stdout(Stdio::piped())
.arg(&packfile_path)
.spawn()
.unwrap();
let out = command.wait_with_output().unwrap();
assert!(out.status.success(), "git exited non-0");
let stdout = crate::test::verify_pack_file(output.freeze());
let stdout = String::from_utf8_lossy(&out.stdout);
insta::with_settings!({filters => vec![
(r#"/(.*)/example.pack"#, "/path/to/example.pack")
]}, {
@@ -30,3 +30,40 @@ mod util;
pub use error::Error;
pub use packet_line::PktLine;
#[cfg(test)]
mod test {
use bytes::Bytes;
use std::process::{Command, Stdio};
use tempfile::TempDir;
pub fn verify_pack_file(packed: Bytes) -> String {
let scratch_dir = TempDir::new().unwrap();
let packfile_path = scratch_dir.path().join("example.pack");
std::fs::write(&packfile_path, packed).unwrap();
let res = Command::new("git")
.arg("index-pack")
.arg(&packfile_path)
.stdout(Stdio::piped())
.spawn()
.unwrap()
.wait()
.unwrap();
assert!(res.success());
let command = Command::new("git")
.arg("verify-pack")
.arg("-v")
.stdout(Stdio::piped())
.arg(&packfile_path)
.spawn()
.unwrap();
let out = command.wait_with_output().unwrap();
assert!(out.status.success(), "git exited non-0");
String::from_utf8(out.stdout).unwrap()
}
}
@@ -355,6 +355,64 @@ impl PackFileEntry {
#[cfg(test)]
mod test {
mod packfile {
use crate::low_level::{
Commit, CommitUserInfo, PackFile, PackFileEntry, TreeItem, TreeItemKind,
};
use bytes::{Bytes, BytesMut};
fn example() -> Bytes {
let blob = PackFileEntry::Blob(Bytes::from("hello world"));
let tree = PackFileEntry::Tree(vec![TreeItem {
kind: TreeItemKind::File,
name: "helloworld.txt".into(),
hash: blob.hash().unwrap(),
sort_name: "helloworld.txt".to_string(),
}]);
let commit = PackFileEntry::Commit(Commit {
tree: tree.hash().unwrap(),
author: CommitUserInfo {
name: "example",
email: "example@me.com",
time: time::OffsetDateTime::UNIX_EPOCH,
},
committer: CommitUserInfo {
name: "example",
email: "example@me.com",
time: time::OffsetDateTime::UNIX_EPOCH,
},
message: "initial commit",
});
let mut out = BytesMut::new();
PackFile::new(&[blob, tree, commit])
.encode_to(&mut out)
.unwrap();
out.freeze()
}
#[test]
fn snapshot() {
let actual = example();
insta::assert_debug_snapshot!(actual);
}
#[test]
fn is_readable_by_git() {
let stdout = crate::test::verify_pack_file(example());
insta::with_settings!({filters => vec![
(r#"/(.*)/example.pack"#, "/path/to/example.pack")
]}, {
insta::assert_snapshot!(stdout);
});
}
}
mod packfile_entry {
use crate::low_level::PackFileEntry;
use bytes::{Bytes, BytesMut};
@@ -378,5 +436,170 @@ mod test {
assert_eq!(header.to_vec(), &[0xbf, 0x00]);
}
mod commit {
use crate::low_level::{Commit, CommitUserInfo, PackFileEntry};
use bytes::BytesMut;
fn example() -> PackFileEntry {
PackFileEntry::Commit(Commit {
tree: [0; 20],
author: CommitUserInfo {
name: "author",
email: "author@example.com",
time: time::OffsetDateTime::from_unix_timestamp(1_688_494_158).unwrap(),
},
committer: CommitUserInfo {
name: "committer",
email: "committer@example.com",
time: time::OffsetDateTime::from_unix_timestamp(1_687_494_158).unwrap(),
},
message: "hello world!",
})
}
#[test]
fn hash() {
let commit = example();
let actual = hex::encode(commit.hash().unwrap());
let expected = "0cc33510a70f7e9ad5f35738385d7ace25d0bbf4";
assert_eq!(actual, expected);
}
#[test]
fn uncompressed_size() {
let commit = example();
let actual = commit.uncompressed_size();
let expected = 172;
assert_eq!(actual, expected);
}
#[test]
fn headers() {
let commit = example();
let mut actual = BytesMut::new();
commit.write_header(&mut actual);
let expected = &[0x9c, 0x0a];
assert_eq!(actual.to_vec(), expected);
}
#[test]
fn full() {
let commit = example();
let mut actual = BytesMut::new();
commit.encode_to(&mut actual).unwrap();
insta::assert_debug_snapshot!(actual);
}
}
mod tree {
use crate::low_level::{PackFileEntry, TreeItem, TreeItemKind};
use bytes::BytesMut;
fn example() -> PackFileEntry {
PackFileEntry::Tree(vec![TreeItem {
kind: TreeItemKind::File,
name: "hello".into(),
hash: [0u8; 20],
sort_name: "/hello".to_string(),
}])
}
#[test]
fn hash() {
let commit = example();
let actual = hex::encode(commit.hash().unwrap());
let expected = "9fc911650c548e4aa7b6dfd085a9347df8743e17";
assert_eq!(actual, expected);
}
#[test]
fn uncompressed_size() {
let commit = example();
let actual = commit.uncompressed_size();
let expected = 33;
assert_eq!(actual, expected);
}
#[test]
fn headers() {
let commit = example();
let mut actual = BytesMut::new();
commit.write_header(&mut actual);
let expected = &[0xa1, 0x02];
assert_eq!(actual.to_vec(), expected);
}
#[test]
fn full() {
let commit = example();
let mut actual = BytesMut::new();
commit.encode_to(&mut actual).unwrap();
insta::assert_debug_snapshot!(actual);
}
}
mod blob {
use crate::low_level::PackFileEntry;
use bytes::{Bytes, BytesMut};
fn example() -> PackFileEntry {
PackFileEntry::Blob(Bytes::from("hello world"))
}
#[test]
fn hash() {
let commit = example();
let actual = hex::encode(commit.hash().unwrap());
let expected = "95d09f2b10159347eece71399a7e2e907ea3df4f";
assert_eq!(actual, expected);
}
#[test]
fn uncompressed_size() {
let commit = example();
let actual = commit.uncompressed_size();
let expected = 11;
assert_eq!(actual, expected);
}
#[test]
fn headers() {
let commit = example();
let mut actual = BytesMut::new();
commit.write_header(&mut actual);
let expected = &[0xbb, 0x00];
assert_eq!(actual.to_vec(), expected);
}
#[test]
fn full() {
let commit = example();
let mut actual = BytesMut::new();
commit.encode_to(&mut actual).unwrap();
insta::assert_debug_snapshot!(actual);
}
}
}
}
@@ -0,0 +1,10 @@
---
source: src/low_level.rs
expression: stdout
---
95d09f2b10159347eece71399a7e2e907ea3df4f blob 11 21 12
33afd6485aadae927bc4bc2986ea9a0d86d5d699 tree 42 55 33
b740d25b3a4f2cfe1c472542712249a70e1463eb commit 144 108 88
non delta: 3 objects
/path/to/example.pack: ok
@@ -0,0 +1,5 @@
---
source: src/low_level.rs
expression: actual
---
b"PACK\0\0\0\x02\0\0\0\x03\xbb\0x\x9c\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\x01\0\x1a\x0b\x04]\xaa\x02x\x9c\x01*\0\xd5\xff100644 helloworld.txt\0\x95\xd0\x9f+\x10\x15\x93G\xee\xceq9\x9a~.\x90~\xa3\xdfO<\r\x10\xd3\x90\tx\x9c\x8d\x8bK\n\x840\x10D\xf79E\xef\x07\x86\xe0'\x93\x86a\x98\xab\x94\xe9\x16\x03FEZ\xf0\xf8\nz\0kU\x0f\xde\xb3U\x95\xea\x1a\xbd\x84&\xb6\x80@\xb9\xfat\xa9\xe9R\xc51(\x18^b\x90V\x02\xb3\xc3f\xc3\xbc\x92\xee(\xcb\xa8\xf4\xbd\xcf\xbf\xe8;\xcd\xe5G\x9e^\xfe\x9c;\xa1d3}\xa0\xba<e\xcb\x18\xe9j\x0e\xf9\xdf._L\xcf-\x10\xa1\x87\xe6\x96\xf9\xf4!\xa5\xecZ\x9f[\xc6\xe4\xbd\x01"
@@ -0,0 +1,5 @@
---
source: src/low_level.rs
expression: actual
---
b"\xbb\0x\x9c\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\x01\0\x1a\x0b\x04]"
@@ -0,0 +1,5 @@
---
source: src/low_level.rs
expression: actual
---
b"\x9c\nx\x9c\x8d\x8b\xd1\n@@\x10E\xdf}\xc5xV\xb2\xb5X\xb5\xc9\xaflL\xad\xdam4\x8d\xf8|B$/\xee\xcb9\xdd:\xc2\x88P\xfc\\\xe2f\xf1\xc4p\xc1\x9e\xecpuq\n\x98\xf7\x14[P\x951\xba\xd1\xaa4\x90\x1d\xcd\xfe\xc6Q\x04\x19\x1e\xb3\xb7~\xe2\xfa\x15'\x1eC X\x88\xc3\x90n%S2R"
@@ -0,0 +1,5 @@
---
source: src/low_level.rs
expression: actual
---
b"\xa1\x02x\x9c340031Q\xc8H\xcd\xc9\xc9g\xc0\x06\0YU\x03d"