diff --git a/.travis.yml b/.travis.yml index 05ac307..cc6c943 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,14 +11,14 @@ matrix: env: CLIPPY_VERS="0.0.165" before_script: | [[ "$(cargo +nightly-2017-10-09 clippy --version)" != "$CLIPPY_VERS" ]] && \ - cargo +nightly-2017-10-09 install clippy --vers "$CLIPPY_VERS" --force + cargo +nightly-2017-10-09 install clippy --vers "$CLIPPY_VERS" --force || true script: | cargo +nightly-2017-10-09 clippy -- -D warnings - rust: nightly-2017-10-09 env: RUSTFMT_VERS="0.2.8" before_script: | [[ "$(cargo +nightly-2017-10-09 fmt -- --version)" != "$RUSTFMT_VERS"* ]] && \ - cargo +nightly-2017-10-09 install rustfmt-nightly --vers "$RUSTFMT_VERS" --force + cargo +nightly-2017-10-09 install rustfmt-nightly --vers "$RUSTFMT_VERS" --force || true script: | cargo +nightly-2017-10-09 fmt --all -- --write-mode=diff before_script: diff --git a/Cargo.toml b/Cargo.toml index e6abc58..619722f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ colored = "1.5" difference = "1.0" error-chain = "0.11" serde_json = "1.0" +environment = "0.1" [build-dependencies] skeptic = "0.13" diff --git a/README.md b/README.md index 14642fc..1794e45 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ like this: if there are special characters, which the macro doesn't accept, e.g. `assert_cmd!(cat "foo.txt")`. +Assert Cli use [Environment][Environment] underneath to deal with environment variables. + More detailed information is available in the [documentation]. :-) ## License @@ -95,3 +97,4 @@ license, shall be dual licensed as above, without any additional terms or conditions. [Documentation]: https://docs.rs/assert_cli +[Environment]: https://github.com/Freyskeyd/environment diff --git a/src/assert.rs b/src/assert.rs index e50e9bb..74e45c0 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -1,3 +1,4 @@ +use environment::Environment; use errors::*; use output::{OutputAssertion, OutputKind}; use std::default; @@ -10,6 +11,7 @@ use std::vec::Vec; #[derive(Debug)] pub struct Assert { cmd: Vec, + env: Environment, current_dir: Option, expect_success: Option, expect_exit_code: Option, @@ -27,6 +29,7 @@ impl default::Default for Assert { .into_iter() .map(String::from) .collect(), + env: Environment::inherit(), current_dir: None, expect_success: Some(true), expect_exit_code: None, @@ -87,6 +90,7 @@ impl Assert { /// .with_args(&["42"]) /// .stdout().contains("42") /// .unwrap(); + /// /// ``` pub fn with_args(mut self, args: &[&str]) -> Self { self.cmd.extend(args.into_iter().cloned().map(String::from)); @@ -128,6 +132,41 @@ impl Assert { self } + /// Sets environments variables for the command. + /// + /// # Examples + /// + /// ```rust + /// extern crate assert_cli; + /// + /// assert_cli::Assert::command(&["printenv"]) + /// .with_env(&[("TEST_ENV", "OK")]) + /// .stdout().contains("TEST_ENV=OK") + /// .execute() + /// .unwrap(); + /// + /// let env = assert_cli::Environment::empty() + /// .insert("FOO", "BAR"); + /// + /// assert_cli::Assert::command(&["printenv"]) + /// .with_env(&env) + /// .stdout().is("FOO=BAR") + /// .execute() + /// .unwrap(); + /// + /// ::std::env::set_var("BAZ", "BAR"); + /// + /// assert_cli::Assert::command(&["printenv"]) + /// .stdout().contains("BAZ=BAR") + /// .execute() + /// .unwrap(); + /// ``` + pub fn with_env>(mut self, env: E) -> Self { + self.env = env.into(); + + self + } + /// Small helper to make chains more readable. /// /// # Examples @@ -253,13 +292,17 @@ impl Assert { /// ``` pub fn execute(self) -> Result<()> { let cmd = &self.cmd[0]; + let args: Vec<_> = self.cmd.iter().skip(1).collect(); let mut command = Command::new(cmd); let command = command .stdin(Stdio::piped()) .stdout(Stdio::piped()) - .stderr(Stdio::piped()); - let command = command.args(&args); + .stderr(Stdio::piped()) + .env_clear() + .envs(self.env.clone().compile()) + .args(&args); + let command = match self.current_dir { Some(ref dir) => command.current_dir(dir), None => command, @@ -426,3 +469,168 @@ impl OutputAssertionBuilder { self.not().is(output) } } + +#[cfg(test)] +mod test { + use super::*; + use std::ffi::OsString; + + fn command() -> Assert { + Assert::command(&["printenv"]) + } + + #[test] + fn take_ownership() { + let x = Environment::inherit(); + + command().with_env(x.clone()).with_env(&x).with_env(x); + } + + #[test] + fn in_place_mod() { + let y = Environment::empty(); + + let y = y.insert("key", "value"); + + assert_eq!( + y.compile(), + vec![(OsString::from("key"), OsString::from("value"))] + ); + } + + #[test] + fn in_place_mod2() { + let x = Environment::inherit(); + + command() + .with_env(&x.insert("key", "value").insert("key", "vv")) + .stdout() + .contains("key=vv") + .execute() + .unwrap(); + // Granted, `insert` moved `x`, so we can no longer reference it, even + // though only a reference was passed to `with_env` + } + + #[test] + fn in_place_mod3() { + // In-place modification while allowing later accesses to the `Environment` + let y = Environment::empty(); + + assert_eq!( + y.clone().insert("key", "value").compile(), + vec![(OsString::from("key"), OsString::from("value"))] + ); + + command() + .with_env(y) + .stdout() + .not() + .contains("key=value") + .execute() + .unwrap(); + } + + #[test] + fn empty_env() { + // In-place modification while allowing later accesses to the `Environment` + let y = Environment::empty(); + + assert!(command().with_env(y).stdout().is("").execute().is_ok()); + } + #[test] + fn take_vec() { + let v = vec![("bar".to_string(), "baz".to_string())]; + + command() + .with_env(&vec![("bar", "baz")]) + .stdout() + .contains("bar=baz") + .execute() + .unwrap(); + + command() + .with_env(&v) + .stdout() + .contains("bar=baz") + .execute() + .unwrap(); + + command() + .with_env(&vec![("bar", "baz")]) + .stdout() + .isnt("") + .execute() + .unwrap(); + } + + #[test] + fn take_slice_of_strs() { + command() + .with_env(&[("bar", "BAZ")]) + .stdout() + .contains("bar=BAZ") + .execute() + .unwrap(); + + command() + .with_env(&[("bar", "BAZ")][..]) + .stdout() + .contains("bar=BAZ") + .execute() + .unwrap(); + + command() + .with_env([("bar", "BAZ")].as_ref()) + .stdout() + .contains("bar=BAZ") + .execute() + .unwrap(); + } + + #[test] + fn take_slice_of_strings() { + // same deal as above + + command() + .with_env(&[("bar".to_string(), "BAZ".to_string())]) + .stdout() + .contains("bar=BAZ") + .execute() + .unwrap(); + + command() + .with_env(&[("bar".to_string(), "BAZ".to_string())][..]) + .stdout() + .contains("bar=BAZ") + .execute() + .unwrap(); + } + + #[test] + fn take_slice() { + command() + .with_env(&[("hey", "ho")]) + .stdout() + .contains("hey=ho") + .execute() + .unwrap(); + + command() + .with_env(&[("hey", "ho".to_string())]) + .stdout() + .contains("hey=ho") + .execute() + .unwrap(); + } + + #[test] + fn take_string_i32() { + command() + .with_env(&[("bar", 3 as i32)]) + .stdout() + .contains("bar=3") + .execute() + .unwrap(); + } +} diff --git a/src/lib.rs b/src/lib.rs index c902497..8d56f1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,6 +118,7 @@ #![deny(missing_docs)] extern crate difference; +extern crate environment; #[macro_use] extern crate error_chain; extern crate serde_json; @@ -135,3 +136,7 @@ mod diff; mod assert; pub use assert::Assert; pub use assert::OutputAssertionBuilder; +/// Environment is a re-export of the Environment crate +/// +/// It allow you to define/override environment variables for one or more assertions. +pub use environment::Environment;