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

Commit 73c6004

Browse files
committed
feat(assert_output): Basic CLI Output Diff
1 parent 1e04c94 commit 73c6004

File tree

3 files changed

+193
-2
lines changed

3 files changed

+193
-2
lines changed

src/cli_error.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use std::error::Error;
2+
use std::fmt;
3+
4+
use diff::render as render_diff;
5+
use std::process::Output;
6+
use difference::Difference;
7+
8+
pub enum CliError {
9+
NoSuccess(Output),
10+
OutputMissmatch(Vec<Difference>),
11+
}
12+
13+
impl fmt::Display for CliError {
14+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
15+
write!(f, "{:?}", self)
16+
}
17+
}
18+
19+
impl fmt::Debug for CliError {
20+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21+
match *self {
22+
CliError::NoSuccess(ref output) => write!(f,
23+
"Non-success error code {code:?} with this stderr:\n{stderr}",
24+
code = output.status.code(),
25+
stderr = String::from_utf8_lossy(&output.stderr)),
26+
CliError::OutputMissmatch(ref diff) => {
27+
let diff = match render_diff(&diff) {
28+
Ok(diff) => diff,
29+
Err(_) => return Err(fmt::Error),
30+
};
31+
write!(f, "Output was not as expected:\n{}", diff)
32+
}
33+
}
34+
}
35+
}
36+
37+
impl Error for CliError {
38+
fn description(&self) -> &str {
39+
match *self {
40+
CliError::NoSuccess(_) => "Command return non-success error code.",
41+
CliError::OutputMissmatch(_) => "Command output was not as expected.",
42+
}
43+
}
44+
}

src/diff.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use std::fmt;
2+
3+
use difference::Difference;
4+
use ansi_term::Colour::{Green, Red};
5+
6+
pub fn render(changeset: &[Difference]) -> Result<String, fmt::Error> {
7+
use std::fmt::Write;
8+
9+
let mut t = String::new();
10+
11+
for change in changeset {
12+
match *change {
13+
Difference::Same(ref x) => {
14+
for line in x.lines() {
15+
try!(writeln!(t, " {}", line));
16+
}
17+
}
18+
Difference::Add(ref x) => {
19+
try!(write!(t, "{}", Green.paint("+")));
20+
try!(writeln!(t, "{}", Green.paint(x)));
21+
}
22+
Difference::Rem(ref x) => {
23+
try!(write!(t, "{}", Red.paint("-")));
24+
try!(writeln!(t, "{}", Red.paint(x)));
25+
}
26+
}
27+
}
28+
29+
Ok(t)
30+
}
31+
32+
#[cfg(test)]
33+
mod tests {
34+
use difference::diff;
35+
use super::*;
36+
37+
#[test]
38+
fn basic_diff() {
39+
let (_, diff) = diff("lol", "yay", "\n");
40+
assert_eq!(render(&diff).unwrap(),
41+
"\u{1b}[31m-\u{1b}[0m\u{1b}[31mlol\u{1b}[0m\n\u{1b}[32m+\u{1b}[0m\u{1b}[32myay\
42+
\u{1b}[0m\n")
43+
}
44+
45+
#[test]
46+
fn multiline_diff() {
47+
let (_, diff) = diff("Lorem ipsum dolor sit amet, consectetur adipisicing elit,
48+
sed do eiusmod tempor incididunt ut labore et dolore magna
49+
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
50+
ullamco laboris nisi ut aliquip ex ea commodo consequat.",
51+
"Lorem ipsum dolor sit amet, consectetur adipisicing elit,
52+
sed do eiusmod tempor **incididunt** ut labore et dolore magna
53+
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
54+
ullamco laboris nisi ut aliquip ex ea commodo consequat.",
55+
"\n");
56+
assert_eq!(render(&diff).unwrap(), " Lorem ipsum dolor sit amet, consectetur adipisicing elit,\n\u{1b}[31m-\u{1b}[0m\u{1b}[31msed 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 **incididunt** ut labore et dolore magna\u{1b}[0m\n aliqua. Ut enim ad minim veniam, quis nostrud exercitation\n ullamco laboris nisi ut aliquip ex ea commodo consequat.\n");
57+
}
58+
}

src/lib.rs

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,92 @@
1-
#[test]
2-
fn it_works() {
1+
//! # Test CLI Applications
2+
//!
3+
//! Currently, this crate only includes basic functionality to check the output of a child process
4+
//! is as expected.
5+
//!
6+
//! ## Example
7+
//!
8+
//! Here's a trivial example:
9+
//!
10+
//! ```rust
11+
//! extern crate assert_cli;
12+
//! assert_cli::assert_cli_output("echo", &["42"], "42").unwrap();
13+
//! ```
14+
//!
15+
//! And here is one that will fail:
16+
//!
17+
//! ```rust,should_panic
18+
//! extern crate assert_cli;
19+
//! assert_cli::assert_cli_output("echo", &["42"], "1337").unwrap();
20+
//! ```
21+
//!
22+
//! this will show a nice, colorful diff in your terminal, like this:
23+
//!
24+
//! ```diff
25+
//! -1337
26+
//! +42
27+
//! ```
28+
29+
#![cfg_attr(feature = "dev", feature(plugin))]
30+
#![cfg_attr(feature = "dev", plugin(clippy))]
31+
32+
#![deny(missing_docs)]
33+
34+
extern crate ansi_term;
35+
extern crate difference;
36+
37+
use std::process::{Command, Output};
38+
use std::error::Error;
39+
use std::ffi::OsStr;
40+
41+
mod cli_error;
42+
mod diff;
43+
44+
use cli_error::CliError;
45+
46+
/// Assert a CLI call returns the expected output.
47+
///
48+
/// To test that
49+
///
50+
/// ```sh
51+
/// ls -n1 src/
52+
/// ```
53+
///
54+
/// returns
55+
///
56+
/// ```plain
57+
/// cli_error.rs
58+
/// diff.rs
59+
/// lib.rs
60+
/// ```
61+
///
62+
/// you would call it like this:
63+
///
64+
/// ```rust,no_run
65+
/// # extern crate assert_cli;
66+
/// assert_cli::assert_cli_output("ls", &["-n1", "src/"], "cli_error.rs\ndiff.rs\nlib.rs");
67+
/// ```
68+
pub fn assert_cli_output<S>(cmd: &str, args: &[S], expected_output: &str) -> Result<(), Box<Error>>
69+
where S: AsRef<OsStr>
70+
{
71+
let call: Result<Output, Box<Error>> = Command::new(cmd)
72+
.args(args)
73+
.output()
74+
.map_err(From::from);
75+
76+
call.and_then(|output| {
77+
if !output.status.success() {
78+
return Err(From::from(CliError::NoSuccess(output)));
79+
}
80+
81+
let stdout = String::from_utf8_lossy(&output.stdout);
82+
let (distance, changes) = difference::diff(expected_output.trim(),
83+
&stdout.trim(),
84+
"\n");
85+
if distance > 0 {
86+
return Err(From::from(CliError::OutputMissmatch(changes)));
87+
}
88+
89+
Ok(())
90+
})
91+
.map_err(From::from)
392
}

0 commit comments

Comments
 (0)