use std::fmt::{Debug, Display, Formatter, Result as FormatResult};
#[macro_export]
macro_rules! assert_that {
( $actual: expr, panics ) => {{
let result = std::panic::catch_unwind(|| { $actual; });
if result.is_ok() {
panic!("\nFailed assertion; expected expression to panic")
}
}};
( $actual: expr, does not panic ) => {
let result = std::panic::catch_unwind(|| { $actual; });
if result.is_err() {
panic!("\nFailed assertion; expression panicked unexpectantly")
}
};
( $actual: expr) => {{
if !$actual {
panic!("\nFailed assertion; '{}' is not true", stringify!($actual));
}
}};
( $actual: expr , otherwise $reason: expr ) => {{
if !$actual {
panic!("\nFailed assertion; expression '{}' is not true,\n Because: {}",
stringify!($actual), $reason
);
}
}};
( $actual: expr, $matcher: expr ) => {{
#[allow(unused_imports)]
use galvanic_assert::{MatchResult, Matcher};
let value = $actual;
let m = $matcher;
match m.check(value) {
MatchResult::Matched { .. } => { },
MatchResult::Failed { name, reason } => {
panic!("\nFailed assertion of matcher: {}\n{}", name, reason)
}
}
}};
}
#[macro_export]
macro_rules! get_expectation_for {
( $actual: expr, panics ) => {{
use galvanic_assert::Expectation;
let result = std::panic::catch_unwind(|| { $actual; });
if result.is_ok() {
let assertion = format!("'{}, panics'", stringify!($actual));
Expectation::failed(assertion, file!().to_string(), line!(),
"Expected expression to panic".to_string()
)
} else {
Expectation::satisfied()
}
}};
( $actual: expr, does not panic ) => {{
use galvanic_assert::Expectation;
let result = std::panic::catch_unwind(|| { $actual; });
if result.is_err() {
let assertion = format!("'{}, does not panic'", stringify!($actual));
Expectation::failed(assertion, file!().to_string(), line!(),
"Expression panicked unexpectantly".to_string()
)
} else { Expectation::satisfied() }
}};
( $actual: expr) => {{
use galvanic_assert::Expectation;
if !$actual {
let assertion = format!("'{}' is true", stringify!($actual));
Expectation::failed(assertion, file!().to_string(), line!(),
format!("'{}' is not true", stringify!($actual))
)
} else { Expectation::satisfied() }
}};
( $actual: expr , otherwise $reason: expr ) => {{
use galvanic_assert::Expectation;
if !$actual {
let assertion = format!("'{}' is true", stringify!($actual));
Expectation::failed(assertion, file!().to_string(), line!(),
format!("'{}' is not true,\n\tBecause: {}",
stringify!($actual), $reason)
)
} else { Expectation::satisfied() }
}};
( $actual: expr, $matcher: expr ) => {{
#[allow(unused_imports)]
use galvanic_assert::{Expectation, MatchResult, Matcher};
let value = $actual;
let m = $matcher;
match m.check(value) {
MatchResult::Matched { .. } => { Expectation::satisfied() },
MatchResult::Failed { name, reason } => {
let assertion = format!("'{}' matches '{}'", stringify!($actual), stringify!($matcher));
Expectation::failed(assertion, file!().to_string(), line!(),
format!("Failed assertion of matcher: {}\n{}", name, reason)
)
}
}
}};
}
#[macro_export]
macro_rules! expect_that {
( $actual: expr, panics ) => { #[allow(unused_variables)] let expectation = get_expectation_for!($actual, panics); };
( $actual: expr, does not panic ) => { #[allow(unused_variables)] let expectation = get_expectation_for!($actual, does not panic); };
( $actual: expr) => { #[allow(unused_variables)] let expectation = get_expectation_for!($actual); };
( $actual: expr , otherwise $reason: expr ) => { #[allow(unused_variables)] let expectation = get_expectation_for!($actual, otherwise $reason); };
( $actual: expr, $matcher: expr ) => { #[allow(unused_variables)] let expectation = get_expectation_for!($actual, $matcher); };
}
pub trait Matcher<'a, T:'a> {
fn check(&self, actual: &'a T) -> MatchResult;
}
impl<'a, T:'a, F> Matcher<'a,T> for F
where F: Fn(&'a T) -> MatchResult + ?Sized {
fn check(&self, actual: &'a T) -> MatchResult {
self(actual)
}
}
pub enum MatchResult {
Matched {
name: String
},
Failed {
name: String,
reason: String
}
}
impl std::convert::From<MatchResult> for bool {
fn from(result: MatchResult) -> bool {
match result {
MatchResult::Matched {..} => true,
MatchResult::Failed {..} => false
}
}
}
impl<'a> std::convert::From<&'a MatchResult> for bool {
fn from(result: &'a MatchResult) -> bool {
match result {
&MatchResult::Matched {..} => true,
&MatchResult::Failed {..} => false
}
}
}
pub struct MatchResultBuilder {
matcher_name: String
}
impl MatchResultBuilder {
pub fn new() -> MatchResultBuilder {
MatchResultBuilder {
matcher_name: "_unknown_".to_owned()
}
}
pub fn for_(name: &str) -> MatchResultBuilder {
MatchResultBuilder {
matcher_name: name.to_owned()
}
}
pub fn matched(self) -> MatchResult {
MatchResult::Matched { name: self.matcher_name }
}
pub fn failed_because(self, reason: &str) -> MatchResult {
MatchResult::Failed {
name: self.matcher_name,
reason: format!(" Because: {}", reason)
}
}
pub fn failed_comparison<T: Debug>(self, actual: &T, expected: &T) -> MatchResult {
MatchResult::Failed {
name: self.matcher_name,
reason: format!(" Expected: {:?}\n Got: {:?}", expected, actual)
}
}
}
pub enum Expectation {
Failed {
assertion: String,
file: String,
line: u32,
error_msg: String
},
Satisfied
}
impl Expectation {
pub fn failed(assertion:String, file: String, line: u32, error_msg: String) -> Expectation {
Expectation::Failed {
assertion: assertion,
file: file,
line: line,
error_msg: error_msg
}
}
pub fn satisfied() -> Expectation {
Expectation::Satisfied
}
pub fn verify(self) { }
}
impl Drop for Expectation {
fn drop(&mut self) {
if let &mut Expectation::Failed { .. } = self {
eprintln!("{}", self);
if !std::thread::panicking() {
panic!("Some expectations failed.")
}
}
}
}
impl Display for Expectation {
fn fmt(&self, f: &mut Formatter) -> FormatResult {
match self {
&Expectation::Failed { ref assertion, ref file, ref line, ref error_msg } => {
write!(f, "Expectation '{}' failed, originating from {}:{}\n\t{}",
assertion, file, line, error_msg
)
},
_ => write!(f, "The expectation has been satisfied")
}
}
}
pub mod matchers;
#[cfg(test)]
mod test {
use super::*;
#[test]
fn should_bool() {
let matched = MatchResultBuilder::new().matched();
let failed = MatchResultBuilder::new().failed_because("");
let flag: bool = matched.into();
assert!(flag);
let flag: bool = failed.into();
assert!(!flag);
}
}