use std::borrow::Cow;
use std::fmt;
use std::sync::mpsc::Sender;
pub use NamePadding::*;
pub use TestFn::*;
pub use TestName::*;
use super::bench::Bencher;
use super::event::CompletedTest;
use super::{__rust_begin_short_backtrace, options};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum TestType {
    UnitTest,
    IntegrationTest,
    DocTest,
    Unknown,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum NamePadding {
    PadNone,
    PadOnRight,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TestName {
    StaticTestName(&'static str),
    DynTestName(String),
    AlignedTestName(Cow<'static, str>, NamePadding),
}
impl TestName {
    pub fn as_slice(&self) -> &str {
        match *self {
            StaticTestName(s) => s,
            DynTestName(ref s) => s,
            AlignedTestName(ref s, _) => s,
        }
    }
    pub fn padding(&self) -> NamePadding {
        match self {
            &AlignedTestName(_, p) => p,
            _ => PadNone,
        }
    }
    pub fn with_padding(&self, padding: NamePadding) -> TestName {
        let name = match *self {
            TestName::StaticTestName(name) => Cow::Borrowed(name),
            TestName::DynTestName(ref name) => Cow::Owned(name.clone()),
            TestName::AlignedTestName(ref name, _) => name.clone(),
        };
        TestName::AlignedTestName(name, padding)
    }
}
impl fmt::Display for TestName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self.as_slice(), f)
    }
}
pub enum TestFn {
    StaticTestFn(fn() -> Result<(), String>),
    StaticBenchFn(fn(&mut Bencher) -> Result<(), String>),
    StaticBenchAsTestFn(fn(&mut Bencher) -> Result<(), String>),
    DynTestFn(Box<dyn FnOnce() -> Result<(), String> + Send>),
    DynBenchFn(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>),
    DynBenchAsTestFn(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>),
}
impl TestFn {
    pub fn padding(&self) -> NamePadding {
        match *self {
            StaticTestFn(..) => PadNone,
            StaticBenchFn(..) => PadOnRight,
            StaticBenchAsTestFn(..) => PadNone,
            DynTestFn(..) => PadNone,
            DynBenchFn(..) => PadOnRight,
            DynBenchAsTestFn(..) => PadNone,
        }
    }
    pub(crate) fn into_runnable(self) -> Runnable {
        match self {
            StaticTestFn(f) => Runnable::Test(RunnableTest::Static(f)),
            StaticBenchFn(f) => Runnable::Bench(RunnableBench::Static(f)),
            StaticBenchAsTestFn(f) => Runnable::Test(RunnableTest::StaticBenchAsTest(f)),
            DynTestFn(f) => Runnable::Test(RunnableTest::Dynamic(f)),
            DynBenchFn(f) => Runnable::Bench(RunnableBench::Dynamic(f)),
            DynBenchAsTestFn(f) => Runnable::Test(RunnableTest::DynamicBenchAsTest(f)),
        }
    }
}
impl fmt::Debug for TestFn {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(match *self {
            StaticTestFn(..) => "StaticTestFn(..)",
            StaticBenchFn(..) => "StaticBenchFn(..)",
            StaticBenchAsTestFn(..) => "StaticBenchAsTestFn(..)",
            DynTestFn(..) => "DynTestFn(..)",
            DynBenchFn(..) => "DynBenchFn(..)",
            DynBenchAsTestFn(..) => "DynBenchAsTestFn(..)",
        })
    }
}
pub(crate) enum Runnable {
    Test(RunnableTest),
    Bench(RunnableBench),
}
pub(crate) enum RunnableTest {
    Static(fn() -> Result<(), String>),
    Dynamic(Box<dyn FnOnce() -> Result<(), String> + Send>),
    StaticBenchAsTest(fn(&mut Bencher) -> Result<(), String>),
    DynamicBenchAsTest(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>),
}
impl RunnableTest {
    pub(crate) fn run(self) -> Result<(), String> {
        match self {
            RunnableTest::Static(f) => __rust_begin_short_backtrace(f),
            RunnableTest::Dynamic(f) => __rust_begin_short_backtrace(f),
            RunnableTest::StaticBenchAsTest(f) => {
                crate::bench::run_once(|b| __rust_begin_short_backtrace(|| f(b)))
            }
            RunnableTest::DynamicBenchAsTest(f) => {
                crate::bench::run_once(|b| __rust_begin_short_backtrace(|| f(b)))
            }
        }
    }
    pub(crate) fn is_dynamic(&self) -> bool {
        match self {
            RunnableTest::Static(_) => false,
            RunnableTest::StaticBenchAsTest(_) => false,
            RunnableTest::Dynamic(_) => true,
            RunnableTest::DynamicBenchAsTest(_) => true,
        }
    }
}
pub(crate) enum RunnableBench {
    Static(fn(&mut Bencher) -> Result<(), String>),
    Dynamic(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>),
}
impl RunnableBench {
    pub(crate) fn run(
        self,
        id: TestId,
        desc: &TestDesc,
        monitor_ch: &Sender<CompletedTest>,
        nocapture: bool,
    ) {
        match self {
            RunnableBench::Static(f) => {
                crate::bench::benchmark(id, desc.clone(), monitor_ch.clone(), nocapture, f)
            }
            RunnableBench::Dynamic(f) => {
                crate::bench::benchmark(id, desc.clone(), monitor_ch.clone(), nocapture, f)
            }
        }
    }
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct TestId(pub usize);
#[derive(Clone, Debug)]
pub struct TestDesc {
    pub name: TestName,
    pub ignore: bool,
    pub ignore_message: Option<&'static str>,
    pub source_file: &'static str,
    pub start_line: usize,
    pub start_col: usize,
    pub end_line: usize,
    pub end_col: usize,
    pub should_panic: options::ShouldPanic,
    pub compile_fail: bool,
    pub no_run: bool,
    pub test_type: TestType,
}
impl TestDesc {
    pub fn padded_name(&self, column_count: usize, align: NamePadding) -> String {
        let mut name = String::from(self.name.as_slice());
        let fill = column_count.saturating_sub(name.len());
        let pad = " ".repeat(fill);
        match align {
            PadNone => name,
            PadOnRight => {
                name.push_str(&pad);
                name
            }
        }
    }
    pub fn test_mode(&self) -> Option<&'static str> {
        if self.ignore {
            return None;
        }
        match self.should_panic {
            options::ShouldPanic::Yes | options::ShouldPanic::YesWithMessage(_) => {
                return Some("should panic");
            }
            options::ShouldPanic::No => {}
        }
        if self.compile_fail {
            return Some("compile fail");
        }
        if self.no_run {
            return Some("compile");
        }
        None
    }
}
#[derive(Debug)]
pub struct TestDescAndFn {
    pub desc: TestDesc,
    pub testfn: TestFn,
}
impl TestDescAndFn {
    pub const fn new_doctest(
        test_name: &'static str,
        ignore: bool,
        source_file: &'static str,
        start_line: usize,
        no_run: bool,
        should_panic: bool,
        testfn: TestFn,
    ) -> Self {
        Self {
            desc: TestDesc {
                name: StaticTestName(test_name),
                ignore,
                ignore_message: None,
                source_file,
                start_line,
                start_col: 0,
                end_line: 0,
                end_col: 0,
                compile_fail: false,
                no_run,
                should_panic: if should_panic {
                    options::ShouldPanic::Yes
                } else {
                    options::ShouldPanic::No
                },
                test_type: TestType::DocTest,
            },
            testfn,
        }
    }
}