commit 7a92ec2027c0d172e402efbc48357cd264ee7c82
parent 8c1b2f12888886685a57b9e68ad5b7948370ac24
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date: Wed, 26 Jun 2024 15:54:42 -0700
Add tests for Windows
Diffstat:
6 files changed, 277 insertions(+), 57 deletions(-)
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
@@ -8,7 +8,7 @@ env:
CARGO_TERM_COLOR: always
jobs:
- build:
+ build-and-test-linux:
runs-on: ubuntu-latest
steps:
@@ -19,3 +19,15 @@ jobs:
- name: Run tests
run: cargo test --verbose
+
+ build-and-test-windows:
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Build
+ run: cargo build --verbose
+
+ - name: Run tests
+ run: cargo test --verbose
diff --git a/examples/manifest-windows-test.yml b/examples/manifest-windows-test.yml
@@ -0,0 +1,25 @@
+# Identical to manifest.yml, but uses relative paths instead of paths with
+# tildes becuase $HOME is difficult to mock on windows.
+
+steps:
+ - copy:
+ - src: gitconfig
+ dst: .gitconfig.coliru
+ tags: [ windows, linux, macos ]
+
+ - link:
+ - src: vimrc
+ dst: .vimrc.coliru # Will create symbolic link on Linux & MacOS
+ run:
+ - src: script.sh
+ prefix: sh # unecessary on Unix if script.sh is executable
+ postfix: arg1 $COLIRU_RULES
+ tags: [ linux, macos ]
+
+ - link:
+ - src: vimrc
+ dst: _vimrc.coliru # Will create hard link on Windows
+ run:
+ - src: script.bat
+ postfix: arg1 $COLIRU_RULES
+ tags: [ windows ]
diff --git a/src/manifest.rs b/src/manifest.rs
@@ -67,7 +67,7 @@ mod tests {
use super::*;
#[test]
- #[cfg(target_os = "linux")]
+ #[cfg(target_family = "unix")]
fn parse_manifest_file_missing() {
let expected = "No such file or directory (os error 2)";
let actual = parse_manifest_file(Path::new("examples/missing.yml"));
@@ -75,10 +75,19 @@ mod tests {
}
#[test]
+ #[cfg(target_family = "windows")]
+ fn parse_manifest_file_missing() {
+ let exp = "The system cannot find the file specified. (os error 2)";
+ let actual = parse_manifest_file(Path::new("examples/missing.yml"));
+ assert_eq!(actual, Err(String::from(exp)));
+ }
+
+ #[test]
fn parse_manifest_file_invalid() {
- let expected = "steps[0].copy[0]: missing field `src` at line 5 column 7";
- let actual = parse_manifest_file(Path::new("examples/manifest-invalid.yml"));
- assert_eq!(actual, Err(String::from(expected)));
+ let path = Path::new("examples/manifest-invalid.yml");
+ let exp = "steps[0].copy[0]: missing field `src` at line 5 column 7";
+ let actual = parse_manifest_file(path);
+ assert_eq!(actual, Err(String::from(exp)));
}
#[test]
diff --git a/src/utils.rs b/src/utils.rs
@@ -196,7 +196,6 @@ mod tests {
}
#[test]
- #[cfg(target_family = "unix")]
fn test_link_file_create_dirs() {
let tmp = setup("test_link_file_create_dirs");
@@ -213,7 +212,6 @@ mod tests {
}
#[test]
- #[cfg(target_family = "unix")]
fn test_link_file_existing_file() {
let tmp = setup("test_link_file_existing_file");
@@ -294,7 +292,20 @@ mod tests {
let src = &tmp.dir.join("foo");
write_file(src, "exit 0");
- let result = run_script(src.to_str().unwrap(), "bash", "");
+ let result = run_script(src.to_str().unwrap(), "sh", "");
+
+ assert_eq!(result.is_ok(), true);
+ }
+
+ #[test]
+ #[cfg(target_family = "windows")]
+ fn test_run_script_successful() {
+ let tmp = setup("test_run_script_successful");
+
+ let src = &tmp.dir.join("foo.bat");
+ write_file(src, "exit 0");
+
+ let result = run_script(src.to_str().unwrap(), "", "");
assert_eq!(result.is_ok(), true);
}
@@ -307,13 +318,27 @@ mod tests {
let src = &tmp.dir.join("foo");
write_file(src, "exit 2");
- let result = run_script(src.to_str().unwrap(), "bash", "");
+ let result = run_script(src.to_str().unwrap(), "sh", "");
assert_eq!(result.is_ok(), false);
assert_eq!(result.unwrap_err(), "Process exited with exit status: 2");
}
#[test]
+ #[cfg(target_family = "windows")]
+ fn test_run_script_failure() {
+ let tmp = setup("test_run_script_failure");
+
+ let src = &tmp.dir.join("foo.bat");
+ write_file(src, "exit 1");
+
+ let result = run_script(src.to_str().unwrap(), "", "");
+
+ assert_eq!(result.is_ok(), false);
+ assert_eq!(result.unwrap_err(), "Process exited with exit code: 1");
+ }
+
+ #[test]
#[cfg(target_family = "unix")]
fn test_run_script_postfix() {
let tmp = setup("test_run_script_postfix");
@@ -322,10 +347,26 @@ mod tests {
let dst = &tmp.dir.join("bar");
write_file(src, &format!("echo $@ > {}", dst.to_str().unwrap()));
- let result = run_script(src.to_str().unwrap(), "bash", "arg1 arg2");
+ let result = run_script(src.to_str().unwrap(), "sh", "arg1 arg2");
let contents = fs::read_to_string(dst).unwrap();
assert_eq!(result.is_ok(), true);
assert_eq!(contents, "arg1 arg2\n");
}
+
+ #[test]
+ #[cfg(target_family = "windows")]
+ fn test_run_script_postfix() {
+ let tmp = setup("test_run_script_postfix");
+
+ let src = &tmp.dir.join("foo.bat");
+ let dst = &tmp.dir.join("bar");
+ write_file(src, &format!("echo %* > {}", dst.to_str().unwrap()));
+
+ let result = run_script(src.to_str().unwrap(), "", "arg1 arg2");
+
+ let contents = fs::read_to_string(dst).unwrap();
+ assert_eq!(result.is_ok(), true);
+ assert_eq!(contents, "arg1 arg2 \r\n");
+ }
}
diff --git a/tests/basic.rs b/tests/basic.rs
@@ -1,8 +1,8 @@
mod common;
use common::*;
-use std::env::current_exe;
-use std::fs::{copy, read_to_string, remove_file};
+use std::env::{current_exe, consts::EXE_SUFFIX};
+use std::fs::{copy, remove_file};
use std::path::Path;
/// Create a basic manifest file and its associated dotfiles in a directory
@@ -16,6 +16,7 @@ fn manifest_1(dir: &Path) {
copy_file("script.bat");
copy_file("script.sh");
copy_file("manifest.yml");
+ copy_file("manifest-windows-test.yml");
// Create simplified config files
write_file(&dir.join("gitconfig"), "git #1");
@@ -27,10 +28,10 @@ fn manifest_1(dir: &Path) {
fn test_help() {
let (_dir, mut cmd) = setup("test_help");
cmd.arg("--help");
- let expected = "\
+ let expected = format!("\
A minimal, flexible, dotfile installer
-Usage: coliru [OPTIONS] <MANIFEST>
+Usage: coliru{EXE_SUFFIX} [OPTIONS] <MANIFEST>
Arguments:
<MANIFEST> The path to the coliru YAML manifest file
@@ -41,13 +42,13 @@ Options:
-n, --dry-run Do a trial run without any permanent changes
-h, --help Print help
-V, --version Print version
-";
- assert_eq!(&stdout_to_string(&mut cmd), expected);
+");
+ assert_eq!(stdout_to_string(&mut cmd), expected);
assert_eq!(&stderr_to_string(&mut cmd), "");
}
#[test]
-#[cfg(target_os = "linux")]
+#[cfg(target_family = "unix")]
fn test_standard() {
let (dir, mut cmd) = setup("test_standard");
cmd.args(["manifest.yml", "-t", "linux"]);
@@ -65,10 +66,10 @@ script.sh called with arg1 linux
// Assert files are correctly copied/linked/run
write_file(&dir.dir.join("gitconfig"), "git #2");
write_file(&dir.dir.join("vimrc"), "vim #2");
- let git_contents = read_to_string(&dir.dir.join(".gitconfig.coliru")).unwrap();
- let vim1_contents = read_to_string(&dir.dir.join(".vimrc.coliru")).unwrap();
- let vim2_exists = dir.dir.join("_vimrc").exists();
- let log_contents = read_to_string(&dir.dir.join("log.txt")).unwrap();
+ let git_contents = read_file(&dir.dir.join(".gitconfig.coliru"));
+ let vim1_contents = read_file(&dir.dir.join(".vimrc.coliru"));
+ let vim2_exists = dir.dir.join("_vimrc.coliru").exists();
+ let log_contents = read_file(&dir.dir.join("log.txt"));
assert_eq!(git_contents, "git #1");
assert_eq!(vim1_contents, "vim #2");
assert_eq!(vim2_exists, false);
@@ -76,58 +77,88 @@ script.sh called with arg1 linux
}
#[test]
+#[cfg(target_family = "windows")]
+fn test_standard() {
+ let (dir, mut cmd) = setup("test_standard");
+ cmd.args(["manifest-windows-test.yml", "-t", "windows"]);
+ manifest_1(&dir.dir);
+
+ let expected = "\
+[1/3] Copy gitconfig to .gitconfig.coliru
+[3/3] Link vimrc to _vimrc.coliru
+[3/3] Run script.bat arg1 windows
+script.bat called with arg1 windows\r
+";
+ 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("gitconfig"), "git #2");
+ write_file(&dir.dir.join("vimrc"), "vim #2");
+ let git_contents = read_file(&dir.dir.join(".gitconfig.coliru"));
+ let vim1_exists = dir.dir.join(".vimrc.coliru").exists();
+ let vim2_contents = read_file(&dir.dir.join("_vimrc.coliru"));
+ let log_contents = read_file(&dir.dir.join("log.txt"));
+ assert_eq!(git_contents, "git #1");
+ assert_eq!(vim1_exists, false);
+ assert_eq!(vim2_contents, "vim #2");
+ assert_eq!(log_contents, "script.bat called with arg1 windows \r\n");
+}
+
+#[test]
+#[cfg(target_family = "unix")]
fn test_run_alternate_tag_rules_1() {
let (dir, mut cmd) = setup("test_run_alternate_tag_rules_1");
- cmd.args(["manifest.yml", "-t", "macos"]);
+ cmd.args(["manifest.yml", "-t", "linux", "^windows"]);
manifest_1(&dir.dir);
let expected = "\
-[1/3] Copy gitconfig to ~/.gitconfig.coliru
[2/3] Link vimrc to ~/.vimrc.coliru
-[2/3] Run sh script.sh arg1 macos
-script.sh called with arg1 macos
+[2/3] Run sh script.sh arg1 linux ^windows
+script.sh called with arg1 linux ^windows
";
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("gitconfig"), "git #2");
write_file(&dir.dir.join("vimrc"), "vim #2");
- let git_contents = read_to_string(&dir.dir.join(".gitconfig.coliru")).unwrap();
- let vim1_contents = read_to_string(&dir.dir.join(".vimrc.coliru")).unwrap();
- let vim2_exists = dir.dir.join("_vimrc").exists();
- let log_contents = read_to_string(&dir.dir.join("log.txt")).unwrap();
- assert_eq!(git_contents, "git #1");
+ let git_exists = dir.dir.join(".gitconfig.coliru").exists();
+ let vim1_contents = read_file(&dir.dir.join(".vimrc.coliru"));
+ let vim2_exists = dir.dir.join("_vimrc.coliru").exists();
+ let log_contents = read_file(&dir.dir.join("log.txt"));
+ assert_eq!(git_exists, false);
assert_eq!(vim1_contents, "vim #2");
assert_eq!(vim2_exists, false);
- assert_eq!(log_contents, "script.sh called with arg1 macos\n");
+ assert_eq!(log_contents, "script.sh called with arg1 linux ^windows\n");
}
#[test]
-#[cfg(target_os = "linux")]
+#[cfg(target_family = "unix")]
fn test_run_alternate_tag_rules_2() {
let (dir, mut cmd) = setup("test_run_alternate_tag_rules_2");
- cmd.args(["manifest.yml", "-t", "linux,macos", "^windows"]);
+ cmd.args(["manifest.yml", "-t", "macos"]);
manifest_1(&dir.dir);
let expected = "\
+[1/3] Copy gitconfig to ~/.gitconfig.coliru
[2/3] Link vimrc to ~/.vimrc.coliru
-[2/3] Run sh script.sh arg1 linux,macos ^windows
-script.sh called with arg1 linux,macos ^windows
+[2/3] Run sh script.sh arg1 macos
+script.sh called with arg1 macos
";
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("gitconfig"), "git #2");
write_file(&dir.dir.join("vimrc"), "vim #2");
- let git_exists = dir.dir.join(".gitconfig.coliru").exists();
- let vim1_contents = read_to_string(&dir.dir.join(".vimrc.coliru")).unwrap();
- let vim2_exists = dir.dir.join("_vimrc").exists();
- let log_contents = read_to_string(&dir.dir.join("log.txt")).unwrap();
- assert_eq!(git_exists, false);
+ let git_contents = read_file(&dir.dir.join(".gitconfig.coliru"));
+ let vim1_contents = read_file(&dir.dir.join(".vimrc.coliru"));
+ let vim2_exists = dir.dir.join("_vimrc.coliru").exists();
+ let log_contents = read_file(&dir.dir.join("log.txt"));
+ assert_eq!(git_contents, "git #1");
assert_eq!(vim1_contents, "vim #2");
assert_eq!(vim2_exists, false);
- assert_eq!(log_contents, "script.sh called with arg1 linux,macos ^windows\n");
+ assert_eq!(log_contents, "script.sh called with arg1 macos\n");
}
#[test]
@@ -147,7 +178,7 @@ fn test_dry_run() {
// Assert files are correctly copied/linked/run
let git_exists = dir.dir.join(".gitconfig.coliru").exists();
let vim1_exists = dir.dir.join(".vimrc.coliru").exists();
- let vim2_exists = dir.dir.join("_vimrc").exists();
+ let vim2_exists = dir.dir.join("_vimrc.coliru").exists();
let log_exists = dir.dir.join("log.txt").exists();
assert_eq!(git_exists, false);
assert_eq!(vim1_exists, false);
@@ -156,7 +187,7 @@ fn test_dry_run() {
}
#[test]
-#[cfg(target_os = "linux")]
+#[cfg(target_family = "unix")]
fn test_copy() {
let (dir, mut cmd) = setup("test_copy");
cmd.args(["manifest.yml", "--copy", "-t", "linux"]);
@@ -174,10 +205,10 @@ script.sh called with arg1 linux
// Assert files are correctly copied/linked/run
write_file(&dir.dir.join("gitconfig"), "git #2");
write_file(&dir.dir.join("vimrc"), "vim #2");
- let git_contents = read_to_string(&dir.dir.join(".gitconfig.coliru")).unwrap();
- let vim1_contents = read_to_string(&dir.dir.join(".vimrc.coliru")).unwrap();
- let vim2_exists = dir.dir.join("_vimrc").exists();
- let log_contents = read_to_string(&dir.dir.join("log.txt")).unwrap();
+ let git_contents = read_file(&dir.dir.join(".gitconfig.coliru"));
+ let vim1_contents = read_file(&dir.dir.join(".vimrc.coliru"));
+ let vim2_exists = dir.dir.join("_vimrc.coliru").exists();
+ let log_contents = read_file(&dir.dir.join("log.txt"));
assert_eq!(git_contents, "git #1");
assert_eq!(vim1_contents, "vim #1");
assert_eq!(vim2_exists, false);
@@ -185,7 +216,36 @@ script.sh called with arg1 linux
}
#[test]
-#[cfg(target_os = "linux")]
+#[cfg(target_family = "windows")]
+fn test_copy() {
+ let (dir, mut cmd) = setup("test_copy");
+ cmd.args(["manifest-windows-test.yml", "--copy", "-t", "windows"]);
+ manifest_1(&dir.dir);
+
+ let expected = "\
+[1/3] Copy gitconfig to .gitconfig.coliru
+[3/3] Copy vimrc to _vimrc.coliru
+[3/3] Run script.bat arg1 windows
+script.bat called with arg1 windows\r
+";
+ 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("gitconfig"), "git #2");
+ write_file(&dir.dir.join("vimrc"), "vim #2");
+ let git_contents = read_file(&dir.dir.join(".gitconfig.coliru"));
+ let vim1_exists = dir.dir.join(".vimrc.coliru").exists();
+ let vim2_contents = read_file(&dir.dir.join("_vimrc.coliru"));
+ let log_contents = read_file(&dir.dir.join("log.txt"));
+ assert_eq!(git_contents, "git #1");
+ assert_eq!(vim1_exists, false);
+ assert_eq!(vim2_contents, "vim #1");
+ assert_eq!(log_contents, "script.bat called with arg1 windows \r\n");
+}
+
+#[test]
+#[cfg(target_family = "unix")]
fn test_run_failure() {
let (dir, mut cmd) = setup("test_run_failure");
cmd.args(["manifest.yml", "-t", "linux"]);
@@ -204,16 +264,44 @@ fn test_run_failure() {
// Assert files are correctly copied/linked/run
write_file(&dir.dir.join("gitconfig"), "git #2");
write_file(&dir.dir.join("vimrc"), "vim #2");
- let git_contents = read_to_string(&dir.dir.join(".gitconfig.coliru")).unwrap();
- let vim1_contents = read_to_string(&dir.dir.join(".vimrc.coliru")).unwrap();
- let vim2_exists = dir.dir.join("_vimrc").exists();
+ let git_contents = read_file(&dir.dir.join(".gitconfig.coliru"));
+ let vim1_contents = read_file(&dir.dir.join(".vimrc.coliru"));
+ let vim2_exists = dir.dir.join("_vimrc.coliru").exists();
assert_eq!(git_contents, "git #1");
assert_eq!(vim1_contents, "vim #2");
assert_eq!(vim2_exists, false);
}
#[test]
-#[cfg(target_os = "linux")]
+#[cfg(target_family = "windows")]
+fn test_run_failure() {
+ let (dir, mut cmd) = setup("test_run_failure");
+ cmd.args(["manifest-windows-test.yml", "-t", "windows"]);
+ manifest_1(&dir.dir);
+ write_file(&dir.dir.join("script.bat"), "@echo off\r\nexit 1");
+
+ let expected_stdout = "\
+[1/3] Copy gitconfig to .gitconfig.coliru
+[3/3] Link vimrc to _vimrc.coliru
+[3/3] Run script.bat arg1 windows
+";
+ let expected_stderr = " Error: Process exited with exit code: 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("gitconfig"), "git #2");
+ write_file(&dir.dir.join("vimrc"), "vim #2");
+ let git_contents = read_file(&dir.dir.join(".gitconfig.coliru"));
+ let vim1_exists = dir.dir.join(".vimrc.coliru").exists();
+ let vim2_contents = read_file(&dir.dir.join("_vimrc.coliru"));
+ assert_eq!(git_contents, "git #1");
+ assert_eq!(vim1_exists, false);
+ assert_eq!(vim2_contents, "vim #2");
+}
+
+#[test]
+#[cfg(target_family = "unix")]
fn test_missing_file() {
let (dir, mut cmd) = setup("test_missing_file");
cmd.args(["manifest.yml", "-t", "linux"]);
@@ -226,19 +314,47 @@ fn test_missing_file() {
[2/3] Run sh script.sh arg1 linux
script.sh called with arg1 linux
";
- let expected_stderr = " Error: No such file or directory (os error 2)\n";
+ 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("gitconfig"), "git #2");
- let git_contents = read_to_string(&dir.dir.join(".gitconfig.coliru")).unwrap();
- let log_contents = read_to_string(&dir.dir.join("log.txt")).unwrap();
+ let git_contents = read_file(&dir.dir.join(".gitconfig.coliru"));
+ let log_contents = read_file(&dir.dir.join("log.txt"));
assert_eq!(git_contents, "git #1");
assert_eq!(log_contents, "script.sh called with arg1 linux\n");
}
#[test]
+#[cfg(target_family = "windows")]
+fn test_missing_file() {
+ let (dir, mut cmd) = setup("test_missing_file");
+ cmd.args(["manifest-windows-test.yml", "-t", "windows"]);
+ manifest_1(&dir.dir);
+ remove_file(&dir.dir.join("vimrc")).unwrap();
+
+ let expected_stdout = "\
+[1/3] Copy gitconfig to .gitconfig.coliru
+[3/3] Link vimrc to _vimrc.coliru
+[3/3] Run script.bat arg1 windows
+script.bat called with arg1 windows\r
+";
+ let expected_stderr = " Error: The system cannot find the file specified. \
+ (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("gitconfig"), "git #2");
+ let git_contents = read_file(&dir.dir.join(".gitconfig.coliru"));
+ let log_contents = read_file(&dir.dir.join("log.txt"));
+ assert_eq!(git_contents, "git #1");
+ assert_eq!(log_contents, "script.bat called with arg1 windows \r\n");
+}
+
+#[test]
fn test_empty_manifest() {
let (dir, mut cmd) = setup("test_empty_manifest");
cmd.args(["manifest.yml"]);
@@ -250,7 +366,7 @@ fn test_empty_manifest() {
}
#[test]
-#[cfg(target_os = "linux")]
+#[cfg(target_family = "unix")]
fn test_missing_manifest() {
let (_dir, mut cmd) = setup("test_missing_manifest");
cmd.args(["missing.yml"]);
@@ -259,3 +375,15 @@ fn test_missing_manifest() {
assert_eq!(&stdout_to_string(&mut cmd), "");
assert_eq!(&stderr_to_string(&mut cmd), expected);
}
+
+#[test]
+#[cfg(target_family = "windows")]
+fn test_missing_manifest() {
+ let (_dir, mut cmd) = setup("test_missing_manifest");
+ cmd.args(["missing-windows-test.yml"]);
+
+ let expected = "Error: The system cannot find the file specified. \
+ (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
@@ -50,6 +50,11 @@ pub fn write_file(path: &Path, contents: &str) {
file.write_all(contents.as_bytes()).unwrap();
}
+/// Reads the contents of a file into a string.
+pub fn read_file(path: &Path) -> String {
+ fs::read_to_string(path).unwrap()
+}
+
/// Returns the stdout of a command as a String.
pub fn stdout_to_string(cmd: &mut Command) -> String {
String::from_utf8_lossy(&cmd.output().unwrap().stdout).into_owned()