commit 7019ca4df776e7f42142ececf8ed2f4161334b99
parent 04f5f7cecccf4b7ca5b4f2b2dc6a2f7e4ba8b6f2
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date: Sat, 6 Jul 2024 14:40:29 -0700
Improve error messages with anyhow
Diffstat:
10 files changed, 156 insertions(+), 87 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -52,6 +52,12 @@ dependencies = [
]
[[package]]
+name = "anyhow"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+
+[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -107,6 +113,7 @@ checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
name = "coliru"
version = "0.2.0"
dependencies = [
+ "anyhow",
"clap",
"serde",
"serde_yaml",
diff --git a/Cargo.toml b/Cargo.toml
@@ -4,8 +4,9 @@ version = "0.2.0"
edition = "2021"
[dependencies]
+anyhow = "1.0.86"
clap = { version = "4.5.7", features = ["derive"] }
-shellexpand = "3.0"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"
+shellexpand = "3.0"
tempfile = "3"
diff --git a/src/cli.rs b/src/cli.rs
@@ -49,7 +49,7 @@ pub fn run() {
match execute_manifest_file(&manifest_path, args.tag_rules, &args.host,
args.dry_run, args.copy) {
Err(why) => {
- eprintln!("Error: {}", why);
+ eprintln!("Error: {:#}", why);
std::process::exit(2);
},
Ok(minor_errors) => {
diff --git a/src/core.rs b/src/core.rs
@@ -1,7 +1,7 @@
//! Manifest execution functions
+use anyhow::{Context, Result};
use std::env::set_current_dir;
-use std::fmt::Display;
use std::path::Path;
use super::manifest::{CopyLinkOptions, RunOptions, parse_manifest_file};
use super::tags::tags_match;
@@ -28,9 +28,9 @@ macro_rules! check_dry_run {
/// Handles minor errors that occur during command execution and returns a bool
/// indicating whether an error occurred
-fn handle_error<T: Display>(result: Result<(), T>) -> bool {
+fn handle_error(result: Result<()>) -> bool {
if let Err(why) = result {
- eprintln!(" Error: {}", why);
+ eprintln!(" Error: {:#}", why);
return true;
}
false
@@ -41,11 +41,13 @@ fn handle_error<T: Display>(result: Result<(), T>) -> bool {
/// Returns an Err if a critical err occurs and returns a bool indicating
/// whether any minor errors occurred otherwise
pub fn execute_manifest_file(path: &Path, tag_rules: Vec<String>, host: &str,
- dry_run: bool, copy: bool) -> Result<bool, String> {
+ dry_run: bool, copy: bool) -> Result<bool> {
- let manifest = parse_manifest_file(path).map_err(|why| why.to_string())?;
- let temp_dir = tempdir().map_err(|why| why.to_string())?;
- set_current_dir(manifest.base_dir).map_err(|why| why.to_string())?;
+ let manifest = parse_manifest_file(path)
+ .context("Failed to parse manifest")?;
+ let temp_dir = tempdir().context("Failed to create temporary directory")?;
+ set_current_dir(manifest.base_dir)
+ .context("Failed to set working directory")?;
let mut errors = false;
@@ -97,12 +99,16 @@ fn execute_copies(copies: &[CopyLinkOptions], host: &str, staging_dir: &Path,
if host == "" {
errors |= handle_error(copy_file(©.src, &_dst));
} else {
- errors |= handle_error(stage_file(©.src, &_dst, staging_dir));
+ errors |= handle_error(stage_file(©.src, &_dst, staging_dir)
+ .with_context(|| {
+ format!("Failed to copy {} to staging directory", ©.src)
+ }));
}
}
if !dry_run {
- errors |= handle_error(send_staged_files(staging_dir, host));
+ errors |= handle_error(send_staged_files(staging_dir, host)
+ .context("Failed to transfer staged files"));
}
errors
@@ -139,7 +145,8 @@ fn execute_runs(runs: &[RunOptions], tag_rules: &[String], host: &str,
CopyLinkOptions { src: x.src.clone(), dst: x.src.clone() }
}).collect();
- execute_copies(&run_copies, host, staging_dir, dry_run, step_str);
+ errors |= execute_copies(&run_copies, host, staging_dir, dry_run,
+ step_str);
}
for run in runs {
diff --git a/src/local.rs b/src/local.rs
@@ -6,8 +6,8 @@
//! run_command("echo 'Hello world'");
//! ```
+use anyhow::{bail, Context, Result};
use shellexpand::tilde;
-use std::io;
use std::fs;
#[cfg(target_family = "unix")]
use std::os::unix::fs::symlink;
@@ -22,8 +22,15 @@ use std::process::Command;
/// ```
/// copy_file("foo", "~/foo");
/// ```
-pub fn copy_file(src: &str, dst: &str) -> io::Result<()> {
- if absolute(src)? == absolute(dst)? { return Ok(()); }
+pub fn copy_file(src: &str, dst: &str) -> Result<()> {
+ let src_abs = absolute(src).with_context(|| {
+ format!("Failed to make {} absolute", src)
+ })?;
+ let dst_abs = absolute(dst).with_context(|| {
+ format!("Failed to make {} absolute", dst)
+ })?;
+ if src_abs == dst_abs { return Ok(()); }
+
let _dst = prepare_path(dst)?;
fs::copy(src, _dst)?;
Ok(())
@@ -38,15 +45,29 @@ pub fn copy_file(src: &str, dst: &str) -> io::Result<()> {
/// link_file("bar", "~/bar");
/// ```
#[cfg(target_family = "unix")]
-pub fn link_file(src: &str, dst: &str) -> io::Result<()> {
- if absolute(src)? == absolute(dst)? { return Ok(()); }
+pub fn link_file(src: &str, dst: &str) -> Result<()> {
+ let src_abs = absolute(src).with_context(|| {
+ format!("Failed to make {} absolute", src)
+ })?;
+ let dst_abs = absolute(dst).with_context(|| {
+ format!("Failed to make {} absolute", dst)
+ })?;
+ if src_abs == dst_abs { return Ok(()); }
+
let _dst = prepare_path(dst)?;
- symlink(fs::canonicalize(src)?, _dst)?;
+ symlink(src_abs, _dst)?;
Ok(())
}
#[cfg(not(target_family = "unix"))]
-pub fn link_file(src: &str, dst: &str) -> io::Result<()> {
- if absolute(src)? == absolute(dst)? { return Ok(()); }
+pub fn link_file(src: &str, dst: &str) -> Result<()> {
+ let src_abs = absolute(src).with_context(|| {
+ format!("Failed to make {} absolute", src)
+ })?;
+ let dst_abs = absolute(dst).with_context(|| {
+ format!("Failed to make {} absolute", dst)
+ })?;
+ if src_abs == dst_abs { return Ok(()); }
+
let _dst = prepare_path(dst)?;
fs::hard_link(src, _dst)?;
Ok(())
@@ -58,14 +79,18 @@ pub fn link_file(src: &str, dst: &str) -> io::Result<()> {
/// ```
/// prepare_path("~/foo");
/// ```
-fn prepare_path(path: &str) -> io::Result<PathBuf> {
+fn prepare_path(path: &str) -> Result<PathBuf> {
let _dst: PathBuf = (&tilde(path).to_mut()).into();
if let Some(_path) = _dst.parent() {
- fs::create_dir_all(_path)?;
+ fs::create_dir_all(_path).with_context(|| {
+ format!("Failed to create parent directories of {}", path)
+ })?;
}
if fs::symlink_metadata(&_dst).is_ok() {
// Check for existing files, including broken symlinks
- fs::remove_file(&_dst)?;
+ fs::remove_file(&_dst).with_context(|| {
+ format!("Failed to remove existing file at {}", path)
+ })?;
}
Ok(_dst)
}
@@ -75,25 +100,24 @@ fn prepare_path(path: &str) -> io::Result<PathBuf> {
/// ```
/// run_command("echo 'Hello world'");
/// ```
-pub fn run_command(command: &str) -> Result<(), String>
+pub fn run_command(command: &str) -> Result<()>
{
- let status;
+ let mut cmd;
if cfg!(target_family = "unix") {
- status = Command::new("sh")
- .args(["-c", command])
- .status()
- .map_err(|why| why.to_string())?;
+ cmd = Command::new("sh");
+ cmd.args(["-c", command]);
} else {
- status = Command::new("cmd.exe")
- .args(["/C", command])
- .status()
- .map_err(|why| why.to_string())?;
+ cmd = Command::new("cmd.exe");
+ cmd.args(["/C", command]);
}
- if status.success() {
- Ok(())
- } else {
- Err(format!("Process exited with {status}"))
+
+ let status = cmd.status().with_context(|| {
+ format!("Failed to execute {:?}", cmd)
+ })?;
+ if !status.success() {
+ bail!("Process terminated unsuccessfully: {}", status);
}
+ Ok(())
}
#[cfg(test)]
@@ -329,7 +353,8 @@ mod tests {
let result = run_command(&format!("sh {}", src.to_str().unwrap()));
assert_eq!(result.is_ok(), false);
- assert_eq!(result.unwrap_err(), "Process exited with exit status: 2");
+ assert_eq!(result.unwrap_err().to_string(),
+ "Process terminated unsuccessfully: exit status: 2");
}
#[test]
@@ -343,7 +368,8 @@ mod tests {
let result = run_command(src.to_str().unwrap());
assert_eq!(result.is_ok(), false);
- assert_eq!(result.unwrap_err(), "Process exited with exit code: 1");
+ assert_eq!(result.unwrap_err().to_string(),
+ "Process terminated unsuccessfully: exit code: 1");
}
#[test]
diff --git a/src/manifest.rs b/src/manifest.rs
@@ -1,5 +1,6 @@
//! Coliru manifest parsing
+use anyhow::Result;
use serde::Deserialize;
use serde_yaml;
use std::fs::read_to_string;
@@ -73,10 +74,9 @@ pub struct Manifest {
/// ```
/// let manifest = parse_manifest_file(Path::new("manifest.yml"))?;
/// ```
-pub fn parse_manifest_file(path: &Path) -> Result<Manifest, String> {
- let raw_str = read_to_string(path).map_err(|why| why.to_string())?;
- let raw_manifest = serde_yaml::from_str::<RawManifest>(&raw_str)
- .map_err(|why| why.to_string())?;
+pub fn parse_manifest_file(path: &Path) -> Result<Manifest> {
+ let raw_str = read_to_string(path)?;
+ let raw_manifest = serde_yaml::from_str::<RawManifest>(&raw_str)?;
let base_dir = match path.parent() {
None => &Path::new("."),
Some(p) => if p == Path::new("") { &Path::new(".") } else { p },
@@ -98,7 +98,8 @@ mod tests {
let manifest_path = Path::new("examples/test/missing.yml");
let expected = "No such file or directory (os error 2)";
let actual = parse_manifest_file(manifest_path);
- assert_eq!(actual, Err(String::from(expected)));
+ assert_eq!(actual.is_ok(), false);
+ assert_eq!(actual.unwrap_err().to_string(), expected);
}
#[test]
@@ -107,7 +108,8 @@ mod tests {
let manifest_path = Path::new("examples/test/missing.yml");
let exp = "The system cannot find the file specified. (os error 2)";
let actual = parse_manifest_file(manifest_path);
- assert_eq!(actual, Err(String::from(exp)));
+ assert_eq!(actual.is_ok(), false);
+ assert_eq!(actual.unwrap_err().to_string(), exp);
}
#[test]
@@ -115,7 +117,8 @@ mod tests {
let manifest_path = Path::new("examples/test/invalid.yml");
let exp = "steps[0].copy[0]: missing field `src` at line 5 column 7";
let actual = parse_manifest_file(manifest_path);
- assert_eq!(actual, Err(String::from(exp)));
+ assert_eq!(actual.is_ok(), false);
+ assert_eq!(actual.unwrap_err().to_string(), exp);
}
#[test]
@@ -180,6 +183,7 @@ mod tests {
base_dir: PathBuf::from("examples/test"),
};
let actual = parse_manifest_file(manifest_path);
- assert_eq!(actual, Ok(expected));
+ assert_eq!(actual.is_ok(), true);
+ assert_eq!(actual.unwrap(), expected);
}
}
diff --git a/src/ssh.rs b/src/ssh.rs
@@ -11,6 +11,7 @@
//! send_command("bash ~/foo.sh", host);
//! ```
+use anyhow::{bail, anyhow, Context, Result};
use shellexpand::tilde_with_context;
use std::fs::{read_dir, remove_dir_all};
use std::path::{MAIN_SEPARATOR_STR, Path, PathBuf};
@@ -45,9 +46,7 @@ pub fn resolve_path(src: &str, dir: &str) -> String {
/// stage_file("bar", "/bar", staging_dir);
/// stage_file("baz", "baz", staging_dir);
/// ```
-pub fn stage_file(src: &str, dst: &str, staging_dir: &Path) -> Result<(),
- String> {
-
+pub fn stage_file(src: &str, dst: &str, staging_dir: &Path) -> Result<()> {
// Staging directories are used to copy multiple files at once while
// automatically creating missing directories on the remote machine. The
// example code above produces the following staging directory layout:
@@ -79,14 +78,16 @@ pub fn stage_file(src: &str, dst: &str, staging_dir: &Path) -> Result<(),
// path separator. (Duplicate slashes are ignored on Unix).
let root = PathBuf::from(match _dst.iter().next() {
Some(x) => Ok(x),
- None => Err(String::from("Destination path does not have root")),
+ None => Err(anyhow!("Failed to get root of {}", _dst.display())),
}?).join(MAIN_SEPARATOR_STR);
- _dst = root_dir.join(_dst.strip_prefix(root)
- .map_err(|why| why.to_string())?);
+
+ let dst_without_root = _dst.strip_prefix(root).with_context(|| {
+ format!("Failed to strip root from {}", _dst.display())
+ })?;
+ _dst = root_dir.join(dst_without_root);
}
copy_file(src, _dst.to_string_lossy().to_mut())
- .map_err(|why| why.to_string())
}
/// Transfers the files in an SCP staging directory to a remote machine
@@ -98,16 +99,22 @@ pub fn stage_file(src: &str, dst: &str, staging_dir: &Path) -> Result<(),
/// ```
/// send_staged_files(Path::new("/tmp/staging"), "user@hostname");
/// ```
-pub fn send_staged_files(staging_dir: &Path, host: &str) -> Result<(), String> {
+pub fn send_staged_files(staging_dir: &Path, host: &str) -> Result<()> {
let home_dir = staging_dir.join("home");
if home_dir.exists() {
send_dir(home_dir.to_string_lossy().to_mut(), "~", host)?;
- remove_dir_all(home_dir).map_err(|why| why.to_string())?;
+ remove_dir_all(&home_dir).with_context(|| {
+ format!("Failed to remove staging dir {} after use",
+ &home_dir.display())
+ })?;
}
let root_dir = staging_dir.join("root");
if root_dir.exists() {
send_dir(root_dir.to_string_lossy().to_mut(), "/", host)?;
- remove_dir_all(root_dir).map_err(|why| why.to_string())?;
+ remove_dir_all(&root_dir).with_context(|| {
+ format!("Failed to remove staging dir {} after use",
+ &root_dir.display())
+ })?;
}
Ok(())
}
@@ -120,12 +127,17 @@ pub fn send_staged_files(staging_dir: &Path, host: &str) -> Result<(), String> {
/// ```
/// send_dir("new_home", "~/", "user@hostname");
/// ```
-fn send_dir(src: &str, dst: &str, host: &str) -> Result<(), String> {
+fn send_dir(src: &str, dst: &str, host: &str) -> Result<()> {
// To avoid the source directory being copied as a subdirectory of the
// destination directory, we must send the contents of the directory
// item by item.
- for item in read_dir(&src).map_err(|why| why.to_string())? {
- let _src = item.map_err(|why| why.to_string())?.path();
+ let items = read_dir(&src).with_context(|| {
+ format!("Failed to list contents of {}", src)
+ })?;
+ for item in items {
+ let _src = item.with_context(|| {
+ format!("Failed to list contents of {}", src)
+ })?.path();
let mut cmd = Command::new("scp");
if host == "test@localhost" {
@@ -134,9 +146,11 @@ fn send_dir(src: &str, dst: &str, host: &str) -> Result<(), String> {
}
cmd.args(["-r", &_src.to_string_lossy(), &format!("{host}:{dst}")]);
- let status = cmd.status().map_err(|why| why.to_string())?;
+ let status = cmd.status().with_context(|| {
+ format!("Failed to execute {:?}", cmd)
+ })?;
if !status.success() {
- return Err(format!("SCP exited with {status}"));
+ bail!("SCP terminated unsuccessfully: {}", status);
}
}
Ok(())
@@ -149,7 +163,7 @@ fn send_dir(src: &str, dst: &str, host: &str) -> Result<(), String> {
/// ```
/// send_command("echo 'Hello World'");
/// ```
-pub fn send_command(command: &str, host: &str) -> Result<(), String> {
+pub fn send_command(command: &str, host: &str) -> Result<()> {
let mut cmd = Command::new("ssh");
if host == "test@localhost" {
// SSH options and port for test server hard coded for now
@@ -157,9 +171,11 @@ pub fn send_command(command: &str, host: &str) -> Result<(), String> {
}
cmd.args([host, command]);
- let status = cmd.status().map_err(|why| why.to_string())?;
+ let status = cmd.status().with_context(|| {
+ format!("Failed to execute {:?}", cmd)
+ })?;
if !status.success() {
- return Err(format!("SSH exited with {status}"));
+ bail!("SSH terminated unsuccessfully: {}", status);
}
Ok(())
}
@@ -215,7 +231,7 @@ mod tests {
let result = stage_file(src.to_str().unwrap(), dst, staging);
- assert_eq!(result, Ok(()));
+ assert_eq!(result.is_ok(), true);
assert_eq!(dst_real.exists(), true);
assert_eq!(read_file(&dst_real), "contents of foo");
}
@@ -233,7 +249,7 @@ mod tests {
let result = stage_file(src.to_str().unwrap(), dst, staging);
- assert_eq!(result, Ok(()));
+ assert_eq!(result.is_ok(), true);
assert_eq!(dst_real.exists(), true);
assert_eq!(read_file(&dst_real), "contents of foo");
}
@@ -250,7 +266,7 @@ mod tests {
let result = stage_file(src.to_str().unwrap(), dst, staging);
- assert_eq!(result, Ok(()));
+ assert_eq!(result.is_ok(), true);
assert_eq!(dst_real.exists(), true);
assert_eq!(read_file(&dst_real), "contents of foo");
}
@@ -262,7 +278,7 @@ mod tests {
let result = send_staged_files(&tmp.local, SSH_HOST);
- assert_eq!(result, Ok(()));
+ assert_eq!(result.is_ok(), true);
}
#[test]
@@ -281,7 +297,7 @@ mod tests {
let dst_foo = tmp.ssh.join("foo");
let dst_bar = tmp.ssh.join("dir").join("bar");
- assert_eq!(result, Ok(()));
+ assert_eq!(result.is_ok(), true);
assert_eq!(dst_foo.exists(), true);
assert_eq!(read_file(&dst_foo), "contents of foo");
assert_eq!(dst_bar.exists(), true);
@@ -307,7 +323,7 @@ mod tests {
let dst_foo = tmp.ssh.join("foo");
let dst_bar = tmp.ssh.join("dir").join("bar");
- assert_eq!(result, Ok(()));
+ assert_eq!(result.is_ok(), true);
assert_eq!(dst_foo.exists(), true);
assert_eq!(read_file(&dst_foo), "contents of foo");
assert_eq!(dst_bar.exists(), true);
@@ -330,7 +346,7 @@ mod tests {
let result = send_dir(tmp.local.to_str().unwrap(), dst, SSH_HOST);
- assert_eq!(result, Ok(()));
+ assert_eq!(result.is_ok(), true);
assert_eq!(dst_foo.exists(), true);
assert_eq!(read_file(&dst_foo), "contents of foo");
assert_eq!(dst_bar.exists(), true);
@@ -354,7 +370,7 @@ mod tests {
let result = send_dir(tmp.local.to_str().unwrap(), dst, SSH_HOST);
- assert_eq!(result, Ok(()));
+ assert_eq!(result.is_ok(), true);
assert_eq!(dst_foo.exists(), true);
assert_eq!(read_file(&dst_foo), "contents of foo");
assert_eq!(dst_bar.exists(), true);
@@ -381,7 +397,7 @@ mod tests {
let result = send_dir(tmp.local.to_str().unwrap(), dst, SSH_HOST);
- assert_eq!(result, Ok(()));
+ assert_eq!(result.is_ok(), true);
assert_eq!(dst_foo.exists(), true);
assert_eq!(read_file(&dst_foo), "old contents of foo");
assert_eq!(dst_bar.exists(), true);
@@ -401,7 +417,7 @@ mod tests {
let result = send_command(&cmd, SSH_HOST);
- assert_eq!(result, Ok(()));
+ assert_eq!(result.is_ok(), true);
assert_eq!(dst_real.exists(), true);
assert_eq!(read_file(&dst_real), "contents of foo\n");
}
diff --git a/tests/basic.rs b/tests/basic.rs
@@ -64,7 +64,7 @@ fn test_basic_empty_manifest() {
cmd.args(["manifest.yml"]);
write_file(&dirs.local.join("manifest.yml"), "");
- let expected = "Error: missing field `steps`\n";
+ let expected = "Error: Failed to parse manifest: missing field `steps`\n";
let (stdout, stderr, exitcode) = run_command(&mut cmd);
assert_eq!(&stderr, expected);
assert_eq!(&stdout, "");
@@ -77,7 +77,8 @@ fn test_basic_missing_manifest() {
let (_dirs, mut cmd) = setup_e2e_local("test_basic_missing_manifest");
cmd.args(["missing.yml"]);
- let expected = "Error: No such file or directory (os error 2)\n";
+ let expected = "Error: Failed to parse manifest: No such file or directory \
+ (os error 2)\n";
let (stdout, stderr, exitcode) = run_command(&mut cmd);
assert_eq!(&stderr, expected);
assert_eq!(&stdout, "");
@@ -90,8 +91,8 @@ fn test_basic_missing_manifest() {
let (_dirs, mut cmd) = setup_e2e_local("test_basic_missing_manifest");
cmd.args(["missing.yml"]);
- let expected = "Error: The system cannot find the file specified. \
- (os error 2)\n";
+ let expected = "Error: Failed to parse manifest: The system cannot find \
+ the file specified. (os error 2)\n";
let (stdout, stderr, exitcode) = run_command(&mut cmd);
assert_eq!(&stderr, expected);
assert_eq!(&stdout, "");
diff --git a/tests/local.rs b/tests/local.rs
@@ -275,7 +275,8 @@ fn test_local_run_failure() {
[2/3] Link vimrc to ~/.vimrc
[2/3] Run sh script.sh arg1 linux
";
- let expected_stderr = " Error: Process exited with exit status: 1\n";
+ let expected_stderr = " Error: Process terminated unsuccessfully: \
+ exit status: 1\n";
let (stdout, stderr, exitcode) = run_command(&mut cmd);
assert_eq!(&stderr, expected_stderr);
assert_eq!(&stdout, expected_stdout);
@@ -307,7 +308,8 @@ fn test_local_run_failure() {
[3/3] Link vimrc to _vimrc
[3/3] Run script.bat arg1 windows
";
- let expected_stderr = " Error: Process exited with exit code: 1\n";
+ let expected_stderr = " Error: Process terminated unsuccessfully: \
+ exit code: 1\n";
let (stdout, stderr, exitcode) = run_command(&mut cmd);
assert_eq!(&stderr, expected_stderr);
assert_eq!(&stdout, expected_stdout);
@@ -331,7 +333,7 @@ fn test_local_run_failure() {
fn test_local_missing_file() {
let (dirs, mut cmd) = setup_e2e_local("test_local_missing_file");
cmd.args(["manifest.yml", "-t", "linux"]);
- remove_file(&dirs.local.join("vimrc")).unwrap();
+ remove_file(&dirs.local.join("gitconfig")).unwrap();
let expected_stdout = "\
[1/3] Copy gitconfig to ~/.gitconfig
@@ -348,12 +350,14 @@ script.sh called with arg1 linux
// Assert files are correctly copied/linked/run
write_file(&dirs.local.join("bashrc"), "bash #2\n");
- write_file(&dirs.local.join("gitconfig"), "git #2\n");
+ write_file(&dirs.local.join("vimrc"), "vim #2\n");
let bash_contents = read_file(&dirs.home.join(".bashrc"));
- let git_contents = read_file(&dirs.home.join(".gitconfig"));
+ let vim1_contents = read_file(&dirs.home.join(".vimrc"));
+ let vim2_exists = dirs.home.join("_vimrc").exists();
let log_contents = read_file(&dirs.local.join("log.txt"));
assert_eq!(bash_contents, "bash #2\n");
- assert_eq!(git_contents, "git #1\n");
+ assert_eq!(vim1_contents, "vim #2\n");
+ assert_eq!(vim2_exists, false);
assert_eq!(log_contents, "script.sh called with arg1 linux\n");
}
diff --git a/tests/ssh.rs b/tests/ssh.rs
@@ -1,3 +1,5 @@
+#![allow(unused_imports)]
+
//! End to end tests that test installation behavior on a remote machine via SSH
mod test_utils;
@@ -176,7 +178,7 @@ fn test_ssh_run_failure() {
[2/3] Copy test_ssh_run_failure/script.sh to {SSH_HOST}:~/.coliru/test_ssh_run_failure/script.sh
[2/3] Run sh test_ssh_run_failure/script.sh arg1 linux on {SSH_HOST}
");
- let expected_stderr = " Error: SSH exited with exit status: 1\n";
+ let expected_stderr = " Error: SSH terminated unsuccessfully: exit status: 1\n";
let (stdout, stderr, exitcode) = run_command(&mut cmd);
assert_eq!(&stderr, expected_stderr);
assert_eq!(&stdout, &expected_stdout);
@@ -208,7 +210,8 @@ fn test_ssh_missing_file() {
[2/3] Run sh test_ssh_missing_file/script.sh arg1 linux on {SSH_HOST}
script.sh called with arg1 linux
");
- let expected_stderr = " Error: No such file or directory (os error 2)\n";
+ let expected_stderr = " Error: Failed to copy vimrc to staging directory: \
+ No such file or directory (os error 2)\n";
let (stdout, stderr, exitcode) = run_command(&mut cmd);
assert_eq!(&stderr, expected_stderr);
assert_eq!(&stdout, &expected_stdout);