#[cfg(feature = "parameterized")]
pub use extel_parameterized::parameters;
pub mod prelude {
pub use crate::{
cmd, err, errors::Error, extel_assert, fail, init_test_suite, pass, ExtelResult,
RunnableTestSet, TestConfig,
};
#[cfg(feature = "parameterized")]
pub use extel_parameterized::parameters;
}
use errors::Error;
use std::io::{BufWriter, Write};
pub mod errors;
#[doc(hidden)]
pub mod macros;
pub type ExtelResult = Result<(), Error>;
#[derive(Debug)]
pub enum TestStatus {
Single(ExtelResult),
Parameterized(Vec<ExtelResult>),
}
pub trait GenericTestResult {
fn get_test_result(self: Box<Self>) -> TestStatus;
}
impl GenericTestResult for ExtelResult {
fn get_test_result(self: Box<Self>) -> TestStatus {
TestStatus::Single(*self)
}
}
impl GenericTestResult for Vec<ExtelResult> {
fn get_test_result(self: Box<Self>) -> TestStatus {
TestStatus::Parameterized(*self)
}
}
pub struct Test {
pub test_name: &'static str,
pub test_fn: fn() -> Box<dyn GenericTestResult>,
}
impl Test {
pub fn run_test(self) -> TestResult {
TestResult {
test_name: self.test_name,
test_result: (self.test_fn)().get_test_result(),
}
}
}
#[derive(Debug)]
pub struct TestResult {
pub test_name: &'static str,
pub test_result: TestStatus,
}
#[derive(Debug)]
pub enum OutputDest<'a> {
Stdout,
File(&'static str),
Buffer(&'a mut Vec<u8>),
None,
}
#[derive(Debug)]
pub struct TestConfig<'a> {
pub output: OutputDest<'a>,
pub colored: bool,
}
impl<'a> TestConfig<'a> {
pub fn output(mut self, output_style: OutputDest<'a>) -> Self {
self.output = output_style;
self
}
pub fn colored(mut self, yes: bool) -> Self {
self.colored = yes;
self
}
}
impl<'a> Default for TestConfig<'a> {
fn default() -> Self {
Self {
output: OutputDest::Stdout,
colored: true,
}
}
}
pub trait RunnableTestSet {
fn run(cfg: TestConfig) -> Vec<TestResult>;
}
pub fn output_test_result<T: Write>(
stream: T,
result: &TestResult,
test_num: usize,
colored: bool,
) {
let color_terminator = match colored {
true => "\x1b[0m",
false => "",
};
let ok_color = match colored {
true => "\x1b[32m",
false => "",
};
let fail_color = match colored {
true => "\x1b[31m",
false => "",
};
let fmt_output = match &result.test_result {
TestStatus::Single(status) => match status {
Ok(()) => format!(
"\tTest #{} ({}) ... {ok_color}ok{color_terminator}\n",
test_num, result.test_name
),
Err(err_msg) => format!(
"\tTest #{} ({}) ... {fail_color}FAILED{color_terminator}\n\t [x] {}\n",
test_num, result.test_name, err_msg
),
},
TestStatus::Parameterized(statuses) => statuses
.iter()
.enumerate()
.map(|(idx, status)| match status {
Ok(()) => {
format!(
"\tTest #{}.{} ({}) ... {ok_color}ok{color_terminator}\n",
test_num, idx, result.test_name
)
}
Err(err_msg) => format!(
"\tTest #{}.{} ({}) ... {fail_color}FAILED{color_terminator}\n\t [x] {}\n",
test_num,
idx + 1,
result.test_name,
err_msg
),
})
.collect::<String>(),
};
let mut writer: BufWriter<T> = BufWriter::new(stream);
writer
.write_all(fmt_output.as_bytes())
.expect("stream could not be written to");
}
#[cfg(test)]
mod tests {
use super::*;
use Error as XE;
use TestStatus as TRT;
#[test]
fn write_test_output_no_color() {
let ok_test = TestResult {
test_name: "this_test_passes",
test_result: TRT::Single(Ok(())),
};
let fail_test = TestResult {
test_name: "this_test_fails",
test_result: TRT::Single(Err(XE::TestFailed(format!(
"test failed after {}",
ok_test.test_name
)))),
};
let mut ok_result_buffer: Vec<u8> = Vec::new();
let mut fail_result_buffer: Vec<u8> = Vec::new();
output_test_result(&mut ok_result_buffer, &ok_test, 1, false);
output_test_result(&mut fail_result_buffer, &fail_test, 2, false);
assert_eq!(
String::from_utf8_lossy(&ok_result_buffer),
"\tTest #1 (this_test_passes) ... ok\n"
);
assert_eq!(
String::from_utf8_lossy(&fail_result_buffer),
"\tTest #2 (this_test_fails) ... FAILED\n\t [x] test failed after this_test_passes\n"
);
}
#[test]
fn write_test_output_with_color() {
let ok_test = TestResult {
test_name: "this_test_passes",
test_result: TRT::Single(Ok(())),
};
let fail_test = TestResult {
test_name: "this_test_fails",
test_result: TRT::Single(Err(XE::TestFailed(format!(
"test failed after {}",
ok_test.test_name
)))),
};
let mut ok_result_buffer: Vec<u8> = Vec::new();
let mut fail_result_buffer: Vec<u8> = Vec::new();
output_test_result(&mut ok_result_buffer, &ok_test, 1, true);
output_test_result(&mut fail_result_buffer, &fail_test, 2, true);
assert_eq!(
String::from_utf8_lossy(&ok_result_buffer),
"\tTest #1 (this_test_passes) ... \x1b[32mok\x1b[0m\n"
);
assert_eq!(
String::from_utf8_lossy(&fail_result_buffer),
"\tTest #2 (this_test_fails) ... \x1b[31mFAILED\x1b[0m\n\t [x] test failed after this_test_passes\n"
);
}
}