use derive_more::From;
#[derive(Debug, Clone, From)]
pub enum ExpectedOutput {
#[from]
String(String),
StringInsensitive(String),
#[cfg(feature = "regex")]
#[from]
Regex(regex::Regex),
}
impl From<&str> for ExpectedOutput {
fn from(value: &str) -> Self {
ExpectedOutput::String(value.into())
}
}
impl PartialEq for ExpectedOutput {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(ExpectedOutput::String(s), ExpectedOutput::String(o)) => s == o,
(ExpectedOutput::StringInsensitive(s), ExpectedOutput::StringInsensitive(o)) => s == o,
#[cfg(feature = "regex")]
(ExpectedOutput::Regex(s), ExpectedOutput::Regex(o)) => s.as_str() == o.as_str(),
_ => false,
}
}
}
impl ExpectedOutput {
pub fn is_valid(&self, output: &str) -> bool {
match self {
Self::String(ref s) => s == output,
Self::StringInsensitive(ref s) => s.eq_ignore_ascii_case(output),
#[cfg(feature = "regex")]
Self::Regex(ref reg) => reg.is_match(output),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TestCase<T = ()> {
input: String,
output: ExpectedOutput,
data: T,
}
impl<T> TestCase<T> {
pub fn new(input: impl Into<String>, output: impl Into<ExpectedOutput>, data: T) -> Self {
Self {
input: input.into(),
output: output.into(),
data,
}
}
pub fn input(&self) -> &str {
&self.input
}
pub fn output(&self) -> &ExpectedOutput {
&self.output
}
pub fn data(&self) -> &T {
&self.data
}
pub fn into_data(self) -> T {
self.data
}
}
impl<I, O> From<(I, O)> for TestCase<()>
where
I: Into<String>,
O: Into<ExpectedOutput>,
{
fn from((input, output): (I, O)) -> Self {
Self::new(input, output, ())
}
}
impl<I, O, T> From<(I, O, T)> for TestCase<T>
where
I: Into<String>,
O: Into<ExpectedOutput>,
{
fn from((input, output, data): (I, O, T)) -> Self {
Self::new(input, output, data)
}
}
#[cfg(test)]
mod test {
use regex::Regex;
use crate::cases::{ExpectedOutput, TestCase};
macro_rules! re {
($re: literal) => {
Regex::new($re).unwrap()
};
}
#[test]
fn expected_output_equality() {
assert_eq!(
ExpectedOutput::from("foobar"),
ExpectedOutput::from("foobar"),
);
assert_ne!(
ExpectedOutput::from("foobar"),
ExpectedOutput::from("bazqux"),
);
assert_ne!(
ExpectedOutput::from("foobar"),
ExpectedOutput::from(re!(".")),
);
assert_ne!(
ExpectedOutput::from(re!(".")),
ExpectedOutput::from("foobar"),
);
assert_eq!(
ExpectedOutput::from(re!(".")),
ExpectedOutput::from(re!(".")),
);
assert_ne!(
ExpectedOutput::from(re!(".")),
ExpectedOutput::from(re!(".+")),
);
}
#[test]
fn expect_output_is_valid_string() {
let validator = ExpectedOutput::from("hi");
assert!(validator.is_valid("hi"));
assert!(!validator.is_valid("bye"));
}
#[test]
fn expect_output_is_valid_empty_string() {
let validator = ExpectedOutput::String(String::new());
assert!(validator.is_valid(""));
assert!(!validator.is_valid("this is not empty"));
}
#[test]
fn expect_output_is_valid_regex() {
let validator = ExpectedOutput::from(re!(r"([01]\d|2[0-3])(:[0-5]\d){2}"));
assert!(validator.is_valid("04:22:57"));
assert!(validator.is_valid("14:22:57"));
assert!(!validator.is_valid("24:22:57"));
}
#[test]
fn test_case_getters() {
let case = TestCase::new("hello", "world", 69);
assert_eq!(case.input(), "hello");
assert!(matches!(case.output(), ExpectedOutput::String(x) if x == "world"));
assert_eq!(*case.data(), 69);
assert_eq!(case.into_data(), 69);
let case2 = TestCase::new("foo", "bar", 420);
assert_eq!(case2.input(), "foo");
assert!(matches!(case2.output(), ExpectedOutput::String(x) if x == "bar"));
assert_eq!(*case2.data(), 420);
assert_eq!(case2.into_data(), 420);
}
#[test]
fn test_from_tuple2() {
let case: TestCase<_> = ("hello", "world").into();
assert_eq!(case.input(), "hello");
assert!(matches!(case.output(), ExpectedOutput::String(x) if x == "world"));
let () = *case.data();
let () = case.into_data();
}
#[test]
fn test_from_tuple3() {
let case: TestCase<_> = ("hello", "world", 69).into();
assert_eq!(case.input(), "hello");
assert!(matches!(case.output(), ExpectedOutput::String(x) if x == "world"));
assert_eq!(*case.data(), 69);
assert_eq!(case.into_data(), 69);
let case2: TestCase<_> = ("foo", "bar", 420).into();
assert_eq!(case2.input(), "foo");
assert!(matches!(case2.output(), ExpectedOutput::String(x) if x == "bar"));
assert_eq!(*case2.data(), 420);
assert_eq!(case2.into_data(), 420);
}
}