use derive_getters::Getters;
use time::{Duration, OffsetDateTime};
#[derive(Debug, Clone, Getters)]
pub struct TestSuite {
pub name: String,
pub package: String,
pub timestamp: OffsetDateTime,
pub hostname: String,
pub testcases: Vec<TestCase>,
pub system_out: Option<String>,
pub system_err: Option<String>,
}
impl TestSuite {
pub fn new(name: &str) -> Self {
TestSuite {
hostname: "localhost".into(),
package: format!("testsuite/{}", &name),
name: name.into(),
timestamp: OffsetDateTime::now_utc(),
testcases: Vec::new(),
system_out: None,
system_err: None,
}
}
pub fn add_testcase(&mut self, testcase: TestCase) {
self.testcases.push(testcase);
}
pub fn add_testcases(&mut self, testcases: impl IntoIterator<Item = TestCase>) {
self.testcases.extend(testcases);
}
pub fn set_timestamp(&mut self, timestamp: OffsetDateTime) {
self.timestamp = timestamp;
}
pub fn set_system_out(&mut self, system_out: &str) {
self.system_out = Some(system_out.to_owned());
}
pub fn set_system_err(&mut self, system_err: &str) {
self.system_err = Some(system_err.to_owned());
}
pub fn tests(&self) -> usize {
self.testcases.len()
}
pub fn errors(&self) -> usize {
self.testcases.iter().filter(|x| x.is_error()).count()
}
pub fn failures(&self) -> usize {
self.testcases.iter().filter(|x| x.is_failure()).count()
}
pub fn skipped(&self) -> usize {
self.testcases.iter().filter(|x| x.is_skipped()).count()
}
pub fn time(&self) -> Duration {
self.testcases
.iter()
.fold(Duration::ZERO, |sum, d| sum + d.time)
}
}
#[derive(Debug, Clone, Getters)]
pub struct TestSuiteBuilder {
pub testsuite: TestSuite,
}
impl TestSuiteBuilder {
pub fn new(name: &str) -> Self {
TestSuiteBuilder {
testsuite: TestSuite::new(name),
}
}
pub fn add_testcase(&mut self, testcase: TestCase) -> &mut Self {
self.testsuite.testcases.push(testcase);
self
}
pub fn add_testcases(&mut self, testcases: impl IntoIterator<Item = TestCase>) -> &mut Self {
self.testsuite.testcases.extend(testcases);
self
}
pub fn set_timestamp(&mut self, timestamp: OffsetDateTime) -> &mut Self {
self.testsuite.timestamp = timestamp;
self
}
pub fn set_system_out(&mut self, system_out: &str) -> &mut Self {
self.testsuite.system_out = Some(system_out.to_owned());
self
}
pub fn set_system_err(&mut self, system_err: &str) -> &mut Self {
self.testsuite.system_err = Some(system_err.to_owned());
self
}
pub fn build(&self) -> TestSuite {
self.testsuite.clone()
}
}
#[derive(Debug, Clone, Getters)]
pub struct TestCase {
pub name: String,
pub time: Duration,
pub result: TestResult,
pub classname: Option<String>,
pub filepath: Option<String>,
pub system_out: Option<String>,
pub system_err: Option<String>,
}
#[derive(Debug, Clone)]
pub enum TestResult {
Success,
Skipped,
Error { type_: String, message: String },
Failure { type_: String, message: String },
}
impl TestCase {
pub fn success(name: &str, time: Duration) -> Self {
TestCase {
name: name.into(),
time,
result: TestResult::Success,
classname: None,
filepath: None,
system_out: None,
system_err: None,
}
}
pub fn set_classname(&mut self, classname: &str) {
self.classname = Some(classname.to_owned());
}
pub fn set_filepath(&mut self, filepath: &str) {
self.filepath = Some(filepath.to_owned());
}
pub fn set_system_out(&mut self, system_out: &str) {
self.system_out = Some(system_out.to_owned());
}
pub fn set_system_err(&mut self, system_err: &str) {
self.system_err = Some(system_err.to_owned());
}
pub fn is_success(&self) -> bool {
matches!(self.result, TestResult::Success)
}
pub fn error(name: &str, time: Duration, type_: &str, message: &str) -> Self {
TestCase {
name: name.into(),
time,
result: TestResult::Error {
type_: type_.into(),
message: message.into(),
},
classname: None,
filepath: None,
system_out: None,
system_err: None,
}
}
pub fn is_error(&self) -> bool {
matches!(self.result, TestResult::Error { .. })
}
pub fn failure(name: &str, time: Duration, type_: &str, message: &str) -> Self {
TestCase {
name: name.into(),
time,
result: TestResult::Failure {
type_: type_.into(),
message: message.into(),
},
classname: None,
filepath: None,
system_out: None,
system_err: None,
}
}
pub fn is_failure(&self) -> bool {
matches!(self.result, TestResult::Failure { .. })
}
pub fn skipped(name: &str) -> Self {
TestCase {
name: name.into(),
time: Duration::ZERO,
result: TestResult::Skipped,
classname: None,
filepath: None,
system_out: None,
system_err: None,
}
}
pub fn is_skipped(&self) -> bool {
matches!(self.result, TestResult::Skipped)
}
}
#[derive(Debug, Clone, Getters)]
pub struct TestCaseBuilder {
pub testcase: TestCase,
}
impl TestCaseBuilder {
pub fn success(name: &str, time: Duration) -> Self {
TestCaseBuilder {
testcase: TestCase::success(name, time),
}
}
pub fn set_classname(&mut self, classname: &str) -> &mut Self {
self.testcase.classname = Some(classname.to_owned());
self
}
pub fn set_filepath(&mut self, filepath: &str) -> &mut Self {
self.testcase.filepath = Some(filepath.to_owned());
self
}
pub fn set_system_out(&mut self, system_out: &str) -> &mut Self {
self.testcase.system_out = Some(system_out.to_owned());
self
}
pub fn set_system_err(&mut self, system_err: &str) -> &mut Self {
self.testcase.system_err = Some(system_err.to_owned());
self
}
pub fn error(name: &str, time: Duration, type_: &str, message: &str) -> Self {
TestCaseBuilder {
testcase: TestCase::error(name, time, type_, message),
}
}
pub fn failure(name: &str, time: Duration, type_: &str, message: &str) -> Self {
TestCaseBuilder {
testcase: TestCase::failure(name, time, type_, message),
}
}
pub fn skipped(name: &str) -> Self {
TestCaseBuilder {
testcase: TestCase::skipped(name),
}
}
pub fn build(&self) -> TestCase {
self.testcase.clone()
}
}
#[cfg(doctest)]
doc_comment::doctest!("../README.md");