commit 757f1c0fda08bdb4055b25526383a67a21b0284a
parent 0f5dfafbf7097df7bc083618c5bc9f52eddae03e
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date: Tue, 25 Jun 2024 17:50:37 -0700
Implement basic integration tests
Diffstat:
4 files changed, 284 insertions(+), 32 deletions(-)
diff --git a/README.md b/README.md
@@ -47,11 +47,11 @@ steps:
link:
- src: vim_dotfiles/.vimrc
dst: ~/.vimrc
- tags: [ windows, linux, macos ]
+ tags: [ linux, macos ]
- run:
- src: install_programs.sh
prefix: bash # Unecessary if install_programs.sh is executable
- postfix: -y
+ postfix: $COLIRU_RULES -y
tags: [ linux ]
```
diff --git a/src/utils.rs b/src/utils.rs
@@ -120,8 +120,8 @@ mod tests {
dir
}
- /// Create (or overwrite) a file with certain contents.
- fn create_file(path: &Path, contents: &str) {
+ /// Writes a string to a file, overwriting it if it already exists.
+ fn write_file(path: &Path, contents: &str) {
let mut file = fs::File::create(path).unwrap();
file.write_all(contents.as_bytes()).unwrap();
}
@@ -132,11 +132,11 @@ mod tests {
let src = &tmp.dir.join("foo");
let dst = &tmp.dir.join("dir1").join("dir2").join("bar");
- create_file(src, "old contents of foo");
+ write_file(src, "old contents of foo");
let result = copy_file(src.to_str().unwrap(), dst.to_str().unwrap());
- create_file(src, "new contents of foo");
+ write_file(src, "new contents of foo");
let contents = fs::read_to_string(dst).unwrap();
assert_eq!(result.is_ok(), true);
assert_eq!(contents, "old contents of foo");
@@ -148,12 +148,12 @@ mod tests {
let src = &tmp.dir.join("foo");
let dst = &tmp.dir.join("bar");
- create_file(src, "old contents of foo");
- create_file(dst, "old contents of bar");
+ write_file(src, "old contents of foo");
+ write_file(dst, "old contents of bar");
let result = copy_file(src.to_str().unwrap(), dst.to_str().unwrap());
- create_file(src, "new contents of foo");
+ write_file(src, "new contents of foo");
let contents = fs::read_to_string(dst).unwrap();
assert_eq!(result.is_ok(), true);
assert_eq!(contents, "old contents of foo");
@@ -166,12 +166,12 @@ mod tests {
let src = &tmp.dir.join("foo");
let dst = &tmp.dir.join("bar");
- create_file(src, "old contents of foo");
+ write_file(src, "old contents of foo");
symlink("missing", dst).unwrap();
let result = copy_file(src.to_str().unwrap(), dst.to_str().unwrap());
- create_file(src, "new contents of foo");
+ write_file(src, "new contents of foo");
let contents = fs::read_to_string(dst).unwrap();
assert_eq!(result.is_ok(), true);
assert_eq!(contents, "old contents of foo");
@@ -185,11 +185,11 @@ mod tests {
let src = &tmp.dir.join("foo");
let dst = &tmp.dir.join("dir").join("bar");
let dst_tilde = "~/test_copy_file_tilde_expansion/dir/bar";
- create_file(src, "old contents of foo");
+ write_file(src, "old contents of foo");
let result = copy_file(src.to_str().unwrap(), dst_tilde);
- create_file(src, "new contents of foo");
+ write_file(src, "new contents of foo");
let contents = fs::read_to_string(dst).unwrap();
assert_eq!(result.is_ok(), true);
assert_eq!(contents, "old contents of foo");
@@ -202,11 +202,11 @@ mod tests {
let src = &tmp.dir.join("foo");
let dst = &tmp.dir.join("dir1").join("dir2").join("bar");
- create_file(src, "old contents of foo");
+ write_file(src, "old contents of foo");
let result = link_file(src.to_str().unwrap(), dst.to_str().unwrap());
- create_file(src, "new contents of foo");
+ write_file(src, "new contents of foo");
let contents = fs::read_to_string(dst).unwrap();
assert_eq!(result.is_ok(), true);
assert_eq!(contents, "new contents of foo");
@@ -219,12 +219,12 @@ mod tests {
let src = &tmp.dir.join("foo");
let dst = &tmp.dir.join("bar");
- create_file(src, "old contents of foo");
- create_file(dst, "old contents of bar");
+ write_file(src, "old contents of foo");
+ write_file(dst, "old contents of bar");
let result = link_file(src.to_str().unwrap(), dst.to_str().unwrap());
- create_file(src, "new contents of foo");
+ write_file(src, "new contents of foo");
let contents = fs::read_to_string(dst).unwrap();
assert_eq!(result.is_ok(), true);
assert_eq!(contents, "new contents of foo");
@@ -237,12 +237,12 @@ mod tests {
let src = &tmp.dir.join("foo");
let dst = &tmp.dir.join("bar");
- create_file(src, "old contents of foo");
+ write_file(src, "old contents of foo");
symlink("missing", dst).unwrap();
let result = link_file(src.to_str().unwrap(), dst.to_str().unwrap());
- create_file(src, "new contents of foo");
+ write_file(src, "new contents of foo");
let contents = fs::read_to_string(dst).unwrap();
assert_eq!(result.is_ok(), true);
assert_eq!(contents, "new contents of foo");
@@ -256,11 +256,11 @@ mod tests {
let src = &tmp.dir.join("foo");
let dst = &tmp.dir.join("dir").join("bar");
let dst_tilde = "~/test_link_file_tilde_expansion/dir/bar";
- create_file(src, "old contents of foo");
+ write_file(src, "old contents of foo");
let result = link_file(src.to_str().unwrap(), dst_tilde);
- create_file(src, "new contents of foo");
+ write_file(src, "new contents of foo");
let contents = fs::read_to_string(dst).unwrap();
assert_eq!(result.is_ok(), true);
assert_eq!(contents, "new contents of foo");
@@ -274,11 +274,11 @@ mod tests {
let src = &tmp.dir.join("foo");
let src_rel = "test_link_file_relative_source/foo";
let dst = &tmp.dir.join("dir1").join("dir2").join("bar");
- create_file(src, "old contents of foo");
+ write_file(src, "old contents of foo");
let result = link_file(src_rel, dst.to_str().unwrap());
- create_file(src, "new contents of foo");
+ write_file(src, "new contents of foo");
let contents = fs::read_to_string(dst).unwrap();
let link = fs::read_link(dst).unwrap();
assert_eq!(result.is_ok(), true);
@@ -292,7 +292,7 @@ mod tests {
let tmp = setup("test_run_script_successful");
let src = &tmp.dir.join("foo");
- create_file(src, "exit 0");
+ write_file(src, "exit 0");
let result = run_script(src.to_str().unwrap(), "bash", "");
@@ -305,7 +305,7 @@ mod tests {
let tmp = setup("test_run_script_failure");
let src = &tmp.dir.join("foo");
- create_file(src, "exit 2");
+ write_file(src, "exit 2");
let result = run_script(src.to_str().unwrap(), "bash", "");
@@ -320,7 +320,7 @@ mod tests {
let src = &tmp.dir.join("foo");
let dst = &tmp.dir.join("bar");
- create_file(src, &format!("echo $@ > {}", dst.to_str().unwrap()));
+ write_file(src, &format!("echo $@ > {}", dst.to_str().unwrap()));
let result = run_script(src.to_str().unwrap(), "bash", "arg1 arg2");
diff --git a/tests/basic.rs b/tests/basic.rs
@@ -1,6 +1,37 @@
mod common;
-use common::{get_output, setup};
+use common::*;
+use std::fs::{create_dir_all, read_to_string, remove_file};
+use std::path::Path;
+
+/// Create a basic manifest file and its associated dotfiles in a directory
+fn manifest_1(dir: &Path) {
+ let manifest = "\
+steps:
+ - copy:
+ - src: git_dotfiles/.gitconfig
+ dst: ~/.gitconfig
+ link:
+ - src: vim_dotfiles/.vimrc
+ dst: ~/.vimrc
+ tags: [ windows, linux, macos ]
+ - link:
+ - src: vim_dotfiles/.vimrc
+ dst: ~/_vimrc
+ tags: [ windows ]
+ - run:
+ - src: install_programs.sh
+ prefix: bash # Unecessary if install_programs.sh is executable
+ postfix: $COLIRU_RULES -y
+ tags: [ linux ]
+";
+ write_file(&dir.join("manifest.yml"), manifest);
+ create_dir_all(&dir.join("git_dotfiles")).unwrap();
+ write_file(&dir.join("git_dotfiles").join(".gitconfig"), "git config");
+ create_dir_all(&dir.join("vim_dotfiles")).unwrap();
+ write_file(&dir.join("vim_dotfiles").join(".vimrc"), "vim config");
+ write_file(&dir.join("install_programs.sh"), "echo $@ > log");
+}
#[test]
fn test_help() {
@@ -21,5 +52,214 @@ Options:
-h, --help Print help
-V, --version Print version
";
- assert_eq!(&get_output(&mut cmd), expected);
+ assert_eq!(&stdout_to_string(&mut cmd), expected);
+ assert_eq!(&stderr_to_string(&mut cmd), "");
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_standard() {
+ let (dir, mut cmd) = setup("test_standard");
+ cmd.args(["manifest.yml", "-t", "linux"]);
+ manifest_1(&dir.dir);
+
+ let expected = "\
+[1/3] Copy git_dotfiles/.gitconfig to ~/.gitconfig
+[1/3] Link vim_dotfiles/.vimrc to ~/.vimrc
+[3/3] Run bash install_programs.sh linux -y
+";
+ assert_eq!(&stdout_to_string(&mut cmd), expected);
+ assert_eq!(&stderr_to_string(&mut cmd), "");
+
+ // Assert files are correctly copied/linked/run
+ write_file(&dir.dir.join("git_dotfiles").join(".gitconfig"), "git #2");
+ write_file(&dir.dir.join("vim_dotfiles").join(".vimrc"), "vim #2");
+ let git_contents = read_to_string(&dir.dir.join(".gitconfig")).unwrap();
+ let vim1_contents = read_to_string(&dir.dir.join(".vimrc")).unwrap();
+ let vim2_exists = dir.dir.join("_vimrc").exists();
+ let log_contents = read_to_string(&dir.dir.join("log")).unwrap();
+ assert_eq!(git_contents, "git config");
+ assert_eq!(vim1_contents, "vim #2");
+ assert_eq!(vim2_exists, false);
+ assert_eq!(log_contents, "linux -y\n");
+}
+
+#[test]
+fn test_run_alternate_tag_rules_1() {
+ let (dir, mut cmd) = setup("test_run_alternate_tag_rules_1");
+ cmd.args(["manifest.yml", "-t", "macos"]);
+ manifest_1(&dir.dir);
+
+ let expected = "\
+[1/3] Copy git_dotfiles/.gitconfig to ~/.gitconfig
+[1/3] Link vim_dotfiles/.vimrc to ~/.vimrc
+";
+ assert_eq!(&stdout_to_string(&mut cmd), expected);
+ assert_eq!(&stderr_to_string(&mut cmd), "");
+
+ // Assert files are correctly copied/linked/run
+ write_file(&dir.dir.join("git_dotfiles").join(".gitconfig"), "git #2");
+ write_file(&dir.dir.join("vim_dotfiles").join(".vimrc"), "vim #2");
+ let git_contents = read_to_string(&dir.dir.join(".gitconfig")).unwrap();
+ let vim1_contents = read_to_string(&dir.dir.join(".vimrc")).unwrap();
+ let vim2_exists = dir.dir.join("_vimrc").exists();
+ let log_exists = dir.dir.join("log").exists();
+ assert_eq!(git_contents, "git config");
+ assert_eq!(vim1_contents, "vim #2");
+ assert_eq!(vim2_exists, false);
+ assert_eq!(log_exists, false);
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_run_alternate_tag_rules_2() {
+ let (dir, mut cmd) = setup("test_run_alternate_tag_rules_2");
+ cmd.args(["manifest.yml", "-t", "linux,windows", "^macos"]);
+ manifest_1(&dir.dir);
+
+ let expected = "\
+[2/3] Link vim_dotfiles/.vimrc to ~/_vimrc
+[3/3] Run bash install_programs.sh linux,windows ^macos -y
+";
+ assert_eq!(&stdout_to_string(&mut cmd), expected);
+ assert_eq!(&stderr_to_string(&mut cmd), "");
+
+ // Assert files are correctly copied/linked/run
+ write_file(&dir.dir.join("vim_dotfiles").join(".vimrc"), "vim #2");
+ let git_exists = dir.dir.join(".gitconfig").exists();
+ let vim1_exists = dir.dir.join(".vimrc").exists();
+ let vim2_contents = read_to_string(&dir.dir.join("_vimrc")).unwrap();
+ let log_contents = read_to_string(&dir.dir.join("log")).unwrap();
+ assert_eq!(git_exists, false);
+ assert_eq!(vim1_exists, false);
+ assert_eq!(vim2_contents, "vim #2");
+ assert_eq!(log_contents, "linux,windows ^macos -y\n");
+}
+
+#[test]
+fn test_dry_run() {
+ let (dir, mut cmd) = setup("test_dry_run");
+ cmd.args(["manifest.yml", "--dry-run", "-t", "linux"]);
+ manifest_1(&dir.dir);
+
+ let expected = "\
+[1/3] Copy git_dotfiles/.gitconfig to ~/.gitconfig (DRY RUN)
+[1/3] Link vim_dotfiles/.vimrc to ~/.vimrc (DRY RUN)
+[3/3] Run bash install_programs.sh linux -y (DRY RUN)
+";
+ assert_eq!(&stdout_to_string(&mut cmd), expected);
+ assert_eq!(&stderr_to_string(&mut cmd), "");
+
+ // Assert files are correctly copied/linked/run
+ let git_exists = dir.dir.join(".gitconfig").exists();
+ let vim1_exists = dir.dir.join(".vimrc").exists();
+ let vim2_exists = dir.dir.join("_vimrc").exists();
+ let log_exists = dir.dir.join("log").exists();
+ assert_eq!(git_exists, false);
+ assert_eq!(vim1_exists, false);
+ assert_eq!(vim2_exists, false);
+ assert_eq!(log_exists, false);
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_copy() {
+ let (dir, mut cmd) = setup("test_copy");
+ cmd.args(["manifest.yml", "--copy", "-t", "linux"]);
+ manifest_1(&dir.dir);
+
+ let expected = "\
+[1/3] Copy git_dotfiles/.gitconfig to ~/.gitconfig
+[1/3] Copy vim_dotfiles/.vimrc to ~/.vimrc
+[3/3] Run bash install_programs.sh linux -y
+";
+ assert_eq!(&stdout_to_string(&mut cmd), expected);
+ assert_eq!(&stderr_to_string(&mut cmd), "");
+
+ // Assert files are correctly copied/linked/run
+ write_file(&dir.dir.join("git_dotfiles").join(".gitconfig"), "git #2");
+ write_file(&dir.dir.join("vim_dotfiles").join(".vimrc"), "vim #2");
+ let git_contents = read_to_string(&dir.dir.join(".gitconfig")).unwrap();
+ let vim1_contents = read_to_string(&dir.dir.join(".vimrc")).unwrap();
+ let vim2_exists = dir.dir.join("_vimrc").exists();
+ let log_contents = read_to_string(&dir.dir.join("log")).unwrap();
+ assert_eq!(git_contents, "git config");
+ assert_eq!(vim1_contents, "vim config");
+ assert_eq!(vim2_exists, false);
+ assert_eq!(log_contents, "linux -y\n");
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_run_failure() {
+ let (dir, mut cmd) = setup("test_run_failure");
+ cmd.args(["manifest.yml", "-t", "linux"]);
+ manifest_1(&dir.dir);
+ write_file(&dir.dir.join("install_programs.sh"), "exit 1");
+
+ let expected_stdout = "\
+[1/3] Copy git_dotfiles/.gitconfig to ~/.gitconfig
+[1/3] Link vim_dotfiles/.vimrc to ~/.vimrc
+[3/3] Run bash install_programs.sh linux -y
+";
+ let expected_stderr = " Error: Process exited with exit status: 1\n";
+ assert_eq!(&stdout_to_string(&mut cmd), expected_stdout);
+ assert_eq!(&stderr_to_string(&mut cmd), expected_stderr);
+
+ // Assert files are correctly copied/linked/run
+ write_file(&dir.dir.join("git_dotfiles").join(".gitconfig"), "git #2");
+ write_file(&dir.dir.join("vim_dotfiles").join(".vimrc"), "vim #2");
+ let git_contents = read_to_string(&dir.dir.join(".gitconfig")).unwrap();
+ let vim1_contents = read_to_string(&dir.dir.join(".vimrc")).unwrap();
+ let vim2_exists = dir.dir.join("_vimrc").exists();
+ assert_eq!(git_contents, "git config");
+ assert_eq!(vim1_contents, "vim #2");
+ assert_eq!(vim2_exists, false);
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_missing_file() {
+ let (dir, mut cmd) = setup("test_missing_file");
+ cmd.args(["manifest.yml", "-t", "linux"]);
+ manifest_1(&dir.dir);
+ remove_file(&dir.dir.join("vim_dotfiles").join(".vimrc")).unwrap();
+
+ let expected_stdout = "\
+[1/3] Copy git_dotfiles/.gitconfig to ~/.gitconfig
+[1/3] Link vim_dotfiles/.vimrc to ~/.vimrc
+[3/3] Run bash install_programs.sh linux -y
+";
+ let expected_stderr = " Error: No such file or directory (os error 2)\n";
+ assert_eq!(&stdout_to_string(&mut cmd), expected_stdout);
+ assert_eq!(&stderr_to_string(&mut cmd), expected_stderr);
+
+ // Assert files are correctly copied/linked/run
+ write_file(&dir.dir.join("git_dotfiles").join(".gitconfig"), "git #2");
+ let git_contents = read_to_string(&dir.dir.join(".gitconfig")).unwrap();
+ let log_contents = read_to_string(&dir.dir.join("log")).unwrap();
+ assert_eq!(git_contents, "git config");
+ assert_eq!(log_contents, "linux -y\n");
+}
+
+#[test]
+fn test_empty_manifest() {
+ let (dir, mut cmd) = setup("test_empty_manifest");
+ cmd.args(["manifest.yml"]);
+ write_file(&dir.dir.join("manifest.yml"), "");
+
+ let expected = "Error: missing field `steps`\n";
+ assert_eq!(&stdout_to_string(&mut cmd), "");
+ assert_eq!(&stderr_to_string(&mut cmd), expected);
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_missing_manifest() {
+ let (_dir, mut cmd) = setup("test_missing_manifest");
+ cmd.args(["missing.yml"]);
+
+ let expected = "Error: No such file or directory (os error 2)\n";
+ assert_eq!(&stdout_to_string(&mut cmd), "");
+ assert_eq!(&stderr_to_string(&mut cmd), expected);
}
diff --git a/tests/common/mod.rs b/tests/common/mod.rs
@@ -1,6 +1,7 @@
use std::env;
use std::fs;
-use std::path::PathBuf;
+use std::io::Write;
+use std::path::{Path, PathBuf};
use std::process::Command;
/// Stores the path to a temporary directory that is automatically deleted
@@ -8,7 +9,7 @@ use std::process::Command;
///
/// Adapted from ripgrep's tests (crates/ignore/src/lib.rs)
pub struct TempDir {
- dir: PathBuf
+ pub dir: PathBuf
}
impl Drop for TempDir {
fn drop(&mut self) {
@@ -43,7 +44,18 @@ pub fn setup(name: &str) -> (TempDir, Command) {
(dir, cmd)
}
+/// Writes a string to a file, overwriting it if it already exists.
+pub fn write_file(path: &Path, contents: &str) {
+ let mut file = fs::File::create(path).unwrap();
+ file.write_all(contents.as_bytes()).unwrap();
+}
+
/// Returns the stdout of a command as a String.
-pub fn get_output(cmd: &mut Command) -> String {
+pub fn stdout_to_string(cmd: &mut Command) -> String {
String::from_utf8_lossy(&cmd.output().unwrap().stdout).into_owned()
}
+
+/// Returns the stderr of a command as a String.
+pub fn stderr_to_string(cmd: &mut Command) -> String {
+ String::from_utf8_lossy(&cmd.output().unwrap().stderr).into_owned()
+}