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

Commit dad1065

Browse files
authored
Merge pull request #24 from killercup/refactor/output-assertion
Refactor output assertions
2 parents 434303a + 2620e07 commit dad1065

File tree

4 files changed

+134
-101
lines changed

4 files changed

+134
-101
lines changed

src/diff.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ extern crate colored;
22
use self::colored::Colorize;
33

44
use difference::{Difference, Changeset};
5-
use std::fmt::Write;
5+
use std::fmt::{Write, Error as fmtError};
66

7-
use errors::*;
8-
9-
pub fn render(&Changeset { ref diffs, .. }: &Changeset) -> Result<String> {
7+
pub fn render(&Changeset { ref diffs, .. }: &Changeset) -> Result<String, fmtError> {
108
let mut t = String::new();
119

1210
for (i, diff) in diffs.iter().enumerate() {
@@ -66,11 +64,11 @@ mod tests {
6664
sed do eiusmod tempor incididunt ut labore et dolore magna
6765
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
6866
ullamco laboris nisi ut aliquip ex ea commodo consequat.",
69-
"Lorem ipsum dolor sit amet, consectetur adipisicing elit,
67+
"Lorem ipsum dolor sit amet, consectetur adipisicing elit,
7068
sed do eiusmod tempor **incididunt** ut labore et dolore magna
7169
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
7270
ullamco laboris nisi ut aliquip ex ea commodo consequat.",
73-
"\n");
71+
"\n");
7472
println!("{}", render(&diff).unwrap());
7573
assert_eq!(render(&diff).unwrap(), " Lorem ipsum dolor sit amet, consectetur adipisicing elit,\n\u{1b}[31m-sed do eiusmod tempor incididunt ut labore et dolore magna\u{1b}[0m\n\u{1b}[32m+\u{1b}[0m\u{1b}[32msed do eiusmod tempor\u{1b}[0m \u{1b}[7;32m**incididunt**\u{1b}[0m \u{1b}[32mut labore et dolore magna\u{1b}[0m \n aliqua. Ut enim ad minim veniam, quis nostrud exercitation\nullamco laboris nisi ut aliquip ex ea commodo consequat.\n");
7674
}

src/errors.rs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ error_chain! {
99
StatusMismatch(cmd: Vec<String>, expected: bool) {
1010
description("Wrong status")
1111
display(
12-
"{}: `(command `{}` expected to {})` (command {})",
12+
"{}: (command `{}` expected to {}) (command {})",
1313
ERROR_PREFIX,
1414
cmd.join(" "),
1515
expected = if *expected { "succeed" } else { "fail" },
@@ -19,33 +19,27 @@ error_chain! {
1919
ExitCodeMismatch(cmd: Vec<String>, expected: Option<i32>, got: Option<i32>) {
2020
description("Wrong exit code")
2121
display(
22-
"{}: `(exit code of `{}` expected to be `{:?}`)` (exit code was: `{:?}`)",
22+
"{}: (exit code of `{}` expected to be `{:?}`) (exit code was: `{:?}`)",
2323
ERROR_PREFIX,
2424
cmd.join(" "),
2525
expected,
2626
got,
2727
)
2828
}
29-
OutputMismatch(output_name: String, cmd: Vec<String>, expected: String, got: String) {
29+
StdoutMismatch(cmd: Vec<String>, output_err: ::output::Error) {
3030
description("Output was not as expected")
3131
display(
32-
"{}: `({} of `{}` expected to contain `{:?}`)` (output was: `{:?}`)",
33-
ERROR_PREFIX,
34-
output_name,
35-
cmd.join(" "),
36-
expected,
37-
got,
32+
"{}: `{}` stdout mismatch: `{}`)",
33+
ERROR_PREFIX, cmd.join(" "), output_err,
3834
)
3935
}
40-
ExactOutputMismatch(output_name: String, cmd: Vec<String>, diff: String) {
41-
description("Output was not as expected")
36+
StderrMismatch(cmd: Vec<String>, output_err: ::output::Error) {
37+
description("Error output was not as expected")
4238
display(
43-
"{}: `({} of `{}` was not as expected)`\n{}\n",
44-
ERROR_PREFIX,
45-
output_name,
46-
cmd.join(" "),
47-
diff.trim()
39+
"{}: `{}` stderr mismatch: `{}`)",
40+
ERROR_PREFIX, cmd.join(" "), output_err,
4841
)
4942
}
43+
5044
}
5145
}

src/lib.rs

Lines changed: 18 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -108,17 +108,17 @@ extern crate difference;
108108
#[macro_use] extern crate error_chain;
109109
extern crate rustc_serialize;
110110

111-
use std::process::{Command, Output};
112-
use std::fmt;
113-
114-
use difference::Changeset;
111+
use std::process::Command;
115112

116113
mod errors;
117114
use errors::*;
118115

119116
#[macro_use] mod macros;
120117
pub use macros::flatten_escaped_string;
121118

119+
mod output;
120+
use output::{OutputAssertion, StdErr, StdOut};
121+
122122
mod diff;
123123

124124
/// Assertions for a specific command.
@@ -127,38 +127,8 @@ pub struct Assert {
127127
cmd: Vec<String>,
128128
expect_success: Option<bool>,
129129
expect_exit_code: Option<i32>,
130-
expect_stdout: Option<OutputAssertion>,
131-
expect_stderr: Option<OutputAssertion>,
132-
}
133-
134-
#[derive(Debug)]
135-
struct OutputAssertion {
136-
expect: String,
137-
fuzzy: bool,
138-
}
139-
140-
#[derive(Debug, Copy, Clone)]
141-
enum OutputType {
142-
StdOut,
143-
StdErr,
144-
}
145-
146-
impl OutputType {
147-
fn select<'a>(&self, o: &'a Output) -> &'a [u8] {
148-
match *self {
149-
OutputType::StdOut => &o.stdout,
150-
OutputType::StdErr => &o.stderr,
151-
}
152-
}
153-
}
154-
155-
impl fmt::Display for OutputType {
156-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157-
match *self {
158-
OutputType::StdOut => write!(f, "stdout"),
159-
OutputType::StdErr => write!(f, "stderr"),
160-
}
161-
}
130+
expect_stdout: Option<OutputAssertion<StdOut>>,
131+
expect_stderr: Option<OutputAssertion<StdErr>>,
162132
}
163133

164134
impl std::default::Default for Assert {
@@ -318,6 +288,7 @@ impl Assert {
318288
self.expect_stdout = Some(OutputAssertion {
319289
expect: output.into(),
320290
fuzzy: true,
291+
kind: StdOut,
321292
});
322293
self
323294
}
@@ -337,6 +308,7 @@ impl Assert {
337308
self.expect_stdout = Some(OutputAssertion {
338309
expect: output.into(),
339310
fuzzy: false,
311+
kind: StdOut,
340312
});
341313
self
342314
}
@@ -358,6 +330,7 @@ impl Assert {
358330
self.expect_stderr = Some(OutputAssertion {
359331
expect: output.into(),
360332
fuzzy: true,
333+
kind: StdErr,
361334
});
362335
self
363336
}
@@ -379,6 +352,7 @@ impl Assert {
379352
self.expect_stderr = Some(OutputAssertion {
380353
expect: output.into(),
381354
fuzzy: false,
355+
kind: StdErr,
382356
});
383357
self
384358
}
@@ -421,52 +395,17 @@ impl Assert {
421395
));
422396
}
423397

424-
self.assert_output(OutputType::StdOut, &output)?;
425-
self.assert_output(OutputType::StdErr, &output)?;
426-
427-
Ok(())
428-
}
429-
430-
/// Perform the appropriate output assertion.
431-
fn assert_output(&self, output_type: OutputType, output: &Output) -> Result<()> {
432-
let observed = String::from_utf8_lossy(output_type.select(output));
433-
match *self.expect_output(output_type) {
434-
Some(OutputAssertion {
435-
expect: ref expected_output,
436-
fuzzy: true,
437-
}) if !observed.contains(expected_output) => {
438-
bail!(ErrorKind::OutputMismatch(
439-
output_type.to_string(),
440-
self.cmd.clone(),
441-
expected_output.clone(),
442-
observed.into(),
443-
));
444-
},
445-
Some(OutputAssertion {
446-
expect: ref expected_output,
447-
fuzzy: false,
448-
}) => {
449-
let differences = Changeset::new(expected_output.trim(), observed.trim(), "\n");
450-
if differences.distance > 0 {
451-
let nice_diff = diff::render(&differences)?;
452-
bail!(ErrorKind::ExactOutputMismatch(
453-
output_type.to_string(),
454-
self.cmd.clone(),
455-
nice_diff
456-
));
457-
}
458-
},
459-
_ => {},
398+
if let Some(ref ouput_assertion) = self.expect_stdout {
399+
ouput_assertion.execute(&output)
400+
.map_err(|e| ErrorKind::StdoutMismatch(self.cmd.clone(), e))?;
460401
}
461-
Ok(())
462-
}
463402

464-
/// Return a reference to the appropriate output assertion.
465-
fn expect_output(&self, output_type: OutputType) -> &Option<OutputAssertion> {
466-
match output_type {
467-
OutputType::StdOut => &self.expect_stdout,
468-
OutputType::StdErr => &self.expect_stderr,
403+
if let Some(ref ouput_assertion) = self.expect_stderr {
404+
ouput_assertion.execute(&output)
405+
.map_err(|e| ErrorKind::StderrMismatch(self.cmd.clone(), e))?;
469406
}
407+
408+
Ok(())
470409
}
471410

472411
/// Execute the command, check the assertions, and panic when they fail.

src/output.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use std::fmt;
2+
use std::process::Output;
3+
4+
use difference::Changeset;
5+
6+
use self::errors::*;
7+
pub use self::errors::{Error, ErrorKind};
8+
use diff;
9+
10+
#[derive(Debug, Clone)]
11+
pub struct OutputAssertion<T> {
12+
pub expect: String,
13+
pub fuzzy: bool,
14+
pub kind: T,
15+
}
16+
17+
impl<T: OutputType> OutputAssertion<T> {
18+
fn matches_fuzzy(&self, got: &str) -> Result<()> {
19+
if !got.contains(&self.expect) {
20+
bail!(ErrorKind::OutputMismatch(self.expect.clone(), got.into()));
21+
}
22+
23+
Ok(())
24+
}
25+
26+
fn matches_exact(&self, got: &str) -> Result<()> {
27+
let differences = Changeset::new(self.expect.trim(), got.trim(), "\n");
28+
29+
if differences.distance > 0 {
30+
let nice_diff = diff::render(&differences)?;
31+
bail!(ErrorKind::ExactOutputMismatch(nice_diff));
32+
}
33+
34+
Ok(())
35+
}
36+
37+
pub fn execute(&self, output: &Output) -> Result<()> {
38+
let observed = String::from_utf8_lossy(self.kind.select(output));
39+
40+
if self.fuzzy {
41+
self.matches_fuzzy(&observed)
42+
} else {
43+
self.matches_exact(&observed)
44+
}
45+
}
46+
}
47+
48+
49+
pub trait OutputType: fmt::Display {
50+
fn select<'a>(&self, o: &'a Output) -> &'a [u8];
51+
}
52+
53+
54+
#[derive(Debug, Clone, Copy)]
55+
pub struct StdOut;
56+
57+
impl fmt::Display for StdOut {
58+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59+
write!(f, "stdout")
60+
}
61+
}
62+
63+
impl OutputType for StdOut {
64+
fn select<'a>(&self, o: &'a Output) -> &'a [u8] {
65+
&o.stdout
66+
}
67+
}
68+
69+
70+
#[derive(Debug, Clone, Copy)]
71+
pub struct StdErr;
72+
73+
impl fmt::Display for StdErr {
74+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75+
write!(f, "stderr")
76+
}
77+
}
78+
79+
impl OutputType for StdErr {
80+
fn select<'a>(&self, o: &'a Output) -> &'a [u8] {
81+
&o.stderr
82+
}
83+
}
84+
85+
mod errors {
86+
error_chain! {
87+
foreign_links {
88+
// Io(::std::io::Error);
89+
Fmt(::std::fmt::Error);
90+
}
91+
errors {
92+
OutputMismatch(expected: String, got: String) {
93+
description("Output was not as expected")
94+
display("expected {:?}, got {:?}", expected, got)
95+
}
96+
ExactOutputMismatch(diff: String) {
97+
description("Output was not as expected")
98+
display("{}", diff)
99+
}
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)