mumu-test 0.1.2

Test suite plugin for the Lava language
Documentation
use mumu::parser::interpreter::Interpreter;
use mumu::parser::types::Value;
use super::helper::{
    runner_list_bridge, HRULE, HRULE_PLAIN, copy_to_clipboard
};
use super::run_single::{run_single_file_with_report, run_single_file_verbose, FileReport};
use std::fmt::Write as FmtWrite;

pub fn runner_all_bridge(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    let (opts, cb) = match args.len() {
        1 => (None, args[0].clone()),
        2 => (Some(args[0].clone()), args[1].clone()),
        n => return Err(format!("test:all => expected 1 or 2 args, got {}", n)),
    };

    let mut colorize = true;
    let mut errors_only = false;
    if let Some(Value::KeyedArray(ref map)) = opts {
        if let Some(Value::Bool(b)) = map.get("colorize") {
            colorize = *b;
        }
        if let Some(Value::Bool(b)) = map.get("errors_only") {
            errors_only = *b;
        }
    }

    let Value::StrArray(mut files) = runner_list_bridge(interp, vec![])? else {
        unreachable!("runner_list_bridge did not return a StrArray");
    };
    files.sort();

    let mut results: Vec<(bool, String, String, FileReport)> = Vec::new();
    for fname in &files {
        let (pass, output, report) = run_single_file_with_report(interp, fname, &cb, colorize)?;
        results.push((pass, fname.clone(), output, report));
    }

    results.sort_by(|a, b| match (a.0, b.0) {
        (true, false) => std::cmp::Ordering::Less,
        (false, true) => std::cmp::Ordering::Greater,
        _ => a.1.cmp(&b.1),
    });

    let last_fail_idx = results.iter().rposition(|(pass, _, _, _)| !*pass);

    if let Some(idx) = last_fail_idx {
        let last_fail_fname = &results[idx].1;
        match run_single_file_verbose(last_fail_fname) {
            Ok(clip_data) => copy_to_clipboard(&clip_data),
            Err(e) => eprintln!("Error running verbose mode for '{}': {}", last_fail_fname, e),
        }
    }

    for (i, (pass, _fname, mut output, report)) in results.into_iter().enumerate() {
        if errors_only && pass {
            continue;
        }
        if errors_only {
            let mut suite_out = String::new();

            if colorize {
                writeln!(suite_out, "{HRULE}").unwrap();
                writeln!(suite_out, "\x1b[1;31m{}\x1b[0m", report.suite).unwrap();
            } else {
                writeln!(suite_out, "{HRULE_PLAIN}").unwrap();
                writeln!(suite_out, "{}", report.suite).unwrap();
            }
            writeln!(suite_out).unwrap();

            let mut had_failing = false;
            for t in &report.tests {
                if t.passed { continue; }
                had_failing = true;
                if colorize {
                    writeln!(suite_out, "\x1b[31m✖ {}\x1b[0m ({} µs)", t.name, t.time_us).unwrap();
                    writeln!(suite_out).unwrap();
                    for line in t.output.lines() {
                        writeln!(suite_out, "  {}", line).unwrap();
                    }
                    writeln!(suite_out).unwrap();
                } else {
                    writeln!(suite_out, "{} ({} µs)", t.name, t.time_us).unwrap();
                    writeln!(suite_out).unwrap();
                    for line in t.output.lines() {
                        writeln!(suite_out, "  {}", line).unwrap();
                    }
                    writeln!(suite_out).unwrap();
                }
            }
            if had_failing {
                if colorize {
                    writeln!(suite_out).unwrap();
                    writeln!(suite_out, "\x1b[1;31mFAILED\x1b[0m").unwrap();
                    writeln!(suite_out).unwrap();
                } else {
                    writeln!(suite_out).unwrap();
                    writeln!(suite_out, "FAILED").unwrap();
                    writeln!(suite_out).unwrap();
                }
                if let Some(idx) = last_fail_idx {
                    if i == idx {
                        if colorize {
                            let needle = "\n\x1b[1;31mFAILED\x1b[0m\n\n";
                            let replacement = "\n\x1b[1;31mFAILED\x1b[0m\n\x1b[1;31mDEBUG IN CLIPBOARD\x1b[0m\n\n";
                            suite_out = suite_out.replacen(needle, replacement, 1);
                        } else {
                            let needle = "\nFAILED\n\n";
                            let replacement = "\nFAILED\nDEBUG IN CLIPBOARD\n\n";
                            suite_out = suite_out.replacen(needle, replacement, 1);
                        }
                    }
                }
                print!("{}", suite_out);
            }
            continue;
        }

        if let Some(idx) = last_fail_idx {
            if !pass && i == idx && colorize {
                let needle = "\n\x1b[1;31mFAILED\x1b[0m\n\n";
                let replacement = "\n\x1b[1;31mFAILED\x1b[0m\n\x1b[1;31mDEBUG IN CLIPBOARD\x1b[0m\n\n";
                output = output.replacen(needle, replacement, 1);
            } else if !pass && i == idx && !colorize {
                let needle = "\nFAILED\n\n";
                let replacement = "\nFAILED\nDEBUG IN CLIPBOARD\n\n";
                output = output.replacen(needle, replacement, 1);
            }
        }
        print!("{}", output);
    }

    Ok(Value::Bool(true))
}