use std::fmt::Debug;
std::thread_local! {
pub static CURRENT_TEST_NAME: std::cell::RefCell<Option<String>> = const { std::cell::RefCell::new(None) };
}
pub fn set_current_test_name(name: Option<String>) {
CURRENT_TEST_NAME.with(|cell| {
*cell.borrow_mut() = name;
});
}
fn get_test_name() -> Option<String> {
CURRENT_TEST_NAME.with(|cell| cell.borrow().clone())
}
fn format_header(location: &str) -> String {
if let Some(name) = get_test_name() {
format!("\nTest: \"{name}\"\n at {location}\n")
} else {
format!("\nassertion failed at {location}\n")
}
}
pub struct Expect<T> {
value: T,
location: &'static str,
}
impl<T> Expect<T> {
pub fn new(value: T, location: &'static str) -> Self {
Self { value, location }
}
}
impl<T: Debug + PartialEq> Expect<T> {
pub fn to_equal(&self, expected: T) {
if self.value != expected {
panic!(
"{}\n expect!(actual).to_equal(expected)\n\n Expected: {:?}\n Received: {:?}\n",
format_header(self.location),
expected,
self.value
);
}
}
pub fn to_not_equal(&self, unexpected: T) {
if self.value == unexpected {
panic!(
"{}\n expect!(actual).to_not_equal(value)\n\n Expected NOT: {:?}\n Received: {:?}\n",
format_header(self.location),
unexpected,
self.value
);
}
}
}
impl Expect<bool> {
pub fn to_be_true(&self) {
if !self.value {
panic!(
"{}\n expect!(value).to_be_true()\n\n Expected: true\n Received: false\n",
format_header(self.location)
);
}
}
pub fn to_be_false(&self) {
if self.value {
panic!(
"{}\n expect!(value).to_be_false()\n\n Expected: false\n Received: true\n",
format_header(self.location)
);
}
}
}
impl<T: Debug> Expect<Option<T>> {
pub fn to_be_some(&self) {
if self.value.is_none() {
panic!(
"{}\n expect!(option).to_be_some()\n\n Expected: Some(_)\n Received: None\n",
format_header(self.location)
);
}
}
pub fn to_be_none(&self) {
if let Some(ref v) = self.value {
panic!(
"{}\n expect!(option).to_be_none()\n\n Expected: None\n Received: Some({:?})\n",
format_header(self.location),
v
);
}
}
}
impl<T: Debug + PartialEq> Expect<Option<T>> {
pub fn to_contain_value(&self, expected: T) {
match &self.value {
Some(v) if *v == expected => {}
Some(v) => {
panic!(
"{}\n expect!(option).to_contain_value(expected)\n\n Expected: Some({:?})\n Received: Some({:?})\n",
format_header(self.location),
expected,
v
);
}
None => {
panic!(
"{}\n expect!(option).to_contain_value(expected)\n\n Expected: Some({:?})\n Received: None\n",
format_header(self.location),
expected
);
}
}
}
}
impl<T: Debug, E: Debug> Expect<Result<T, E>> {
pub fn to_be_ok(&self) {
if let Err(ref e) = self.value {
panic!(
"{}\n expect!(result).to_be_ok()\n\n Expected: Ok(_)\n Received: Err({:?})\n",
format_header(self.location),
e
);
}
}
pub fn to_be_err(&self) {
if let Ok(ref v) = self.value {
panic!(
"{}\n expect!(result).to_be_err()\n\n Expected: Err(_)\n Received: Ok({:?})\n",
format_header(self.location),
v
);
}
}
}
impl Expect<String> {
pub fn to_contain(&self, substring: &str) {
if !self.value.contains(substring) {
panic!(
"{}\n expect!(string).to_contain(substring)\n\n Expected to contain: {:?}\n Received: {:?}\n",
format_header(self.location),
substring,
self.value
);
}
}
pub fn to_start_with(&self, prefix: &str) {
if !self.value.starts_with(prefix) {
panic!(
"{}\n expect!(string).to_start_with(prefix)\n\n Expected to start with: {:?}\n Received: {:?}\n",
format_header(self.location),
prefix,
self.value
);
}
}
pub fn to_end_with(&self, suffix: &str) {
if !self.value.ends_with(suffix) {
panic!(
"{}\n expect!(string).to_end_with(suffix)\n\n Expected to end with: {:?}\n Received: {:?}\n",
format_header(self.location),
suffix,
self.value
);
}
}
pub fn to_have_length(&self, expected: usize) {
let actual = self.value.len();
if actual != expected {
panic!(
"{}\n expect!(string).to_have_length({})\n\n Expected length: {}\n Actual length: {}\n Value: {:?}\n",
format_header(self.location),
expected,
expected,
actual,
self.value
);
}
}
pub fn to_be_empty(&self) {
if !self.value.is_empty() {
panic!(
"{}\n expect!(string).to_be_empty()\n\n Expected: \"\"\n Received: {:?}\n",
format_header(self.location),
self.value
);
}
}
}
impl Expect<&str> {
pub fn to_contain(&self, substring: &str) {
if !self.value.contains(substring) {
panic!(
"{}\n expect!(string).to_contain(substring)\n\n Expected to contain: {:?}\n Received: {:?}\n",
format_header(self.location),
substring,
self.value
);
}
}
pub fn to_start_with(&self, prefix: &str) {
if !self.value.starts_with(prefix) {
panic!(
"{}\n expect!(string).to_start_with(prefix)\n\n Expected to start with: {:?}\n Received: {:?}\n",
format_header(self.location),
prefix,
self.value
);
}
}
pub fn to_end_with(&self, suffix: &str) {
if !self.value.ends_with(suffix) {
panic!(
"{}\n expect!(string).to_end_with(suffix)\n\n Expected to end with: {:?}\n Received: {:?}\n",
format_header(self.location),
suffix,
self.value
);
}
}
pub fn to_have_length(&self, expected: usize) {
let actual = self.value.len();
if actual != expected {
panic!(
"{}\n expect!(string).to_have_length({})\n\n Expected length: {}\n Actual length: {}\n Value: {:?}\n",
format_header(self.location),
expected,
expected,
actual,
self.value
);
}
}
pub fn to_be_empty(&self) {
if !self.value.is_empty() {
panic!(
"{}\n expect!(string).to_be_empty()\n\n Expected: \"\"\n Received: {:?}\n",
format_header(self.location),
self.value
);
}
}
}
impl<T: Debug + PartialEq> Expect<Vec<T>> {
pub fn to_have_length(&self, expected: usize) {
let actual = self.value.len();
if actual != expected {
panic!(
"{}\n expect!(vec).to_have_length({})\n\n Expected length: {}\n Actual length: {}\n",
format_header(self.location),
expected,
expected,
actual
);
}
}
pub fn to_contain(&self, item: &T) {
if !self.value.contains(item) {
panic!(
"{}\n expect!(vec).to_contain(item)\n\n Expected to contain: {:?}\n Received: {:?}\n",
format_header(self.location),
item,
self.value
);
}
}
pub fn to_be_empty(&self) {
if !self.value.is_empty() {
panic!(
"{}\n expect!(vec).to_be_empty()\n\n Expected: []\n Received: {:?}\n",
format_header(self.location),
self.value
);
}
}
}
#[allow(clippy::neg_cmp_op_on_partial_ord)]
impl<T: Debug + PartialOrd> Expect<T> {
pub fn to_be_greater_than(&self, expected: T) {
if !(self.value > expected) {
panic!(
"{}\n expect!(value).to_be_greater_than(expected)\n\n Expected: > {:?}\n Received: {:?}\n",
format_header(self.location),
expected,
self.value
);
}
}
pub fn to_be_less_than(&self, expected: T) {
if !(self.value < expected) {
panic!(
"{}\n expect!(value).to_be_less_than(expected)\n\n Expected: < {:?}\n Received: {:?}\n",
format_header(self.location),
expected,
self.value
);
}
}
pub fn to_be_greater_than_or_equal(&self, expected: T) {
if !(self.value >= expected) {
panic!(
"{}\n expect!(value).to_be_greater_than_or_equal(expected)\n\n Expected: >= {:?}\n Received: {:?}\n",
format_header(self.location),
expected,
self.value
);
}
}
pub fn to_be_less_than_or_equal(&self, expected: T) {
if !(self.value <= expected) {
panic!(
"{}\n expect!(value).to_be_less_than_or_equal(expected)\n\n Expected: <= {:?}\n Received: {:?}\n",
format_header(self.location),
expected,
self.value
);
}
}
}