From 55938bb8888a89723e60517d9367cec83e0e18d4 Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Tue, 4 Jul 2023 21:10:49 +0100 Subject: [PATCH] Add snapshot and git-verify-pack tests for low-level API --- 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(-) create mode 100644 src/snapshots/packfile__low_level__test__packfile__is_readable_by_git.snap create mode 100644 src/snapshots/packfile__low_level__test__packfile__snapshot.snap create mode 100644 src/snapshots/packfile__low_level__test__packfile_entry__blob__full.snap create mode 100644 src/snapshots/packfile__low_level__test__packfile_entry__commit__full.snap create mode 100644 src/snapshots/packfile__low_level__test__packfile_entry__tree__full.snap diff --git a/src/high_level.rs b/src/high_level.rs index 9e3035c..94b6472 100644 --- a/src/high_level.rs +++ b/src/high_level.rs @@ -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") ]}, { diff --git a/src/lib.rs b/src/lib.rs index 4c4b38d..f8af8dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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() + } +} diff --git a/src/low_level.rs b/src/low_level.rs index 4d51d6c..37d9ad5 100644 --- a/src/low_level.rs +++ b/src/low_level.rs @@ -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); + } + } } } diff --git a/src/snapshots/packfile__low_level__test__packfile__is_readable_by_git.snap b/src/snapshots/packfile__low_level__test__packfile__is_readable_by_git.snap new file mode 100644 index 0000000..e2e89b1 --- /dev/null +++ b/src/snapshots/packfile__low_level__test__packfile__is_readable_by_git.snap @@ -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 + diff --git a/src/snapshots/packfile__low_level__test__packfile__snapshot.snap b/src/snapshots/packfile__low_level__test__packfile__snapshot.snap new file mode 100644 index 0000000..7a1d745 --- /dev/null +++ b/src/snapshots/packfile__low_level__test__packfile__snapshot.snap @@ -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