nu-test-support 0.113.0

Support for writing Nushell tests
Documentation
use std::{io::stdout, num::NonZeroUsize};

use kitest::formatter::common::color::ColorSetting;

#[derive(Debug)]
pub struct Args {
    pub color: ColorSetting,
    pub exact: bool,
    pub filter: Vec<String>,
    pub format: Format,
    pub help: bool,
    pub ignored: bool,
    pub include_ignored: bool,
    pub list: bool,
    pub no_capture: bool,
    pub skip: Vec<String>,
    pub test_threads: Option<NonZeroUsize>,
}

#[derive(Debug)]
pub enum Format {
    Pretty,
    Terse,
}

impl Default for Args {
    fn default() -> Self {
        Self {
            color: ColorSetting::Automatic,
            exact: false,
            filter: Vec::new(),
            format: Format::Pretty,
            help: false,
            ignored: false,
            include_ignored: false,
            list: false,
            no_capture: false,
            skip: Vec::new(),
            test_threads: None,
        }
    }
}

impl Args {
    pub fn parse() -> Result<Args, lexopt::Error> {
        use lexopt::prelude::*;

        let mut args = Args::default();
        let mut parser = lexopt::Parser::from_env();

        fn parse_flag(parser: &mut lexopt::Parser, flag: &mut bool) -> Result<(), lexopt::Error> {
            let _: () = match parser.optional_value() {
                None => *flag = true,
                Some(value) => *flag = value.parse()?,
            };
            Ok(())
        }

        while let Some(arg) = parser.next()? {
            match arg {
                Long("color") => {
                    let color = parser.value()?.string()?;
                    match color.as_str() {
                        "auto" | "automatic" => args.color = ColorSetting::Automatic,
                        "always" => args.color = ColorSetting::Always,
                        "never" => args.color = ColorSetting::Never,
                        _ => todo!(),
                    }
                }
                Long("exact") => parse_flag(&mut parser, &mut args.exact)?,
                Value(value) => args.filter.push(value.parse()?),
                Long("format") => {
                    let color: String = parser.value()?.parse()?;
                    match color.as_str() {
                        "pretty" => args.format = Format::Pretty,
                        "terse" => args.format = Format::Terse,
                        _ => todo!(),
                    }
                }
                Long("help") => parse_flag(&mut parser, &mut args.help)?,
                Long("ignored") => parse_flag(&mut parser, &mut args.ignored)?,
                Long("include-ignored") => parse_flag(&mut parser, &mut args.include_ignored)?,
                Long("list") => parse_flag(&mut parser, &mut args.list)?,
                Long("nocapture" | "no-capture") => parse_flag(&mut parser, &mut args.no_capture)?,
                Long("skip") => args.skip.push(parser.value()?.parse()?),
                Long("test-threads") => args.test_threads = Some(parser.value()?.parse()?),
                arg => return Err(arg.unexpected()),
            }
        }

        Ok(args)
    }

    #[rustfmt::skip]
    pub fn help() {
        use std::io::Write;

        let mut out = stdout();

        macro_rules! line {
            () => {{ let _ = ::std::writeln!(out); }};
            ($fmt:expr) => {{ let _ = ::std::writeln!(out, $fmt); }};
            ($fmt:expr, $($args:tt)*) => {{ let _ = ::std::writeln!(out, $fmt, $($args)*); }};
        }

        line!("nu-test-support test harness (kitest based)");
        line!();
        line!("Usage: [OPTIONS] [FILTERS...]");
        line!();
        line!("Arguments:");
        line!("  [OPTIONS]     Settings that adjust how the test binary runs");
        line!("  [FILTERS...]  Names or patterns of tests to run");
        line!();
        line!("Options:");
        line!("  --color <auto|always|never>  Control colored output");
        line!("  --exact                      Match filters exactly");
        line!("  --format <pretty|terse>      Choose output style");
        line!("  --help                       Show this help text");
        line!("  --ignored                    Run only ignored tests");
        line!("  --include-ignored            Include ignored tests");
        line!("  --list                       List tests without running them");
        line!("  --nocapture                  Print test output directly");
        line!("  --skip <FILTER>              Skip matching tests, can be used multiple times");
        line!("  --test-threads <N>           Number of test threads to use, default is {}", *super::DEFAULT_THREAD_COUNT);
        line!();
        line!("Test Attributes:");
        line!("  #[test]                      Mark a function as a test. Must take no arguments.");
        line!("  #[should_panic]              Test passes only if it panics."); 
        line!("                               Can check the panic message with #[should_panic(expected = \"foo\")].");
        line!("  #[ignore]                    Skip this test in normal runs. Use --ignored to run it.");
        line!("  #[exp(option = true|false)]  Set an experimental option for this test.");
        line!("                               For the key import an `ExperimentalOption` and set it to");
        line!("                               true or false to enable or disable it.");
        line!("  #[env(KEY = \"value\")]        Set environment variables for this test.");
        line!("  #[serial]                    Run this test serially, with no other tests at the same time.");
    }
}