#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![warn(clippy::print_stdout)]
pub struct Harness {
raw: Vec<std::ffi::OsString>,
cases: Vec<Trial>,
}
impl Harness {
pub fn with_env() -> Self {
let raw = std::env::args_os();
Self::with_args(raw)
}
pub fn with_args(args: impl IntoIterator<Item = impl Into<std::ffi::OsString>>) -> Self {
Self {
raw: args.into_iter().map(|a| a.into()).collect(),
cases: Vec::new(),
}
}
pub fn discover(mut self, cases: impl IntoIterator<Item = Trial>) -> Self {
self.cases.extend(cases);
self
}
pub fn main(self) -> ! {
match self.run() {
Ok(true) => std::process::exit(0),
Ok(false) => std::process::exit(libtest2_harness::ERROR_EXIT_CODE),
Err(err) => {
eprintln!("{err}");
std::process::exit(libtest2_harness::ERROR_EXIT_CODE)
}
}
}
fn run(self) -> std::io::Result<bool> {
let harness = libtest2_harness::Harness::new();
let harness = match harness.with_args(self.raw) {
Ok(harness) => harness,
Err(err) => {
eprintln!("{err}");
std::process::exit(1);
}
};
let harness = match harness.parse() {
Ok(harness) => harness,
Err(err) => {
eprintln!("{err}");
std::process::exit(1);
}
};
let harness = harness.discover(self.cases.into_iter().map(|t| TrialCase { inner: t }))?;
harness.run()
}
}
pub struct Trial {
name: String,
#[allow(clippy::type_complexity)]
runner: Box<dyn Fn(RunContext<'_>) -> Result<(), RunError> + Send + Sync>,
}
impl Trial {
pub fn test(
name: impl Into<String>,
runner: impl Fn(RunContext<'_>) -> Result<(), RunError> + Send + Sync + 'static,
) -> Self {
Self {
name: name.into(),
runner: Box::new(runner),
}
}
}
struct TrialCase {
inner: Trial,
}
impl libtest2_harness::Case for TrialCase {
fn name(&self) -> &str {
&self.inner.name
}
fn kind(&self) -> libtest2_harness::TestKind {
Default::default()
}
fn source(&self) -> Option<&libtest2_harness::Source> {
None
}
fn exclusive(&self, _: &libtest2_harness::TestContext) -> bool {
false
}
fn run(
&self,
context: &libtest2_harness::TestContext,
) -> Result<(), libtest2_harness::RunError> {
(self.inner.runner)(RunContext { inner: context }).map_err(|e| e.inner)
}
}
pub type RunResult = Result<(), RunError>;
#[derive(Debug)]
pub struct RunError {
inner: libtest2_harness::RunError,
}
impl RunError {
pub fn with_cause(cause: impl std::error::Error + Send + Sync + 'static) -> Self {
Self {
inner: libtest2_harness::RunError::with_cause(cause),
}
}
pub fn fail(cause: impl std::fmt::Display) -> Self {
Self {
inner: libtest2_harness::RunError::fail(cause),
}
}
}
pub struct RunContext<'t> {
inner: &'t libtest2_harness::TestContext,
}
impl<'t> RunContext<'t> {
pub fn ignore(&self) -> Result<(), RunError> {
self.inner.ignore().map_err(|e| RunError { inner: e })
}
pub fn ignore_for(&self, reason: impl std::fmt::Display) -> Result<(), RunError> {
self.inner
.ignore_for(reason)
.map_err(|e| RunError { inner: e })
}
}
#[doc = include_str!("../README.md")]
#[cfg(doctest)]
pub struct ReadmeDoctests;