Skip to content

Commit 3caa128

Browse files
committed
feat(output): Generic predicate support
This accepts a message with it. This should hit the 90% case of a `satisfies_ok` (or whatever it'd be called). I'm also assuming that it'll be a best practice to document the custom predicates, so its acceptable to force it on everyone. If a `satisfies_ok` is found to be needed, I'm assuming its because the user wants to tie into existing machinery that has error reporting. This means we'll probably need to accept an `Fn` that `Box`es the error to preserve it. Fixes assert-rs#55
1 parent b199120 commit 3caa128

File tree

2 files changed

+78
-3
lines changed

2 files changed

+78
-3
lines changed

src/assert.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,26 @@ impl OutputAssertionBuilder {
475475
self.assertion.expect_output.push(pred);
476476
self.assertion
477477
}
478+
479+
/// Expect the command output to satisfy the given predicate.
480+
///
481+
/// # Examples
482+
///
483+
/// ```rust
484+
/// extern crate assert_cli;
485+
///
486+
/// assert_cli::Assert::command(&["echo", "-n", "42"])
487+
/// .stdout().satisfies(|x| x.len() == 2, "bad length")
488+
/// .unwrap();
489+
/// ```
490+
pub fn satisfies<F, M>(mut self, pred: F, msg: M) -> Assert
491+
where F: 'static + Fn(&str) -> bool,
492+
M: Into<String>
493+
{
494+
let pred = OutputPredicate::new(self.kind, Output::satisfies(pred, msg));
495+
self.assertion.expect_output.push(pred);
496+
self.assertion
497+
}
478498
}
479499

480500
#[cfg(test)]

src/output.rs

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
use std::fmt;
2+
use std::process;
3+
use std::rc;
4+
5+
use difference::Changeset;
6+
7+
use diff;
18
use self::errors::*;
29
pub use self::errors::{Error, ErrorKind};
3-
use diff;
4-
use difference::Changeset;
5-
use std::process;
610

711

812
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -57,17 +61,42 @@ impl ContainsPredicate {
5761
}
5862
}
5963

64+
#[derive(Clone)]
65+
struct FnPredicate {
66+
pub pred: rc::Rc<Fn(&str) -> bool>,
67+
pub msg: String,
68+
}
69+
70+
impl FnPredicate {
71+
pub fn verify_str(&self, got: &str) -> Result<()> {
72+
let pred = &self.pred;
73+
if ! pred(got) {
74+
bail!(ErrorKind::PredicateFailed(got.into(), self.msg.clone()));
75+
}
76+
77+
Ok(())
78+
}
79+
}
80+
81+
impl fmt::Debug for FnPredicate {
82+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83+
write!(f, "{}", self.msg)
84+
}
85+
}
86+
6087
#[derive(Debug, Clone)]
6188
enum StrPredicate {
6289
Is(IsPredicate),
6390
Contains(ContainsPredicate),
91+
Fn(FnPredicate),
6492
}
6593

6694
impl StrPredicate {
6795
pub fn verify_str(&self, got: &str) -> Result<()> {
6896
match *self {
6997
StrPredicate::Is(ref pred) => pred.verify_str(got),
7098
StrPredicate::Contains(ref pred) => pred.verify_str(got),
99+
StrPredicate::Fn(ref pred) => pred.verify_str(got),
71100
}
72101
}
73102
}
@@ -159,6 +188,28 @@ impl Output {
159188
Self::new(StrPredicate::Is(pred))
160189
}
161190

191+
/// Expect the command output to satisfy the given predicate.
192+
///
193+
/// # Examples
194+
///
195+
/// ```rust
196+
/// extern crate assert_cli;
197+
///
198+
/// assert_cli::Assert::command(&["echo", "-n", "42"])
199+
/// .stdout().satisfies(|x| x.len() == 2, "bad length")
200+
/// .unwrap();
201+
/// ```
202+
pub fn satisfies<F, M>(pred: F, msg: M) -> Self
203+
where F: 'static + Fn(&str) -> bool,
204+
M: Into<String>
205+
{
206+
let pred = FnPredicate {
207+
pred: rc::Rc::new(pred),
208+
msg: msg.into(),
209+
};
210+
Self::new(StrPredicate::Fn(pred))
211+
}
212+
162213
fn new(pred: StrPredicate) -> Self {
163214
Self { pred }
164215
}
@@ -232,6 +283,10 @@ mod errors {
232283
description("Output was not as expected")
233284
display("expected to not match\noutput=```{}```", got)
234285
}
286+
PredicateFailed(got: String, msg: String) {
287+
description("Output predicate failed")
288+
display("{}\noutput=```{}```", msg, got)
289+
}
235290
OutputMismatch(kind: super::OutputKind) {
236291
description("Output was not as expected")
237292
display(

0 commit comments

Comments
 (0)