use crate::{test_result::TestResult, Errors, Format};
use std::{
boxed::Box,
fmt::Debug,
panic::RefUnwindSafe,
path::{Path, PathBuf},
};
pub mod debug;
#[cfg(feature = "gha")]
pub use gha::*;
#[cfg(feature = "gha")]
mod gha;
pub use json::*;
mod json;
pub use text::*;
mod text;
pub trait StatusEmitter: Send + Sync + RefUnwindSafe {
fn register_test(&self, path: PathBuf) -> Box<dyn TestStatus + 'static>;
#[allow(clippy::type_complexity)]
fn finalize(
&self,
failed: usize,
succeeded: usize,
ignored: usize,
filtered: usize,
aborted: bool,
) -> Box<dyn Summary>;
}
impl From<Format> for Box<dyn StatusEmitter> {
fn from(format: Format) -> Box<dyn StatusEmitter> {
match format {
Format::JSON => Box::new(JSON::new()),
Format::Pretty => Box::new(Text::verbose()),
Format::Terse => Box::new(Text::quiet()),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum RevisionStyle {
Separate,
Show,
}
pub trait TestStatus: Send + Sync + RefUnwindSafe {
fn for_revision(&self, revision: &str, style: RevisionStyle) -> Box<dyn TestStatus>;
fn for_path(&self, path: &Path) -> Box<dyn TestStatus>;
fn failed_test<'a>(
&'a self,
cmd: &'a str,
stderr: &'a [u8],
stdout: &'a [u8],
) -> Box<dyn Debug + 'a>;
fn done(&self, _result: &TestResult, _aborted: bool) {}
fn path(&self) -> &Path;
fn revision(&self) -> &str;
}
pub trait Summary {
fn test_failure(&mut self, _status: &dyn TestStatus, _errors: &Errors) {}
}
impl Summary for () {}
impl StatusEmitter for () {
fn register_test(&self, path: PathBuf) -> Box<dyn TestStatus> {
Box::new(SilentStatus {
path,
revision: String::new(),
})
}
fn finalize(
&self,
_failed: usize,
_succeeded: usize,
_ignored: usize,
_filtered: usize,
_aborted: bool,
) -> Box<dyn Summary> {
Box::new(())
}
}
pub struct SilentStatus {
pub revision: String,
pub path: PathBuf,
}
impl TestStatus for SilentStatus {
fn for_revision(&self, revision: &str, _style: RevisionStyle) -> Box<dyn TestStatus> {
Box::new(SilentStatus {
revision: revision.into(),
path: self.path.clone(),
})
}
fn for_path(&self, path: &Path) -> Box<dyn TestStatus> {
Box::new(SilentStatus {
revision: self.revision.clone(),
path: path.to_path_buf(),
})
}
fn failed_test<'a>(
&'a self,
_cmd: &'a str,
_stderr: &'a [u8],
_stdout: &'a [u8],
) -> Box<dyn Debug + 'a> {
Box::new(())
}
fn path(&self) -> &Path {
&self.path
}
fn revision(&self) -> &str {
&self.revision
}
}
impl<T: TestStatus, U: TestStatus> TestStatus for (T, U) {
fn done(&self, result: &TestResult, aborted: bool) {
self.0.done(result, aborted);
self.1.done(result, aborted);
}
fn failed_test<'a>(
&'a self,
cmd: &'a str,
stderr: &'a [u8],
stdout: &'a [u8],
) -> Box<dyn Debug + 'a> {
Box::new((
self.0.failed_test(cmd, stderr, stdout),
self.1.failed_test(cmd, stderr, stdout),
))
}
fn path(&self) -> &Path {
let path = self.0.path();
assert_eq!(path, self.1.path());
path
}
fn revision(&self) -> &str {
let rev = self.0.revision();
assert_eq!(rev, self.1.revision());
rev
}
fn for_revision(&self, revision: &str, style: RevisionStyle) -> Box<dyn TestStatus> {
Box::new((
self.0.for_revision(revision, style),
self.1.for_revision(revision, style),
))
}
fn for_path(&self, path: &Path) -> Box<dyn TestStatus> {
Box::new((self.0.for_path(path), self.1.for_path(path)))
}
}
impl<T: StatusEmitter, U: StatusEmitter> StatusEmitter for (T, U) {
fn register_test(&self, path: PathBuf) -> Box<dyn TestStatus> {
Box::new((
self.0.register_test(path.clone()),
self.1.register_test(path),
))
}
fn finalize(
&self,
failures: usize,
succeeded: usize,
ignored: usize,
filtered: usize,
aborted: bool,
) -> Box<dyn Summary> {
Box::new((
self.1
.finalize(failures, succeeded, ignored, filtered, aborted),
self.0
.finalize(failures, succeeded, ignored, filtered, aborted),
))
}
}
impl<T: StatusEmitter> StatusEmitter for (T,) {
fn register_test(&self, path: PathBuf) -> Box<dyn TestStatus> {
self.0.register_test(path.clone())
}
fn finalize(
&self,
failures: usize,
succeeded: usize,
ignored: usize,
filtered: usize,
aborted: bool,
) -> Box<dyn Summary> {
self.0
.finalize(failures, succeeded, ignored, filtered, aborted)
}
}
impl<T: TestStatus + ?Sized> TestStatus for Box<T> {
fn done(&self, result: &TestResult, aborted: bool) {
(**self).done(result, aborted);
}
fn path(&self) -> &Path {
(**self).path()
}
fn revision(&self) -> &str {
(**self).revision()
}
fn for_revision(&self, revision: &str, style: RevisionStyle) -> Box<dyn TestStatus> {
(**self).for_revision(revision, style)
}
fn for_path(&self, path: &Path) -> Box<dyn TestStatus> {
(**self).for_path(path)
}
fn failed_test<'a>(
&'a self,
cmd: &'a str,
stderr: &'a [u8],
stdout: &'a [u8],
) -> Box<dyn Debug + 'a> {
(**self).failed_test(cmd, stderr, stdout)
}
}
impl<T: StatusEmitter + ?Sized> StatusEmitter for Box<T> {
fn register_test(&self, path: PathBuf) -> Box<dyn TestStatus> {
(**self).register_test(path)
}
fn finalize(
&self,
failures: usize,
succeeded: usize,
ignored: usize,
filtered: usize,
aborted: bool,
) -> Box<dyn Summary> {
(**self).finalize(failures, succeeded, ignored, filtered, aborted)
}
}
impl Summary for (Box<dyn Summary>, Box<dyn Summary>) {
fn test_failure(&mut self, status: &dyn TestStatus, errors: &Errors) {
self.0.test_failure(status, errors);
self.1.test_failure(status, errors);
}
}