Skip to content
This repository was archived by the owner on Dec 29, 2021. It is now read-only.

Commit 62a1720

Browse files
authored
Merge pull request #87 from epage/refactor_output
Refactor output
2 parents 3cca64e + f0145fb commit 62a1720

File tree

4 files changed

+396
-114
lines changed

4 files changed

+396
-114
lines changed

src/assert.rs

+57-62
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use environment::Environment;
22
use error_chain::ChainedError;
33
use errors::*;
4-
use output::{OutputAssertion, OutputKind};
4+
use output::{Content, Output, OutputKind, OutputPredicate};
55
use std::default;
66
use std::ffi::{OsStr, OsString};
77
use std::io::Write;
@@ -18,8 +18,8 @@ pub struct Assert {
1818
current_dir: Option<PathBuf>,
1919
expect_success: Option<bool>,
2020
expect_exit_code: Option<i32>,
21-
expect_output: Vec<OutputAssertion>,
22-
stdin_contents: Option<String>,
21+
expect_output: Vec<OutputPredicate>,
22+
stdin_contents: Option<Vec<u8>>,
2323
}
2424

2525
impl default::Default for Assert {
@@ -118,8 +118,8 @@ impl Assert {
118118
/// .stdout().contains("42")
119119
/// .unwrap();
120120
/// ```
121-
pub fn stdin(mut self, contents: &str) -> Self {
122-
self.stdin_contents = Some(String::from(contents));
121+
pub fn stdin<S: Into<Vec<u8>>>(mut self, contents: S) -> Self {
122+
self.stdin_contents = Some(contents.into());
123123
self
124124
}
125125

@@ -289,7 +289,6 @@ impl Assert {
289289
OutputAssertionBuilder {
290290
assertion: self,
291291
kind: OutputKind::StdOut,
292-
expected_result: true,
293292
}
294293
}
295294

@@ -310,7 +309,6 @@ impl Assert {
310309
OutputAssertionBuilder {
311310
assertion: self,
312311
kind: OutputKind::StdErr,
313-
expected_result: true,
314312
}
315313
}
316314

@@ -327,10 +325,10 @@ impl Assert {
327325
/// assert!(test.is_ok());
328326
/// ```
329327
pub fn execute(self) -> Result<()> {
330-
let cmd = &self.cmd[0];
328+
let bin = &self.cmd[0];
331329

332330
let args: Vec<_> = self.cmd.iter().skip(1).collect();
333-
let mut command = Command::new(cmd);
331+
let mut command = Command::new(bin);
334332
let command = command
335333
.stdin(Stdio::piped())
336334
.stdout(Stdio::piped())
@@ -353,38 +351,34 @@ impl Assert {
353351
.stdin
354352
.as_mut()
355353
.expect("Couldn't get mut ref to command stdin")
356-
.write_all(contents.as_bytes())?;
354+
.write_all(contents)?;
357355
}
358356
let output = spawned.wait_with_output()?;
359357

360358
if let Some(expect_success) = self.expect_success {
361359
if expect_success != output.status.success() {
362360
let out = String::from_utf8_lossy(&output.stdout).to_string();
363361
let err = String::from_utf8_lossy(&output.stderr).to_string();
364-
bail!(ErrorKind::StatusMismatch(
365-
self.cmd.clone(),
366-
expect_success,
367-
out,
368-
err,
369-
));
362+
let err: Error = ErrorKind::StatusMismatch(expect_success, out, err).into();
363+
bail!(err.chain_err(|| ErrorKind::AssertionFailed(self.cmd.clone())));
370364
}
371365
}
372366

373367
if self.expect_exit_code.is_some() && self.expect_exit_code != output.status.code() {
374368
let out = String::from_utf8_lossy(&output.stdout).to_string();
375369
let err = String::from_utf8_lossy(&output.stderr).to_string();
376-
bail!(ErrorKind::ExitCodeMismatch(
377-
self.cmd.clone(),
378-
self.expect_exit_code,
379-
output.status.code(),
380-
out,
381-
err,
382-
));
370+
let err: Error =
371+
ErrorKind::ExitCodeMismatch(self.expect_exit_code, output.status.code(), out, err)
372+
.into();
373+
bail!(err.chain_err(|| ErrorKind::AssertionFailed(self.cmd.clone())));
383374
}
384375

385376
self.expect_output
386377
.iter()
387-
.map(|a| a.execute(&output, &self.cmd))
378+
.map(|a| {
379+
a.verify(&output)
380+
.chain_err(|| ErrorKind::AssertionFailed(self.cmd.clone()))
381+
})
388382
.collect::<Result<Vec<()>>>()?;
389383

390384
Ok(())
@@ -414,98 +408,96 @@ impl Assert {
414408
pub struct OutputAssertionBuilder {
415409
assertion: Assert,
416410
kind: OutputKind,
417-
expected_result: bool,
418411
}
419412

420413
impl OutputAssertionBuilder {
421-
/// Negate the assertion predicate
414+
/// Expect the command's output to **contain** `output`.
422415
///
423416
/// # Examples
424417
///
425418
/// ```rust
426419
/// extern crate assert_cli;
427420
///
428421
/// assert_cli::Assert::command(&["echo", "42"])
429-
/// .stdout().not().contains("73")
422+
/// .stdout().contains("42")
430423
/// .unwrap();
431424
/// ```
432-
// No clippy, we don't want to implement std::ops::Not :)
433-
#[cfg_attr(feature = "cargo-clippy", allow(should_implement_trait))]
434-
pub fn not(mut self) -> Self {
435-
self.expected_result = !self.expected_result;
436-
self
425+
pub fn contains<O: Into<Content>>(mut self, output: O) -> Assert {
426+
let pred = OutputPredicate::new(self.kind, Output::contains(output));
427+
self.assertion.expect_output.push(pred);
428+
self.assertion
437429
}
438430

439-
/// Expect the command's output to **contain** `output`.
431+
/// Expect the command to output **exactly** this `output`.
440432
///
441433
/// # Examples
442434
///
443435
/// ```rust
444436
/// extern crate assert_cli;
445437
///
446438
/// assert_cli::Assert::command(&["echo", "42"])
447-
/// .stdout().contains("42")
439+
/// .stdout().is("42")
448440
/// .unwrap();
449441
/// ```
450-
pub fn contains<O: Into<String>>(mut self, output: O) -> Assert {
451-
self.assertion.expect_output.push(OutputAssertion {
452-
expect: output.into(),
453-
fuzzy: true,
454-
expected_result: self.expected_result,
455-
kind: self.kind,
456-
});
442+
pub fn is<O: Into<Content>>(mut self, output: O) -> Assert {
443+
let pred = OutputPredicate::new(self.kind, Output::is(output));
444+
self.assertion.expect_output.push(pred);
457445
self.assertion
458446
}
459447

460-
/// Expect the command to output **exactly** this `output`.
448+
/// Expect the command's output to not **contain** `output`.
461449
///
462450
/// # Examples
463451
///
464452
/// ```rust
465453
/// extern crate assert_cli;
466454
///
467455
/// assert_cli::Assert::command(&["echo", "42"])
468-
/// .stdout().is("42")
456+
/// .stdout().doesnt_contain("73")
469457
/// .unwrap();
470458
/// ```
471-
pub fn is<O: Into<String>>(mut self, output: O) -> Assert {
472-
self.assertion.expect_output.push(OutputAssertion {
473-
expect: output.into(),
474-
fuzzy: false,
475-
expected_result: self.expected_result,
476-
kind: self.kind,
477-
});
459+
pub fn doesnt_contain<O: Into<Content>>(mut self, output: O) -> Assert {
460+
let pred = OutputPredicate::new(self.kind, Output::doesnt_contain(output));
461+
self.assertion.expect_output.push(pred);
478462
self.assertion
479463
}
480464

481-
/// Expect the command's output to not **contain** `output`.
465+
/// Expect the command to output to not be **exactly** this `output`.
482466
///
483467
/// # Examples
484468
///
485469
/// ```rust
486470
/// extern crate assert_cli;
487471
///
488472
/// assert_cli::Assert::command(&["echo", "42"])
489-
/// .stdout().doesnt_contain("73")
473+
/// .stdout().isnt("73")
490474
/// .unwrap();
491475
/// ```
492-
pub fn doesnt_contain<O: Into<String>>(self, output: O) -> Assert {
493-
self.not().contains(output)
476+
pub fn isnt<O: Into<Content>>(mut self, output: O) -> Assert {
477+
let pred = OutputPredicate::new(self.kind, Output::isnt(output));
478+
self.assertion.expect_output.push(pred);
479+
self.assertion
494480
}
495481

496-
/// Expect the command to output to not be **exactly** this `output`.
482+
/// Expect the command output to satisfy the given predicate.
497483
///
498484
/// # Examples
499485
///
500486
/// ```rust
501487
/// extern crate assert_cli;
502488
///
503-
/// assert_cli::Assert::command(&["echo", "42"])
504-
/// .stdout().isnt("73")
489+
/// assert_cli::Assert::command(&["echo", "-n", "42"])
490+
/// .stdout().satisfies(|x| x.len() == 2, "bad length")
505491
/// .unwrap();
506492
/// ```
507-
pub fn isnt<O: Into<String>>(self, output: O) -> Assert {
508-
self.not().is(output)
493+
pub fn satisfies<F, M>(mut self, pred: F, msg: M) -> Assert
494+
where
495+
F: 'static + Fn(&str) -> bool,
496+
M: Into<String>,
497+
{
498+
let pred = OutputPredicate::new(self.kind, Output::satisfies(pred, msg));
499+
self.assertion.expect_output.push(pred);
500+
self.assertion
509501
}
510502
}
511503

@@ -522,7 +514,11 @@ mod test {
522514
fn take_ownership() {
523515
let x = Environment::inherit();
524516

525-
command().with_env(x.clone()).with_env(&x).with_env(x);
517+
command()
518+
.with_env(x.clone())
519+
.with_env(&x)
520+
.with_env(x)
521+
.unwrap();
526522
}
527523

528524
#[test]
@@ -564,8 +560,7 @@ mod test {
564560
command()
565561
.with_env(y)
566562
.stdout()
567-
.not()
568-
.contains("key=value")
563+
.doesnt_contain("key=value")
569564
.execute()
570565
.unwrap();
571566
}

src/errors.rs

+13-15
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ fn format_cmd(cmd: &[OsString]) -> String {
1111
}
1212

1313
error_chain! {
14+
links {
15+
Output(output::Error, output::ErrorKind);
16+
}
1417
foreign_links {
1518
Io(::std::io::Error);
1619
Fmt(::std::fmt::Error);
@@ -24,46 +27,41 @@ error_chain! {
2427
format_cmd(cmd),
2528
)
2629
}
27-
StatusMismatch(cmd: Vec<OsString>, expected: bool, out: String, err: String) {
28-
description("Wrong status")
30+
AssertionFailed(cmd: Vec<OsString>) {
31+
description("Assertion failed")
2932
display(
30-
"{}: (command `{}` expected to {})\nstatus={}\nstdout=```{}```\nstderr=```{}```",
33+
"{}: (command `{}` failed)",
3134
ERROR_PREFIX,
3235
format_cmd(cmd),
36+
)
37+
}
38+
StatusMismatch(expected: bool, out: String, err: String) {
39+
description("Wrong status")
40+
display(
41+
"Expected to {}\nstatus={}\nstdout=```{}```\nstderr=```{}```",
3342
expected = if *expected { "succeed" } else { "fail" },
3443
got = if *expected { "failed" } else { "succeeded" },
3544
out = out,
3645
err = err,
3746
)
3847
}
3948
ExitCodeMismatch(
40-
cmd: Vec<OsString>,
4149
expected: Option<i32>,
4250
got: Option<i32>,
4351
out: String,
4452
err: String
4553
) {
4654
description("Wrong exit code")
4755
display(
48-
"{prefix}: (exit code of `{cmd}` expected to be `{expected:?}`)\n\
56+
"Expected exit code to be `{expected:?}`)\n\
4957
exit code=`{code:?}`\n\
5058
stdout=```{stdout}```\n\
5159
stderr=```{stderr}```",
52-
prefix=ERROR_PREFIX,
53-
cmd=format_cmd(cmd),
5460
expected=expected,
5561
code=got,
5662
stdout=out,
5763
stderr=err,
5864
)
5965
}
60-
OutputMismatch(cmd: Vec<OsString>, output_err: output::Error, kind: output::OutputKind) {
61-
description("Output was not as expected")
62-
display(
63-
"{}: `{}` {:?} mismatch: {}",
64-
ERROR_PREFIX, format_cmd(cmd), kind, output_err,
65-
)
66-
}
67-
6866
}
6967
}

src/lib.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,10 @@ mod errors;
129129
mod macros;
130130
pub use macros::flatten_escaped_string;
131131

132-
mod output;
133-
132+
mod assert;
134133
mod diff;
134+
mod output;
135135

136-
mod assert;
137136
pub use assert::Assert;
138137
pub use assert::OutputAssertionBuilder;
139138
/// Environment is a re-export of the Environment crate

0 commit comments

Comments
 (0)