// SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021 Alyssa Ross use std::{ env::var_os, fs::create_dir, io::{stderr, Write}, process::Command, }; use git2::{Commit, IntoCString, Oid, Reference, Signature, Time, Tree}; use tempdir::TempDir; struct Repo<'a> { inner: git2::Repository, signature: Signature<'a>, } impl Repo<'_> { fn new(inner: git2::Repository) -> Self { Self { inner, signature: Signature::new("git-girf test suite", "git-girf@test", &Time::new(0, 0)) .unwrap(), } } fn commit(&self, files: &[(impl AsRef<[u8]>, impl AsRef<[u8]>)]) -> Oid { let mut treebuilder = self.inner.treebuilder(None).unwrap(); for (path, data) in files { let blob = self.inner.blob(data.as_ref()).unwrap(); treebuilder.insert(path.as_ref(), blob, 0o100644).unwrap(); } let tree_oid = treebuilder.write().unwrap(); let tree = self.inner.find_tree(tree_oid).unwrap(); let parent_oids = self .inner .head() .into_iter() .map(|head| head.target().unwrap()); let parents: Vec<_> = parent_oids .map(|oid| self.inner.find_commit(oid).unwrap()) .collect(); let borrowed_parents: Vec<_> = parents.iter().collect(); dbg!(self.inner .commit( Some("HEAD"), &self.signature, &self.signature, "A commit", &tree, &borrowed_parents, ) .unwrap()) } } #[test] fn happy() { let dir = TempDir::new("git-girf-tests").unwrap(); let path = var_os("PATH").unwrap_or_else(|| "/usr/bin:/bin".into()); let repo = Repo::new(git2::Repository::init(&dir).expect("opening repo")); // We're going to use `tail -n 1` as our filter command, meaning // commits shouldn't be printed if they change the last line of // any file. let mut expected = Vec::new(); repo.commit(&[("a", "a\na\n")]); // This creates a file, so shouldn't be printed. repo.commit(&[("a", "a\na\n"), ("b", "b\nb\n")]); // This changes the first lines only, so should be printed. expected.push(repo.commit(&[("a", "A\na\n"), ("b", "B\nb\n")])); // This changes the last line, so shouldn't be printed. repo.commit(&[("a", "A\nA\n"), ("b", "b\nB\n")]); // This has lots of files, but it should be obvious that it // doesn't have to be printed after the first file. We include it // to test that short circuiting doesn't panic the diff thread. repo.commit(&(b'a'..b'z').map(|c| ([c], [c, b'\n'])).collect::>()); let output = Command::new(env!("CARGO_BIN_EXE_git-girf")) .args(&["tail", "-n", "1"]) .current_dir(&dir) .env_clear() .env("PATH", path) .output() .expect("spawn"); let _ = stderr().write_all(&output.stderr); let len = expected.len(); let mut expected_bytes = Vec::with_capacity(41 * expected.len()); for oid in expected { expected_bytes.extend_from_slice(oid.to_string().as_bytes()); expected_bytes.push(b'\n'); } assert!(output.stderr.is_empty()); assert_eq!( String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&expected_bytes) ); assert!(output.status.success()); } #[test] fn no_repo() { let output = Command::new(env!("CARGO_BIN_EXE_git-girf")) .arg("cat") .current_dir("/var/empty") .env_clear() .output() .expect("spawn"); assert_eq!(output.status.code(), Some(128)); assert_eq!( output.stderr, b"fatal: could not find repository from '.'\n" ); assert!(output.stdout.is_empty()); } #[test] fn no_args() { let output = Command::new(env!("CARGO_BIN_EXE_git-girf")) .current_dir("/var/empty") .env_clear() .output() .expect("spawn"); assert_eq!(output.status.code(), Some(128)); assert_eq!(output.stderr, b"fatal: no command given\n"); assert!(output.stdout.is_empty()); } #[test] fn flag() { let output = Command::new(env!("CARGO_BIN_EXE_git-girf")) .arg("-Z") .current_dir("/var/empty") .env_clear() .output() .expect("spawn"); assert_eq!(output.status.code(), Some(128)); assert_eq!(output.stderr, b"fatal: unrecognized argument: -Z\n"); assert!(output.stdout.is_empty()); } #[test] fn filter_sigpipe() { todo!(); } #[test] fn sigpipe() { todo!(); }