use std::fmt;
use std::process;
use std::str;
use predicates;
use predicates::str::PredicateStrExt;
use predicates_core;
use predicates_tree::CaseTreeExt;
use errors::dump_buffer;
use errors::output_fmt;
pub trait OutputAssertExt {
fn assert(self) -> Assert;
}
impl OutputAssertExt for process::Output {
fn assert(self) -> Assert {
Assert::new(self)
}
}
impl<'c> OutputAssertExt for &'c mut process::Command {
fn assert(self) -> Assert {
let output = self.output().unwrap();
Assert::new(output).append_context("command", format!("{:?}", self))
}
}
pub struct Assert {
output: process::Output,
context: Vec<(&'static str, Box<fmt::Display>)>,
}
impl Assert {
pub fn new(output: process::Output) -> Self {
Self {
output,
context: vec![],
}
}
pub fn append_context<D>(mut self, name: &'static str, context: D) -> Self
where
D: fmt::Display + 'static,
{
self.context.push((name, Box::new(context)));
self
}
pub fn get_output(&self) -> &process::Output {
&self.output
}
pub fn success(self) -> Self {
if !self.output.status.success() {
let actual_code = self.output.status.code().unwrap_or_else(|| {
panic!(
"Unexpected failure.\ncode=<interrupted>\nstderr=```{}```\n{}",
dump_buffer(&self.output.stderr),
self
)
});
panic!(
"Unexpected failure.\ncode-{}\nstderr=```{}```\n{}",
actual_code,
dump_buffer(&self.output.stderr),
self
);
}
self
}
pub fn failure(self) -> Self {
if self.output.status.success() {
panic!("Unexpected success\n{}", self);
}
self
}
pub fn interrupted(self) -> Self {
if self.output.status.code().is_some() {
panic!("Unexpected completion\n{}", self);
}
self
}
pub fn code<I, P>(self, pred: I) -> Self
where
I: IntoCodePredicate<P>,
P: predicates_core::Predicate<i32>,
{
self.code_impl(&pred.into_code())
}
fn code_impl(self, pred: &predicates_core::Predicate<i32>) -> Self {
let actual_code = self.output
.status
.code()
.unwrap_or_else(|| panic!("Command interrupted\n{}", self));
if let Some(case) = pred.find_case(false, &actual_code) {
panic!("Unexpected return code, failed {}\n{}", case.tree(), self);
}
self
}
pub fn stdout<I, P>(self, pred: I) -> Self
where
I: IntoOutputPredicate<P>,
P: predicates_core::Predicate<[u8]>,
{
self.stdout_impl(&pred.into_output())
}
fn stdout_impl(self, pred: &predicates_core::Predicate<[u8]>) -> Self {
{
let actual = &self.output.stdout;
if let Some(case) = pred.find_case(false, &actual) {
panic!("Unexpected stdout, failed {}\n{}", case.tree(), self);
}
}
self
}
pub fn stderr<I, P>(self, pred: I) -> Self
where
I: IntoOutputPredicate<P>,
P: predicates_core::Predicate<[u8]>,
{
self.stderr_impl(&pred.into_output())
}
fn stderr_impl(self, pred: &predicates_core::Predicate<[u8]>) -> Self {
{
let actual = &self.output.stderr;
if let Some(case) = pred.find_case(false, &actual) {
panic!("Unexpected stderr, failed {}\n\n{}", case.tree(), self);
}
}
self
}
}
impl fmt::Display for Assert {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for &(ref name, ref context) in &self.context {
writeln!(f, "{}=`{}`", name, context)?;
}
output_fmt(&self.output, f)
}
}
impl fmt::Debug for Assert {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Assert")
.field("output", &self.output)
.finish()
}
}
pub trait IntoCodePredicate<P>
where
P: predicates_core::Predicate<i32>,
{
type Predicate;
fn into_code(self) -> P;
}
impl<P> IntoCodePredicate<P> for P
where
P: predicates_core::Predicate<i32>,
{
type Predicate = P;
fn into_code(self) -> Self::Predicate {
self
}
}
impl IntoCodePredicate<predicates::ord::EqPredicate<i32>> for i32 {
type Predicate = predicates::ord::EqPredicate<i32>;
fn into_code(self) -> Self::Predicate {
predicates::ord::eq(self)
}
}
impl IntoCodePredicate<predicates::iter::InPredicate<i32>> for Vec<i32> {
type Predicate = predicates::iter::InPredicate<i32>;
fn into_code(self) -> Self::Predicate {
predicates::iter::in_iter(self)
}
}
impl IntoCodePredicate<predicates::iter::InPredicate<i32>> for &'static [i32] {
type Predicate = predicates::iter::InPredicate<i32>;
fn into_code(self) -> Self::Predicate {
predicates::iter::in_iter(self.iter().cloned())
}
}
pub trait IntoOutputPredicate<P>
where
P: predicates_core::Predicate<[u8]>,
{
type Predicate;
fn into_output(self) -> P;
}
impl<P> IntoOutputPredicate<P> for P
where
P: predicates_core::Predicate<[u8]>,
{
type Predicate = P;
fn into_output(self) -> Self::Predicate {
self
}
}
#[derive(Debug)]
pub struct BytesContentOutputPredicate(predicates::ord::EqPredicate<&'static [u8]>);
impl BytesContentOutputPredicate {
pub(crate) fn new(value: &'static [u8]) -> Self {
let pred = predicates::ord::eq(value);
BytesContentOutputPredicate(pred)
}
}
impl predicates_core::reflection::PredicateReflection for BytesContentOutputPredicate {
fn parameters<'a>(
&'a self,
) -> Box<Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
self.0.parameters()
}
fn children<'a>(&'a self) -> Box<Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
self.0.children()
}
}
impl predicates_core::Predicate<[u8]> for BytesContentOutputPredicate {
fn eval(&self, item: &[u8]) -> bool {
self.0.eval(item)
}
fn find_case<'a>(
&'a self,
expected: bool,
variable: &[u8],
) -> Option<predicates_core::reflection::Case<'a>> {
self.0.find_case(expected, variable)
}
}
impl fmt::Display for BytesContentOutputPredicate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl IntoOutputPredicate<BytesContentOutputPredicate> for &'static [u8] {
type Predicate = BytesContentOutputPredicate;
fn into_output(self) -> Self::Predicate {
Self::Predicate::new(self)
}
}
#[derive(Debug, Clone)]
pub struct StrContentOutputPredicate(
predicates::str::Utf8Predicate<predicates::str::DifferencePredicate>,
);
impl StrContentOutputPredicate {
pub(crate) fn new(value: &'static str) -> Self {
let pred = predicates::str::similar(value).from_utf8();
StrContentOutputPredicate(pred)
}
}
impl predicates_core::reflection::PredicateReflection for StrContentOutputPredicate {
fn parameters<'a>(
&'a self,
) -> Box<Iterator<Item = predicates_core::reflection::Parameter<'a>> + 'a> {
self.0.parameters()
}
fn children<'a>(&'a self) -> Box<Iterator<Item = predicates_core::reflection::Child<'a>> + 'a> {
self.0.children()
}
}
impl predicates_core::Predicate<[u8]> for StrContentOutputPredicate {
fn eval(&self, item: &[u8]) -> bool {
self.0.eval(item)
}
fn find_case<'a>(
&'a self,
expected: bool,
variable: &[u8],
) -> Option<predicates_core::reflection::Case<'a>> {
self.0.find_case(expected, variable)
}
}
impl fmt::Display for StrContentOutputPredicate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl IntoOutputPredicate<StrContentOutputPredicate> for &'static str {
type Predicate = StrContentOutputPredicate;
fn into_output(self) -> Self::Predicate {
Self::Predicate::new(self)
}
}
#[cfg(test)]
mod test {
use super::*;
use predicates::prelude::*;
fn convert_code<I, P>(pred: I) -> P
where
I: IntoCodePredicate<P>,
P: predicates_core::Predicate<i32>,
{
pred.into_code()
}
#[test]
fn into_code_from_pred() {
let pred = convert_code(predicate::eq(10));
assert!(pred.eval(&10));
}
#[test]
fn into_code_from_i32() {
let pred = convert_code(10);
assert!(pred.eval(&10));
}
#[test]
fn into_code_from_vec() {
let pred = convert_code(vec![3, 10]);
assert!(pred.eval(&10));
}
#[test]
fn into_code_from_array() {
let pred = convert_code(&[3, 10] as &[i32]);
assert!(pred.eval(&10));
}
fn convert_output<I, P>(pred: I) -> P
where
I: IntoOutputPredicate<P>,
P: predicates_core::Predicate<[u8]>,
{
pred.into_output()
}
#[test]
fn into_output_from_pred() {
let pred = convert_output(predicate::eq(b"Hello" as &[u8]));
assert!(pred.eval(b"Hello" as &[u8]));
}
#[test]
fn into_output_from_bytes() {
let pred = convert_output(b"Hello" as &[u8]);
assert!(pred.eval(b"Hello" as &[u8]));
}
#[test]
fn into_output_from_str() {
let pred = convert_output("Hello");
assert!(pred.eval(b"Hello" as &[u8]));
}
}