-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tests: initial setup of integration tests
- Loading branch information
1 parent
fae1c3d
commit fae1435
Showing
5 changed files
with
249 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#[macro_export] | ||
macro_rules! gashtest { | ||
($name:ident, $func:expr) => { | ||
#[test] | ||
fn $name() { | ||
let cmd = crate::util::setup(stringify!($name)); | ||
$func(cmd); | ||
} | ||
}; | ||
} | ||
|
||
#[macro_export] | ||
macro_rules! eqnice { | ||
($expected:expr, $got:expr) => { | ||
let expected = &*$expected; | ||
let got = &*$got; | ||
if expected != got { | ||
panic!( | ||
" | ||
printed outputs differ! | ||
expected: | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
{} | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
got: | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
{} | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
", | ||
expected, got | ||
); | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// Macros useful for testing. | ||
#[macro_use] | ||
mod macros; | ||
|
||
// Utilities for making tests easier to write. | ||
mod util; | ||
|
||
// Tests themselves. | ||
mod test_prefix; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
use std::fs; | ||
|
||
use crate::util::{git_last_hash, TestCommand}; | ||
|
||
// Simple run with prefix. | ||
gashtest!(it_finds_a_prefix, |mut tcmd: TestCommand| { | ||
let prefix = "dead"; | ||
let stdout = tcmd.args(&[prefix]).stdout(); | ||
|
||
let expected = format!( | ||
"\ | ||
Searching for hash with prefix {prefix} | ||
Found hash {prefix}{hash} | ||
Patching last commit to include new hash... Success! | ||
", | ||
prefix = prefix, | ||
hash = &git_last_hash(tcmd.dir())[prefix.len()..] | ||
); | ||
|
||
eqnice!(expected, stdout); | ||
}); | ||
|
||
// Does not patch the commit with --dry-run. | ||
gashtest!(dry_run_long_prefix, |mut tcmd: TestCommand| { | ||
let hash_before = git_last_hash(tcmd.dir()); | ||
let stdout = tcmd.args(&["dead", "--dry-run"]).stdout(); | ||
let hash_after = git_last_hash(tcmd.dir()); | ||
|
||
assert_eq!( | ||
true, | ||
stdout.contains(&"Not amending commit due to --dry-run") | ||
); | ||
assert_eq!(hash_before, hash_after); | ||
}); | ||
|
||
// Test fails if not run in a git repostiory. | ||
gashtest!(it_does_not_work_outside_of_git, |mut tcmd: TestCommand| { | ||
fs::remove_dir_all(&tcmd.dir().join(".git")).unwrap(); | ||
|
||
let expected = "\ | ||
Error: the command: 'git rev-parse HEAD' failed with: | ||
fatal: not a git repository (or any parent up to mount point /) | ||
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set). | ||
"; | ||
eqnice!(expected, tcmd.args(&["dead"]).stderr()); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
use std::env; | ||
use std::fs; | ||
use std::io::Write; | ||
use std::path::{Path, PathBuf}; | ||
use std::process::{Command, Output}; | ||
use std::sync::atomic::{AtomicUsize, Ordering}; | ||
|
||
const TEST_DIR: &str = "gash-tests"; | ||
const NEXT_ID: AtomicUsize = AtomicUsize::new(0); | ||
|
||
pub fn git(dir: impl AsRef<Path>, args: &[&str]) -> Result<String, String> { | ||
let dir = dir.as_ref(); | ||
let output = Command::new("git") | ||
.current_dir(&dir) | ||
// Do not use system git config (/etc/gitconfig). | ||
.env("GIT_CONFIG_NOSYSTEM", "1") | ||
// Change `HOME` so ~/.gitconfig isn't used. | ||
.env("HOME", &format!("{}", dir.display())) | ||
.args(args) | ||
.output() | ||
.expect("Failed to run git"); | ||
|
||
if !output.status.success() { | ||
Err(format!("Failed to run git!\n{:?}", output)) | ||
} else { | ||
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string()) | ||
} | ||
} | ||
|
||
pub fn git_last_hash(dir: impl AsRef<Path>) -> String { | ||
git(&dir, &["rev-parse", "HEAD"]).unwrap() | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct TestCommand { | ||
cmd: Command, | ||
dir: PathBuf, | ||
exe: PathBuf, | ||
} | ||
|
||
impl TestCommand { | ||
pub fn new(name: &str) -> TestCommand { | ||
// Find the location of the binary we're testing. | ||
let exe = env::current_exe() | ||
.unwrap() | ||
.parent() | ||
.unwrap() | ||
.join(format!("../gash{}", env::consts::EXE_SUFFIX)); | ||
|
||
// Create a temporary directory for each test. | ||
let next_id = NEXT_ID.fetch_add(1, Ordering::SeqCst); | ||
let dir = env::temp_dir() | ||
.join(TEST_DIR) | ||
.join(name) | ||
.join(&format!("{}", next_id)); | ||
|
||
if dir.exists() { | ||
fs::remove_dir_all(&dir).expect("Failed to remove pre-existing directory"); | ||
} | ||
|
||
// Initialise a git repository with a single commit. | ||
TestCommand::git_init(&dir); | ||
|
||
// Create a command. | ||
let mut cmd = Command::new(&exe); | ||
cmd.current_dir(&dir); | ||
// Do not use system git config (/etc/gitconfig). | ||
cmd.env("GIT_CONFIG_NOSYSTEM", "1"); | ||
// Change `HOME` so ~/.gitconfig isn't used. | ||
cmd.env("HOME", &format!("{}", dir.display())); | ||
|
||
TestCommand { cmd, dir, exe } | ||
} | ||
|
||
pub fn dir(&self) -> PathBuf { | ||
self.dir.clone() | ||
} | ||
|
||
fn git_init(dir: impl AsRef<Path>) { | ||
let dir = dir.as_ref(); | ||
if dir.exists() { | ||
panic!("The path {} already exists!", dir.display()); | ||
} | ||
|
||
// Run from parent directory since this creates the target directory. | ||
let parent_dir = dir.parent().unwrap(); | ||
fs::create_dir_all(&parent_dir).unwrap(); | ||
|
||
// Initialise the repository. | ||
git(&parent_dir, &["init", &format!("{}", dir.display())]).unwrap(); | ||
|
||
// Set "user.name" and "user.email" in the new repository. | ||
git(&dir, &["config", "user.name", "elliot alderson"]).unwrap(); | ||
git( | ||
&dir, | ||
&["config", "user.email", "[email protected]"], | ||
) | ||
.unwrap(); | ||
|
||
// Add an initial commit. | ||
fs::OpenOptions::new() | ||
.create(true) | ||
.truncate(true) | ||
.write(true) | ||
.open(dir.join("foo")) | ||
.unwrap() | ||
.write(b"hello") | ||
.unwrap(); | ||
git(&dir, &["add", "foo"]).unwrap(); | ||
git(&dir, &["commit", "-m", "initial commit"]).unwrap(); | ||
} | ||
|
||
pub fn args(&mut self, args: &[&str]) -> &mut Self { | ||
self.cmd.args(args); | ||
self | ||
} | ||
|
||
pub fn stdout(&mut self) -> String { | ||
let output = self.cmd.output().unwrap(); | ||
self.expect_success(&output); | ||
String::from_utf8_lossy(&output.stdout).to_string() | ||
} | ||
|
||
pub fn stderr(&mut self) -> String { | ||
let output = self.cmd.output().unwrap(); | ||
String::from_utf8_lossy(&output.stderr).to_string() | ||
} | ||
|
||
fn expect_success(&self, output: &Output) { | ||
if !output.status.success() { | ||
panic!( | ||
"\n\n==========\n\ | ||
command failed but expected success!\ | ||
\ | ||
\n\ncommand: {:?}\ | ||
\n\ncwd: {}\ | ||
\n\nstatus: {}\ | ||
\n\nstdout: {}\ | ||
\n\nstderr: {}\ | ||
\n\n==========\n", | ||
self.cmd, | ||
self.dir.display(), | ||
output.status, | ||
String::from_utf8_lossy(&output.stdout), | ||
String::from_utf8_lossy(&output.stderr) | ||
); | ||
} | ||
} | ||
} | ||
|
||
pub fn setup(test_name: &str) -> TestCommand { | ||
TestCommand::new(test_name) | ||
} |