diff --git a/Cargo.toml b/Cargo.toml index 2040e02..b13d3dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,9 @@ keywords = ["cli", "test", "assert", "command", "duct"] name = "bin_fixture" [dependencies] -predicates = "0.5.1" +predicates = "0.9.0" +predicates-core = "0.9.0" +predicates-tree = "0.9.0" escargot = "0.2" serde = { version = "1.0", features = ["derive"] } diff --git a/src/assert.rs b/src/assert.rs index 83258c2..3e8d8fb 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -8,6 +8,8 @@ use std::str; use predicates; use predicates::str::PredicateStrExt; +use predicates_core; +use predicates_tree::CaseTreeExt; use errors::dump_buffer; use errors::output_fmt; @@ -185,11 +187,7 @@ impl Assert { /// ``` pub fn failure(self) -> Self { if self.output.status.success() { - panic!( - "Unexpected success\nstdout=```{}```\n{}", - dump_buffer(&self.output.stdout), - self - ); + panic!("Unexpected success\n{}", self); } self } @@ -197,11 +195,7 @@ impl Assert { /// Ensure the command aborted before returning a code. pub fn interrupted(self) -> Self { if self.output.status.code().is_some() { - panic!( - "Unexpected completion\nstdout=```{}```\n{}", - dump_buffer(&self.output.stdout), - self - ); + panic!("Unexpected completion\n{}", self); } self } @@ -210,26 +204,38 @@ impl Assert { /// /// # Examples /// - /// ```rust + /// ```rust,ignore /// use assert_cmd::prelude::*; /// /// use std::process::Command; + /// use predicates::prelude::*; + /// + /// Command::main_binary() + /// .unwrap() + /// .env("exit", "42") + /// .assert() + /// .code(predicate::eq(42)); /// + /// // which can be shortened to: /// Command::main_binary() /// .unwrap() /// .env("exit", "42") /// .assert() /// .code(42); /// ``` + /// + /// See [`IntoCodePredicate`] for other built-in conversions. + /// + /// [IntoCodePredicate]: trait.IntoCodePredicate.html pub fn code(self, pred: I) -> Self where I: IntoCodePredicate

, - P: predicates::Predicate, + P: predicates_core::Predicate, { self.code_impl(&pred.into_code()) } - fn code_impl(self, pred: &predicates::Predicate) -> Self { + fn code_impl(self, pred: &predicates_core::Predicate) -> Self { let actual_code = self.output.status.code().unwrap_or_else(|| { panic!( "Command interrupted\nstderr=```{}```\n{}", @@ -237,13 +243,8 @@ impl Assert { self ) }); - if !pred.eval(&actual_code) { - panic!( - "Unexpected return code\nstdout=```{}```\nstderr=```{}```\n{}", - dump_buffer(&self.output.stdout), - dump_buffer(&self.output.stderr), - self - ); + if let Some(case) = pred.find_case(false, &actual_code) { + panic!("Unexpected return code, failed {}\n{}", case.tree(), self); } self } @@ -252,31 +253,44 @@ impl Assert { /// /// # Examples /// - /// ```rust + /// ```rust,ignore /// use assert_cmd::prelude::*; /// /// use std::process::Command; + /// use predicates::prelude::*; /// /// Command::main_binary() /// .unwrap() /// .env("stdout", "hello") /// .env("stderr", "world") /// .assert() + /// .stdout(predicate::str::similar("hello\n").from_utf8()); + /// + /// // which can be shortened to: + /// Command::main_binary() + /// .unwrap() + /// .env("stdout", "hello") + /// .env("stderr", "world") + /// .assert() /// .stdout("hello\n"); /// ``` + /// + /// See [`IntoOutputPredicate`] for other built-in conversions. + /// + /// [IntoOutputPredicate]: trait.IntoOutputPredicate.html pub fn stdout(self, pred: I) -> Self where I: IntoOutputPredicate

, - P: predicates::Predicate<[u8]>, + P: predicates_core::Predicate<[u8]>, { self.stdout_impl(&pred.into_output()) } - fn stdout_impl(self, pred: &predicates::Predicate<[u8]>) -> Self { + fn stdout_impl(self, pred: &predicates_core::Predicate<[u8]>) -> Self { { let actual = &self.output.stdout; - if !pred.eval(actual) { - panic!("Unexpected stdout\n{}", self); + if let Some(case) = pred.find_case(false, &actual) { + panic!("Unexpected stdout, failed {}\n{}", case.tree(), self); } } self @@ -286,11 +300,20 @@ impl Assert { /// /// # Examples /// - /// ```rust + /// ```rust,ignore /// use assert_cmd::prelude::*; /// /// use std::process::Command; + /// use predicates::prelude::*; + /// + /// Command::main_binary() + /// .unwrap() + /// .env("stdout", "hello") + /// .env("stderr", "world") + /// .assert() + /// .stderr(predicate::str::similar("world\n").from_utf8()); /// + /// // which can be shortened to: /// Command::main_binary() /// .unwrap() /// .env("stdout", "hello") @@ -298,19 +321,23 @@ impl Assert { /// .assert() /// .stderr("world\n"); /// ``` + /// + /// See [`IntoOutputPredicate`] for other built-in conversions. + /// + /// [IntoOutputPredicate]: trait.IntoOutputPredicate.html pub fn stderr(self, pred: I) -> Self where I: IntoOutputPredicate

, - P: predicates::Predicate<[u8]>, + P: predicates_core::Predicate<[u8]>, { self.stderr_impl(&pred.into_output()) } - fn stderr_impl(self, pred: &predicates::Predicate<[u8]>) -> Self { + fn stderr_impl(self, pred: &predicates_core::Predicate<[u8]>) -> Self { { let actual = &self.output.stderr; - if !pred.eval(actual) { - panic!("Unexpected stderr\n{}", self); + if let Some(case) = pred.find_case(false, &actual) { + panic!("Unexpected stderr, failed {}\n\n{}", case.tree(), self); } } self @@ -343,25 +370,27 @@ impl fmt::Debug for Assert { /// use assert_cmd::prelude::*; /// /// use std::process::Command; +/// use predicates::prelude::*; /// /// Command::main_binary() /// .unwrap() /// .env("exit", "42") /// .assert() -/// .code(42); -/// // which is equivalent to +/// .code(predicate::eq(42)); +/// +/// // which can be shortened to: /// Command::main_binary() /// .unwrap() /// .env("exit", "42") /// .assert() -/// .code(predicates::ord::eq(42)); +/// .code(42); /// ``` /// /// [`Assert::code`]: struct.Assert.html#method.code -/// [`Predicate`]: https://docs.rs/predicates/0.5.2/predicates/trait.Predicate.html +/// [`Predicate`]: https://docs.rs/predicates-core/0.9.0/predicates_core/trait.Predicate.html pub trait IntoCodePredicate

where - P: predicates::Predicate, + P: predicates_core::Predicate, { /// The type of the predicate being returned. type Predicate; @@ -372,7 +401,7 @@ where impl

IntoCodePredicate

for P where - P: predicates::Predicate, + P: predicates_core::Predicate, { type Predicate = P; @@ -408,12 +437,36 @@ impl IntoCodePredicate> for &'static [i32] { /// Used by [`Assert::stdout`] and [`Assert::stderr`] to convert Self /// into the needed [`Predicate<[u8]>`]. /// +/// # Examples +/// +/// ```rust,ignore +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// use predicates::prelude::*; +/// +/// Command::main_binary() +/// .unwrap() +/// .env("stdout", "hello") +/// .env("stderr", "world") +/// .assert() +/// .stdout(predicate::str::similar("hello\n").from_utf8()); +/// +/// // which can be shortened to: +/// Command::main_binary() +/// .unwrap() +/// .env("stdout", "hello") +/// .env("stderr", "world") +/// .assert() +/// .stdout("hello\n"); +/// ``` +/// /// [`Assert::stdout`]: struct.Assert.html#method.stdout /// [`Assert::stderr`]: struct.Assert.html#method.stderr -/// [`Predicate<[u8]>`]: https://docs.rs/predicates/0.5.2/predicates/trait.Predicate.html +/// [`Predicate<[u8]>`]: https://docs.rs/predicates-core/0.9.0/predicates_core/trait.Predicate.html pub trait IntoOutputPredicate

where - P: predicates::Predicate<[u8]>, + P: predicates_core::Predicate<[u8]>, { /// The type of the predicate being returned. type Predicate; @@ -424,7 +477,7 @@ where impl

IntoOutputPredicate

for P where - P: predicates::Predicate<[u8]>, + P: predicates_core::Predicate<[u8]>, { type Predicate = P; @@ -437,7 +490,7 @@ where /// [Predicate] used by [`IntoOutputPredicate`] for bytes. /// /// [`IntoOutputPredicate`]: trait.IntoOutputPredicate.html -/// [Predicate]: https://docs.rs/predicates/0.5.2/predicates/trait.Predicate.html +/// [Predicate]: https://docs.rs/predicates-core/0.9.0/predicates_core/trait.Predicate.html #[derive(Debug)] pub struct BytesContentOutputPredicate(predicates::ord::EqPredicate<&'static [u8]>); @@ -448,10 +501,31 @@ impl BytesContentOutputPredicate { } } -impl predicates::Predicate<[u8]> for BytesContentOutputPredicate { +impl predicates_core::reflection::PredicateReflection for BytesContentOutputPredicate { + fn parameters<'a>( + &'a self, + ) -> Box> + 'a> { + self.0.parameters() + } + + /// Nested `Predicate`s of the current `Predicate`. + fn children<'a>(&'a self) -> Box> + 'a> { + self.0.children() + } +} + +impl predicates_core::Predicate<[u8]> for BytesContentOutputPredicate { fn eval(&self, item: &[u8]) -> bool { self.0.eval(item) } + + fn find_case<'a>( + &'a self, + expected: bool, + variable: &[u8], + ) -> Option> { + self.0.find_case(expected, variable) + } } impl fmt::Display for BytesContentOutputPredicate { @@ -472,7 +546,7 @@ impl IntoOutputPredicate for &'static [u8] { /// [Predicate] used by [`IntoOutputPredicate`] for [`str`]. /// /// [`IntoOutputPredicate`]: trait.IntoOutputPredicate.html -/// [Predicate]: https://docs.rs/predicates/0.5.2/predicates/trait.Predicate.html +/// [Predicate]: https://docs.rs/predicates-core/0.9.0/predicates_core/trait.Predicate.html /// [`str`]: https://doc.rust-lang.org/std/primitive.str.html #[derive(Debug, Clone)] pub struct StrContentOutputPredicate( @@ -486,10 +560,31 @@ impl StrContentOutputPredicate { } } -impl predicates::Predicate<[u8]> for StrContentOutputPredicate { +impl predicates_core::reflection::PredicateReflection for StrContentOutputPredicate { + fn parameters<'a>( + &'a self, + ) -> Box> + 'a> { + self.0.parameters() + } + + /// Nested `Predicate`s of the current `Predicate`. + fn children<'a>(&'a self) -> Box> + 'a> { + self.0.children() + } +} + +impl predicates_core::Predicate<[u8]> for StrContentOutputPredicate { fn eval(&self, item: &[u8]) -> bool { self.0.eval(item) } + + fn find_case<'a>( + &'a self, + expected: bool, + variable: &[u8], + ) -> Option> { + self.0.find_case(expected, variable) + } } impl fmt::Display for StrContentOutputPredicate { @@ -517,7 +612,7 @@ mod test { fn convert_code(pred: I) -> P where I: IntoCodePredicate

, - P: predicates::Predicate, + P: predicates_core::Predicate, { pred.into_code() } @@ -551,7 +646,7 @@ mod test { fn convert_output(pred: I) -> P where I: IntoOutputPredicate

, - P: predicates::Predicate<[u8]>, + P: predicates_core::Predicate<[u8]>, { pred.into_output() } diff --git a/src/lib.rs b/src/lib.rs index e991c0c..6f5e759 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,8 @@ extern crate escargot; extern crate predicates; +extern crate predicates_core; +extern crate predicates_tree; #[macro_use] extern crate serde; diff --git a/tests/assert.rs b/tests/assert.rs index e4173b9..77d8a9d 100644 --- a/tests/assert.rs +++ b/tests/assert.rs @@ -12,11 +12,48 @@ fn code_example() { .unwrap() .env("exit", "42") .assert() - .code(42); - // which is equivalent to + .code(predicate::eq(42)); + + // which can be shortened to: Command::main_binary() .unwrap() .env("exit", "42") .assert() - .code(predicate::eq(42)); + .code(42); +} + +#[test] +fn stdout_example() { + Command::main_binary() + .unwrap() + .env("stdout", "hello") + .env("stderr", "world") + .assert() + .stdout(predicate::str::similar("hello\n").from_utf8()); + + // which can be shortened to: + Command::main_binary() + .unwrap() + .env("stdout", "hello") + .env("stderr", "world") + .assert() + .stdout("hello\n"); +} + +#[test] +fn stderr_example() { + Command::main_binary() + .unwrap() + .env("stdout", "hello") + .env("stderr", "world") + .assert() + .stderr(predicate::str::similar("world\n").from_utf8()); + + // which can be shortened to: + Command::main_binary() + .unwrap() + .env("stdout", "hello") + .env("stderr", "world") + .assert() + .stderr("world\n"); }