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

Refactor output assertions #24

Merged
merged 2 commits into from
Sep 8, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 16 additions & 79 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,17 @@ extern crate difference;
#[macro_use] extern crate error_chain;
extern crate rustc_serialize;

use std::process::{Command, Output};
use std::fmt;

use difference::Changeset;
use std::process::Command;

mod errors;
use errors::*;

#[macro_use] mod macros;
pub use macros::flatten_escaped_string;

mod output;
use output::{OutputAssertion, StdErr, StdOut};

mod diff;

/// Assertions for a specific command.
Expand All @@ -127,38 +127,8 @@ pub struct Assert {
cmd: Vec<String>,
expect_success: Option<bool>,
expect_exit_code: Option<i32>,
expect_stdout: Option<OutputAssertion>,
expect_stderr: Option<OutputAssertion>,
}

#[derive(Debug)]
struct OutputAssertion {
expect: String,
fuzzy: bool,
}

#[derive(Debug, Copy, Clone)]
enum OutputType {
StdOut,
StdErr,
}

impl OutputType {
fn select<'a>(&self, o: &'a Output) -> &'a [u8] {
match *self {
OutputType::StdOut => &o.stdout,
OutputType::StdErr => &o.stderr,
}
}
}

impl fmt::Display for OutputType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
OutputType::StdOut => write!(f, "stdout"),
OutputType::StdErr => write!(f, "stderr"),
}
}
expect_stdout: Option<OutputAssertion<StdOut>>,
expect_stderr: Option<OutputAssertion<StdErr>>,
}

impl std::default::Default for Assert {
Expand Down Expand Up @@ -318,6 +288,7 @@ impl Assert {
self.expect_stdout = Some(OutputAssertion {
expect: output.into(),
fuzzy: true,
kind: StdOut,
});
self
}
Expand All @@ -337,6 +308,7 @@ impl Assert {
self.expect_stdout = Some(OutputAssertion {
expect: output.into(),
fuzzy: false,
kind: StdOut,
});
self
}
Expand All @@ -358,6 +330,7 @@ impl Assert {
self.expect_stderr = Some(OutputAssertion {
expect: output.into(),
fuzzy: true,
kind: StdErr,
});
self
}
Expand All @@ -379,6 +352,7 @@ impl Assert {
self.expect_stderr = Some(OutputAssertion {
expect: output.into(),
fuzzy: false,
kind: StdErr,
});
self
}
Expand Down Expand Up @@ -421,52 +395,15 @@ impl Assert {
));
}

self.assert_output(OutputType::StdOut, &output)?;
self.assert_output(OutputType::StdErr, &output)?;

Ok(())
}

/// Perform the appropriate output assertion.
fn assert_output(&self, output_type: OutputType, output: &Output) -> Result<()> {
let observed = String::from_utf8_lossy(output_type.select(output));
match *self.expect_output(output_type) {
Some(OutputAssertion {
expect: ref expected_output,
fuzzy: true,
}) if !observed.contains(expected_output) => {
bail!(ErrorKind::OutputMismatch(
output_type.to_string(),
self.cmd.clone(),
expected_output.clone(),
observed.into(),
));
},
Some(OutputAssertion {
expect: ref expected_output,
fuzzy: false,
}) => {
let differences = Changeset::new(expected_output.trim(), observed.trim(), "\n");
if differences.distance > 0 {
let nice_diff = diff::render(&differences)?;
bail!(ErrorKind::ExactOutputMismatch(
output_type.to_string(),
self.cmd.clone(),
nice_diff
));
}
},
_ => {},
if let Some(ouput_assertion) = self.expect_stdout {
ouput_assertion.execute(&output)?;
Copy link
Collaborator Author

@killercup killercup Mar 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Do error-chain magic here, cf. #24 (comment))

}
Ok(())
}

/// Return a reference to the appropriate output assertion.
fn expect_output(&self, output_type: OutputType) -> &Option<OutputAssertion> {
match output_type {
OutputType::StdOut => &self.expect_stdout,
OutputType::StdErr => &self.expect_stderr,
if let Some(ouput_assertion) = self.expect_stderr {
ouput_assertion.execute(&output)?;
}

Ok(())
}

/// Execute the command, check the assertions, and panic when they fail.
Expand Down
91 changes: 91 additions & 0 deletions src/output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use std::fmt;
use std::process::Output;

use difference::Changeset;

use errors::*;
use diff;

#[derive(Debug, Clone)]
pub struct OutputAssertion<T> {
pub expect: String,
pub fuzzy: bool,
pub kind: T,
}

impl<T: OutputType> OutputAssertion<T> {
fn matches_fuzzy(&self, got: &str) -> Result<()> {
if !got.contains(&self.expect) {
bail!(ErrorKind::OutputMismatch(
self.kind.to_string(),
vec!["Foo".to_string()],
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Placeholder for now. I think we can use error_chain to add the usual "Assert CLI command {cmd} failed:" prefix at the call site.

self.expect.clone(),
got.into(),
));
}

Ok(())
}

fn matches_exact(&self, got: &str) -> Result<()> {
let differences = Changeset::new(self.expect.trim(), got.trim(), "\n");

if differences.distance > 0 {
let nice_diff = diff::render(&differences)?;
bail!(ErrorKind::ExactOutputMismatch(
self.kind.to_string(),
vec!["Foo".to_string()],
nice_diff
));
}

Ok(())
}

pub fn execute(&self, output: &Output) -> Result<()> {
let observed = String::from_utf8_lossy(self.kind.select(output));

if self.fuzzy {
self.matches_fuzzy(&observed)
} else {
self.matches_exact(&observed)
}
}
}


pub trait OutputType: fmt::Display {
fn select<'a>(&self, o: &'a Output) -> &'a [u8];
}


#[derive(Debug, Clone, Copy)]
pub struct StdOut;

impl fmt::Display for StdOut {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "stdout")
}
}

impl OutputType for StdOut {
fn select<'a>(&self, o: &'a Output) -> &'a [u8] {
&o.stdout
}
}


#[derive(Debug, Clone, Copy)]
pub struct StdErr;

impl fmt::Display for StdErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "stderr")
}
}

impl OutputType for StdErr {
fn select<'a>(&self, o: &'a Output) -> &'a [u8] {
&o.stderr
}
}