From aac5ec0820aaac75bb85a29c6a608784e79eca5e Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 12 Oct 2017 18:40:46 -0600 Subject: [PATCH 1/4] fix(error): Report context for spawmn failures --- src/assert.rs | 7 +++++-- src/errors.rs | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/assert.rs b/src/assert.rs index 74e45c0..6590c1c 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -1,4 +1,5 @@ use environment::Environment; +use error_chain::ChainedError; use errors::*; use output::{OutputAssertion, OutputKind}; use std::default; @@ -308,7 +309,9 @@ impl Assert { None => command, }; - let mut spawned = command.spawn()?; + let mut spawned = command + .spawn() + .chain_err(|| ErrorKind::SpawnFailed(self.cmd.clone()))?; if let Some(ref contents) = self.stdin_contents { spawned @@ -365,7 +368,7 @@ impl Assert { /// ``` pub fn unwrap(self) { if let Err(err) = self.execute() { - panic!("{}", err); + panic!("{}", err.display_chain()); } } } diff --git a/src/errors.rs b/src/errors.rs index af9ab5e..ab0f7b1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -6,6 +6,14 @@ error_chain! { Fmt(::std::fmt::Error); } errors { + SpawnFailed(cmd: Vec) { + description("Spawn failed") + display( + "{}: (command `{}` failed to run)", + ERROR_PREFIX, + cmd.join(" "), + ) + } StatusMismatch(cmd: Vec, expected: bool, out: String, err: String) { description("Wrong status") display( From aa89b08602bbe8cc75030a87b87c341b925663fb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 12 Oct 2017 18:41:33 -0600 Subject: [PATCH 2/4] tests: Validate main_binary / cargo_binary `clap` couldn't be used for command line arguments. I didn't want to add it as a dependency for clients, which meant it had to be optional. The problem is that `main_binary` and `cargo_binary` run with optional features enabled. Note: you cannot use `main_binary` or `cargo_binary` - With `Environment::empty` because the executable can't be found - In skeptic tests (like the README) because the working dir is changed --- Cargo.toml | 3 +++ src/bin/assert_fixture.rs | 31 +++++++++++++++++++++++++++++++ tests/cargo.rs | 19 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 src/bin/assert_fixture.rs create mode 100644 tests/cargo.rs diff --git a/Cargo.toml b/Cargo.toml index 39b38b9..db2f366 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,9 @@ categories = ["development-tools::testing"] keywords = ["cli", "testing", "assert"] build = "build.rs" +[[bin]] +name = "assert_fixture" + [dependencies] colored = "1.5" difference = "1.0" diff --git a/src/bin/assert_fixture.rs b/src/bin/assert_fixture.rs new file mode 100644 index 0000000..f22ba0c --- /dev/null +++ b/src/bin/assert_fixture.rs @@ -0,0 +1,31 @@ +#[macro_use] +extern crate error_chain; + +use std::env; +use std::process; + +error_chain! { + foreign_links { + Env(env::VarError); + ParseInt(std::num::ParseIntError); + } +} + +fn run() -> Result<()> { + if let Ok(text) = env::var("stdout") { + println!("{}", text); + } + if let Ok(text) = env::var("stderr") { + eprintln!("{}", text); + } + + let code = env::var("exit") + .ok() + .map(|v| v.parse::()) + .map_or(Ok(None), |r| r.map(Some)) + .chain_err(|| "Invalid exit code")? + .unwrap_or(0); + process::exit(code); +} + +quick_main!(run); diff --git a/tests/cargo.rs b/tests/cargo.rs new file mode 100644 index 0000000..6c211ce --- /dev/null +++ b/tests/cargo.rs @@ -0,0 +1,19 @@ +extern crate assert_cli; + +#[test] +fn main_binary() { + assert_cli::Assert::main_binary() + .with_env(assert_cli::Environment::inherit().insert("stdout", "42")) + .stdout() + .contains("42") + .unwrap(); +} + +#[test] +fn cargo_binary() { + assert_cli::Assert::cargo_binary("assert_fixture") + .with_env(assert_cli::Environment::inherit().insert("stdout", "42")) + .stdout() + .contains("42") + .unwrap(); +} From fae7c90940bc1e7e60e50370ce0115866adf52fe Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 12 Oct 2017 18:45:13 -0600 Subject: [PATCH 3/4] fix: Remove unrelated output in main_binary We use cargo run behind the scenes to run the main binary but that should only be an implementation detail. Fixes #45 --- src/assert.rs | 4 ++-- tests/cargo.rs | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/assert.rs b/src/assert.rs index 6590c1c..0aab75c 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -26,7 +26,7 @@ impl default::Default for Assert { /// Defaults to asserting _successful_ execution. fn default() -> Self { Assert { - cmd: vec!["cargo", "run", "--"] + cmd: vec!["cargo", "run", "--quiet", "--"] .into_iter() .map(String::from) .collect(), @@ -53,7 +53,7 @@ impl Assert { /// Defaults to asserting _successful_ execution. pub fn cargo_binary(name: &str) -> Self { Assert { - cmd: vec!["cargo", "run", "--bin", name, "--"] + cmd: vec!["cargo", "run", "--quiet", "--bin", name, "--"] .into_iter() .map(String::from) .collect(), diff --git a/tests/cargo.rs b/tests/cargo.rs index 6c211ce..f476ee1 100644 --- a/tests/cargo.rs +++ b/tests/cargo.rs @@ -5,7 +5,9 @@ fn main_binary() { assert_cli::Assert::main_binary() .with_env(assert_cli::Environment::inherit().insert("stdout", "42")) .stdout() - .contains("42") + .is("42") + .stderr() + .is("") .unwrap(); } @@ -14,6 +16,8 @@ fn cargo_binary() { assert_cli::Assert::cargo_binary("assert_fixture") .with_env(assert_cli::Environment::inherit().insert("stdout", "42")) .stdout() - .contains("42") + .is("42") + .stderr() + .is("") .unwrap(); } From 2f0e94335a31c1466ad804306ec1ca2eabf0b3f9 Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Fri, 13 Oct 2017 10:40:11 +0200 Subject: [PATCH 4/4] Make cargo bin helpers work with empty env Fixes #51 --- src/assert.rs | 34 ++++++++++++++++++++++++++-------- tests/cargo.rs | 12 ++++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/assert.rs b/src/assert.rs index 0aab75c..cb545cc 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -8,6 +8,20 @@ use std::path::PathBuf; use std::process::{Command, Stdio}; use std::vec::Vec; +fn find_cargo() -> String { + let which_cargo = Command::new("which").arg("cargo") + .output().expect("Cannot exectute `which` to find `cargo`."); + + if !which_cargo.status.success() { + panic!("Could not find `cargo` command"); + } + + String::from_utf8(which_cargo.stdout) + .expect("Path to `cargo` is not UTF-8. This is currently unsupported by assert_cli.") + .trim() + .to_string() +} + /// Assertions for a specific command. #[derive(Debug)] pub struct Assert { @@ -25,11 +39,13 @@ impl default::Default for Assert { /// /// Defaults to asserting _successful_ execution. fn default() -> Self { + let cargo_path = find_cargo(); + let args = vec!["run", "--quiet", "--"] + .into_iter() + .map(String::from); + Assert { - cmd: vec!["cargo", "run", "--quiet", "--"] - .into_iter() - .map(String::from) - .collect(), + cmd: vec![cargo_path].into_iter().chain(args).collect(), env: Environment::inherit(), current_dir: None, expect_success: Some(true), @@ -52,11 +68,13 @@ impl Assert { /// /// Defaults to asserting _successful_ execution. pub fn cargo_binary(name: &str) -> Self { + let cargo_path = find_cargo(); + let args = vec!["run", "--quiet", "--bin", name, "--"] + .into_iter() + .map(String::from); + Assert { - cmd: vec!["cargo", "run", "--quiet", "--bin", name, "--"] - .into_iter() - .map(String::from) - .collect(), + cmd: vec![cargo_path].into_iter().chain(args).collect(), ..Self::default() } } diff --git a/tests/cargo.rs b/tests/cargo.rs index f476ee1..405532d 100644 --- a/tests/cargo.rs +++ b/tests/cargo.rs @@ -21,3 +21,15 @@ fn cargo_binary() { .is("") .unwrap(); } + + +#[test] +fn works_with_empty_env() { + assert_cli::Assert::main_binary() + .with_env(assert_cli::Environment::empty()) + .unwrap(); + + assert_cli::Assert::cargo_binary("assert_fixture") + .with_env(assert_cli::Environment::empty()) + .unwrap(); +}