use crate::messages::MessageType;
use crate::runners::act::Act;
use crate::{Assert, Message, PanicWhen};
use std::panic;
use std::panic::AssertUnwindSafe;
const NO_PANIC_ERROR: &str = "Code has not panicked";
const PANIC_ERROR: &str = "Code has panicked";
pub struct Runner<'a, T, E> {
arrange_function: Box<Fn(&mut Message) -> T + 'a>,
actions: Vec<Act<'a, T, E>>,
}
impl<'a, T, E> Runner<'a, T, E> {
pub fn arrange(function: impl Fn(&mut Message) -> T + 'a) -> Self {
Self {
arrange_function: Box::new(function),
actions: Vec::new(),
}
}
pub fn act(
&mut self,
action: impl Fn(&mut Message, &mut T) + 'a,
expectation: impl Fn() -> E + 'a,
) -> &mut Self {
self.actions.push(Act::new(action, expectation));
self
}
pub fn act_panic(
&self,
when: PanicWhen,
function: impl Fn(&mut Message, &mut T) + 'a,
) -> &Self {
let mut message = Message::new();
let mut value = (self.arrange_function)(&mut message);
message.set_current_type(MessageType::Act);
let result = panic::catch_unwind(AssertUnwindSafe(|| {
function(&mut message, &mut value);
}));
if result.is_ok() == Self::should_panic(when) {
message.set_current_type(MessageType::Assert);
message.set("** Skipped **");
if result.is_ok() {
panic!(Self::format_error_message(&message, NO_PANIC_ERROR));
} else {
panic!(Self::format_error_message(&message, PANIC_ERROR));
}
}
self
}
pub fn assert<A: Assert>(
&self,
function: impl Fn(&mut Message, T, E) -> A + 'a,
) -> &Self {
for act in &self.actions {
let mut message = Message::new();
let mut value = (self.arrange_function)(&mut message);
message.set_current_type(MessageType::Act);
act.execute(&mut message, &mut value);
let expected = act.expect();
message.set_current_type(MessageType::Assert);
let result = function(&mut message, value, expected);
if !result.success() {
panic!(Self::format_error_message(
&message,
&result.error_message()
));
}
}
self
}
pub fn assert_panic<A: Assert>(
&self,
when: PanicWhen,
function: impl Fn(&mut Message, T, E) -> A + 'a,
) -> &Self {
for act in &self.actions {
let mut message = Message::new();
let mut value = (self.arrange_function)(&mut message);
message.set_current_type(MessageType::Act);
act.execute(&mut message, &mut value);
let expected = act.expect();
message.set_current_type(MessageType::Assert);
let result = panic::catch_unwind(AssertUnwindSafe(|| {
function(&mut message, value, expected)
}));
if result.is_ok() == Self::should_panic(when) {
if result.is_ok() {
panic!(Self::format_error_message(
&message,
NO_PANIC_ERROR
));
} else {
panic!(Self::format_error_message(&message, PANIC_ERROR));
}
}
}
self
}
fn format_error_message(message: &Message, error: &str) -> String {
"\n--------------------------------------------\n".to_string()
+ &format!("Arrange:\n {}\n", message.arrange_message())
+ &format!("Act:\n {}\n", message.act_message())
+ &format!("Assert:\n {}\n", message.assert_message())
+ &format!("Error:\n {}\n", error.replace("\n", "\n "))
+ "--------------------------------------------\n"
}
fn should_panic(panic_when: PanicWhen) -> bool {
#[cfg(debug_assertions)]
match panic_when {
PanicWhen::Debug | PanicWhen::DebugAndRelease => true,
PanicWhen::Release => false,
}
#[cfg(not(debug_assertions))]
match panic_when {
PanicWhen::Release | PanicWhen::DebugAndRelease => true,
PanicWhen::Debug => false,
}
}
}
#[cfg(test)]
mod tests {
use crate::{Equal, PanicWhen, Runner};
const VALUE_EXAMPLE: usize = 10;
struct Expected {
value: usize,
}
#[test]
fn test_ok() {
Runner::arrange(|message| {
message.set("Initialization");
0
})
.act(
|message, value| {
message.set("Action");
*value = VALUE_EXAMPLE
},
|| Expected {
value: VALUE_EXAMPLE,
},
)
.act_panic(PanicWhen::Debug, |message, value| {
message.set("Other action");
*value -= 1;
})
.assert_panic(PanicWhen::Debug, |message, mut _value, _expected| {
message.set("Evaluation");
_value -= VALUE_EXAMPLE + 1;
Equal::new(0, 0)
})
.assert(|message, value, expected| {
message.set("Other evaluation");
Equal::new(value, expected.value)
});
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn test_act_panic_fail() {
Runner::arrange(|message| {
message.set("Initialization");
0
})
.act(
|message, value| {
message.set("Action");
*value = VALUE_EXAMPLE
},
|| Expected {
value: VALUE_EXAMPLE,
},
)
.act_panic(PanicWhen::Debug, |message, value| {
message.set("Other action");
*value -= 0;
})
.assert_panic(PanicWhen::Debug, |message, mut _value, _expected| {
message.set("Evaluation");
_value -= VALUE_EXAMPLE + 1;
Equal::new(0, 0)
})
.assert(|message, value, expected| {
message.set("Other evaluation");
Equal::new(value, expected.value)
});
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn test_assert_panic_fail() {
Runner::arrange(|message| {
message.set("Initialization");
0
})
.act(
|message, value| {
message.set("Action");
*value = VALUE_EXAMPLE
},
|| Expected {
value: VALUE_EXAMPLE,
},
)
.act_panic(PanicWhen::Debug, |message, value| {
message.set("Other action");
*value -= 1;
})
.assert_panic(PanicWhen::Debug, |message, mut _value, _expected| {
message.set("Evaluation");
_value -= 0;
Equal::new(0, 0)
})
.assert(|message, value, expected| {
message.set("Other evaluation");
Equal::new(value, expected.value)
});
}
#[test]
#[should_panic]
fn test_assert_fail() {
Runner::arrange(|message| {
message.set("Initialization");
0
})
.act(
|message, value| {
message.set("Action");
*value = VALUE_EXAMPLE
},
|| Expected {
value: VALUE_EXAMPLE,
},
)
.act_panic(PanicWhen::Debug, |message, value| {
message.set("Other action");
*value -= 1;
})
.assert_panic(PanicWhen::Debug, |message, mut _value, _expected| {
message.set("Evaluation");
_value -= VALUE_EXAMPLE + 1;
Equal::new(0, 0)
})
.assert(|message, value, expected| {
message.set("Other evaluation");
Equal::new(value, expected.value + 1)
});
}
}