Skip to content

Commit e2126e6

Browse files
authored
Merge pull request #39 from epage/pred
feat(assert): Show cause of assert
2 parents eaea7a2 + df840d4 commit e2126e6

File tree

4 files changed

+185
-49
lines changed

4 files changed

+185
-49
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ keywords = ["cli", "test", "assert", "command", "duct"]
1515
name = "bin_fixture"
1616

1717
[dependencies]
18-
predicates = "0.5.1"
18+
predicates = "0.9.0"
19+
predicates-core = "0.9.0"
20+
predicates-tree = "0.9.0"
1921
escargot = "0.2"
2022
serde = { version = "1.0", features = ["derive"] }
2123

src/assert.rs

Lines changed: 140 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use std::str;
88

99
use predicates;
1010
use predicates::str::PredicateStrExt;
11+
use predicates_core;
12+
use predicates_tree::CaseTreeExt;
1113

1214
use errors::dump_buffer;
1315
use errors::output_fmt;
@@ -185,23 +187,15 @@ impl Assert {
185187
/// ```
186188
pub fn failure(self) -> Self {
187189
if self.output.status.success() {
188-
panic!(
189-
"Unexpected success\nstdout=```{}```\n{}",
190-
dump_buffer(&self.output.stdout),
191-
self
192-
);
190+
panic!("Unexpected success\n{}", self);
193191
}
194192
self
195193
}
196194

197195
/// Ensure the command aborted before returning a code.
198196
pub fn interrupted(self) -> Self {
199197
if self.output.status.code().is_some() {
200-
panic!(
201-
"Unexpected completion\nstdout=```{}```\n{}",
202-
dump_buffer(&self.output.stdout),
203-
self
204-
);
198+
panic!("Unexpected completion\n{}", self);
205199
}
206200
self
207201
}
@@ -210,40 +204,47 @@ impl Assert {
210204
///
211205
/// # Examples
212206
///
213-
/// ```rust
207+
/// ```rust,ignore
214208
/// use assert_cmd::prelude::*;
215209
///
216210
/// use std::process::Command;
211+
/// use predicates::prelude::*;
212+
///
213+
/// Command::main_binary()
214+
/// .unwrap()
215+
/// .env("exit", "42")
216+
/// .assert()
217+
/// .code(predicate::eq(42));
217218
///
219+
/// // which can be shortened to:
218220
/// Command::main_binary()
219221
/// .unwrap()
220222
/// .env("exit", "42")
221223
/// .assert()
222224
/// .code(42);
223225
/// ```
226+
///
227+
/// See [`IntoCodePredicate`] for other built-in conversions.
228+
///
229+
/// [IntoCodePredicate]: trait.IntoCodePredicate.html
224230
pub fn code<I, P>(self, pred: I) -> Self
225231
where
226232
I: IntoCodePredicate<P>,
227-
P: predicates::Predicate<i32>,
233+
P: predicates_core::Predicate<i32>,
228234
{
229235
self.code_impl(&pred.into_code())
230236
}
231237

232-
fn code_impl(self, pred: &predicates::Predicate<i32>) -> Self {
238+
fn code_impl(self, pred: &predicates_core::Predicate<i32>) -> Self {
233239
let actual_code = self.output.status.code().unwrap_or_else(|| {
234240
panic!(
235241
"Command interrupted\nstderr=```{}```\n{}",
236242
dump_buffer(&self.output.stderr),
237243
self
238244
)
239245
});
240-
if !pred.eval(&actual_code) {
241-
panic!(
242-
"Unexpected return code\nstdout=```{}```\nstderr=```{}```\n{}",
243-
dump_buffer(&self.output.stdout),
244-
dump_buffer(&self.output.stderr),
245-
self
246-
);
246+
if let Some(case) = pred.find_case(false, &actual_code) {
247+
panic!("Unexpected return code, failed {}\n{}", case.tree(), self);
247248
}
248249
self
249250
}
@@ -252,31 +253,44 @@ impl Assert {
252253
///
253254
/// # Examples
254255
///
255-
/// ```rust
256+
/// ```rust,ignore
256257
/// use assert_cmd::prelude::*;
257258
///
258259
/// use std::process::Command;
260+
/// use predicates::prelude::*;
259261
///
260262
/// Command::main_binary()
261263
/// .unwrap()
262264
/// .env("stdout", "hello")
263265
/// .env("stderr", "world")
264266
/// .assert()
267+
/// .stdout(predicate::str::similar("hello\n").from_utf8());
268+
///
269+
/// // which can be shortened to:
270+
/// Command::main_binary()
271+
/// .unwrap()
272+
/// .env("stdout", "hello")
273+
/// .env("stderr", "world")
274+
/// .assert()
265275
/// .stdout("hello\n");
266276
/// ```
277+
///
278+
/// See [`IntoOutputPredicate`] for other built-in conversions.
279+
///
280+
/// [IntoOutputPredicate]: trait.IntoOutputPredicate.html
267281
pub fn stdout<I, P>(self, pred: I) -> Self
268282
where
269283
I: IntoOutputPredicate<P>,
270-
P: predicates::Predicate<[u8]>,
284+
P: predicates_core::Predicate<[u8]>,
271285
{
272286
self.stdout_impl(&pred.into_output())
273287
}
274288

275-
fn stdout_impl(self, pred: &predicates::Predicate<[u8]>) -> Self {
289+
fn stdout_impl(self, pred: &predicates_core::Predicate<[u8]>) -> Self {
276290
{
277291
let actual = &self.output.stdout;
278-
if !pred.eval(actual) {
279-
panic!("Unexpected stdout\n{}", self);
292+
if let Some(case) = pred.find_case(false, &actual) {
293+
panic!("Unexpected stdout, failed {}\n{}", case.tree(), self);
280294
}
281295
}
282296
self
@@ -286,31 +300,44 @@ impl Assert {
286300
///
287301
/// # Examples
288302
///
289-
/// ```rust
303+
/// ```rust,ignore
290304
/// use assert_cmd::prelude::*;
291305
///
292306
/// use std::process::Command;
307+
/// use predicates::prelude::*;
308+
///
309+
/// Command::main_binary()
310+
/// .unwrap()
311+
/// .env("stdout", "hello")
312+
/// .env("stderr", "world")
313+
/// .assert()
314+
/// .stderr(predicate::str::similar("world\n").from_utf8());
293315
///
316+
/// // which can be shortened to:
294317
/// Command::main_binary()
295318
/// .unwrap()
296319
/// .env("stdout", "hello")
297320
/// .env("stderr", "world")
298321
/// .assert()
299322
/// .stderr("world\n");
300323
/// ```
324+
///
325+
/// See [`IntoOutputPredicate`] for other built-in conversions.
326+
///
327+
/// [IntoOutputPredicate]: trait.IntoOutputPredicate.html
301328
pub fn stderr<I, P>(self, pred: I) -> Self
302329
where
303330
I: IntoOutputPredicate<P>,
304-
P: predicates::Predicate<[u8]>,
331+
P: predicates_core::Predicate<[u8]>,
305332
{
306333
self.stderr_impl(&pred.into_output())
307334
}
308335

309-
fn stderr_impl(self, pred: &predicates::Predicate<[u8]>) -> Self {
336+
fn stderr_impl(self, pred: &predicates_core::Predicate<[u8]>) -> Self {
310337
{
311338
let actual = &self.output.stderr;
312-
if !pred.eval(actual) {
313-
panic!("Unexpected stderr\n{}", self);
339+
if let Some(case) = pred.find_case(false, &actual) {
340+
panic!("Unexpected stderr, failed {}\n\n{}", case.tree(), self);
314341
}
315342
}
316343
self
@@ -343,25 +370,27 @@ impl fmt::Debug for Assert {
343370
/// use assert_cmd::prelude::*;
344371
///
345372
/// use std::process::Command;
373+
/// use predicates::prelude::*;
346374
///
347375
/// Command::main_binary()
348376
/// .unwrap()
349377
/// .env("exit", "42")
350378
/// .assert()
351-
/// .code(42);
352-
/// // which is equivalent to
379+
/// .code(predicate::eq(42));
380+
///
381+
/// // which can be shortened to:
353382
/// Command::main_binary()
354383
/// .unwrap()
355384
/// .env("exit", "42")
356385
/// .assert()
357-
/// .code(predicates::ord::eq(42));
386+
/// .code(42);
358387
/// ```
359388
///
360389
/// [`Assert::code`]: struct.Assert.html#method.code
361-
/// [`Predicate<i32>`]: https://docs.rs/predicates/0.5.2/predicates/trait.Predicate.html
390+
/// [`Predicate<i32>`]: https://docs.rs/predicates-core/0.9.0/predicates_core/trait.Predicate.html
362391
pub trait IntoCodePredicate<P>
363392
where
364-
P: predicates::Predicate<i32>,
393+
P: predicates_core::Predicate<i32>,
365394
{
366395
/// The type of the predicate being returned.
367396
type Predicate;
@@ -372,7 +401,7 @@ where
372401

373402
impl<P> IntoCodePredicate<P> for P
374403
where
375-
P: predicates::Predicate<i32>,
404+
P: predicates_core::Predicate<i32>,
376405
{
377406
type Predicate = P;
378407

@@ -408,12 +437,36 @@ impl IntoCodePredicate<predicates::iter::InPredicate<i32>> for &'static [i32] {
408437
/// Used by [`Assert::stdout`] and [`Assert::stderr`] to convert Self
409438
/// into the needed [`Predicate<[u8]>`].
410439
///
440+
/// # Examples
441+
///
442+
/// ```rust,ignore
443+
/// use assert_cmd::prelude::*;
444+
///
445+
/// use std::process::Command;
446+
/// use predicates::prelude::*;
447+
///
448+
/// Command::main_binary()
449+
/// .unwrap()
450+
/// .env("stdout", "hello")
451+
/// .env("stderr", "world")
452+
/// .assert()
453+
/// .stdout(predicate::str::similar("hello\n").from_utf8());
454+
///
455+
/// // which can be shortened to:
456+
/// Command::main_binary()
457+
/// .unwrap()
458+
/// .env("stdout", "hello")
459+
/// .env("stderr", "world")
460+
/// .assert()
461+
/// .stdout("hello\n");
462+
/// ```
463+
///
411464
/// [`Assert::stdout`]: struct.Assert.html#method.stdout
412465
/// [`Assert::stderr`]: struct.Assert.html#method.stderr
413-
/// [`Predicate<[u8]>`]: https://docs.rs/predicates/0.5.2/predicates/trait.Predicate.html
466+
/// [`Predicate<[u8]>`]: https://docs.rs/predicates-core/0.9.0/predicates_core/trait.Predicate.html
414467
pub trait IntoOutputPredicate<P>
415468
where
416-
P: predicates::Predicate<[u8]>,
469+
P: predicates_core::Predicate<[u8]>,
417470
{
418471
/// The type of the predicate being returned.
419472
type Predicate;
@@ -424,7 +477,7 @@ where
424477

425478
impl<P> IntoOutputPredicate<P> for P
426479
where
427-
P: predicates::Predicate<[u8]>,
480+
P: predicates_core::Predicate<[u8]>,
428481
{
429482
type Predicate = P;
430483

@@ -437,7 +490,7 @@ where
437490
/// [Predicate] used by [`IntoOutputPredicate`] for bytes.
438491
///
439492
/// [`IntoOutputPredicate`]: trait.IntoOutputPredicate.html
440-
/// [Predicate]: https://docs.rs/predicates/0.5.2/predicates/trait.Predicate.html
493+
/// [Predicate]: https://docs.rs/predicates-core/0.9.0/predicates_core/trait.Predicate.html
441494
#[derive(Debug)]
442495
pub struct BytesContentOutputPredicate(predicates::ord::EqPredicate<&'static [u8]>);
443496

@@ -448,10 +501,31 @@ impl BytesContentOutputPredicate {
448501
}
449502
}
450503

451-
impl predicates::Predicate<[u8]> for BytesContentOutputPredicate {
504+
impl predicates_core::reflection::PredicateReflection for BytesContentOutputPredicate {
505+
fn parameters<'a>(
506+
&'a self,
507+
) -> Box<Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
508+
self.0.parameters()
509+
}
510+
511+
/// Nested `Predicate`s of the current `Predicate`.
512+
fn children<'a>(&'a self) -> Box<Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
513+
self.0.children()
514+
}
515+
}
516+
517+
impl predicates_core::Predicate<[u8]> for BytesContentOutputPredicate {
452518
fn eval(&self, item: &[u8]) -> bool {
453519
self.0.eval(item)
454520
}
521+
522+
fn find_case<'a>(
523+
&'a self,
524+
expected: bool,
525+
variable: &[u8],
526+
) -> Option<predicates_core::reflection::Case<'a>> {
527+
self.0.find_case(expected, variable)
528+
}
455529
}
456530

457531
impl fmt::Display for BytesContentOutputPredicate {
@@ -472,7 +546,7 @@ impl IntoOutputPredicate<BytesContentOutputPredicate> for &'static [u8] {
472546
/// [Predicate] used by [`IntoOutputPredicate`] for [`str`].
473547
///
474548
/// [`IntoOutputPredicate`]: trait.IntoOutputPredicate.html
475-
/// [Predicate]: https://docs.rs/predicates/0.5.2/predicates/trait.Predicate.html
549+
/// [Predicate]: https://docs.rs/predicates-core/0.9.0/predicates_core/trait.Predicate.html
476550
/// [`str`]: https://doc.rust-lang.org/std/primitive.str.html
477551
#[derive(Debug, Clone)]
478552
pub struct StrContentOutputPredicate(
@@ -486,10 +560,31 @@ impl StrContentOutputPredicate {
486560
}
487561
}
488562

489-
impl predicates::Predicate<[u8]> for StrContentOutputPredicate {
563+
impl predicates_core::reflection::PredicateReflection for StrContentOutputPredicate {
564+
fn parameters<'a>(
565+
&'a self,
566+
) -> Box<Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
567+
self.0.parameters()
568+
}
569+
570+
/// Nested `Predicate`s of the current `Predicate`.
571+
fn children<'a>(&'a self) -> Box<Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
572+
self.0.children()
573+
}
574+
}
575+
576+
impl predicates_core::Predicate<[u8]> for StrContentOutputPredicate {
490577
fn eval(&self, item: &[u8]) -> bool {
491578
self.0.eval(item)
492579
}
580+
581+
fn find_case<'a>(
582+
&'a self,
583+
expected: bool,
584+
variable: &[u8],
585+
) -> Option<predicates_core::reflection::Case<'a>> {
586+
self.0.find_case(expected, variable)
587+
}
493588
}
494589

495590
impl fmt::Display for StrContentOutputPredicate {
@@ -517,7 +612,7 @@ mod test {
517612
fn convert_code<I, P>(pred: I) -> P
518613
where
519614
I: IntoCodePredicate<P>,
520-
P: predicates::Predicate<i32>,
615+
P: predicates_core::Predicate<i32>,
521616
{
522617
pred.into_code()
523618
}
@@ -551,7 +646,7 @@ mod test {
551646
fn convert_output<I, P>(pred: I) -> P
552647
where
553648
I: IntoOutputPredicate<P>,
554-
P: predicates::Predicate<[u8]>,
649+
P: predicates_core::Predicate<[u8]>,
555650
{
556651
pred.into_output()
557652
}

0 commit comments

Comments
 (0)