mumu-test 0.1.2

Test suite plugin for the Lava language
Documentation
use crate::suite::{TestEntry, FileReport, GLOBAL_SUITE, CURRENT_TEST_ARC};
use mumu::{
    Interpreter,
    Value,
    parser::interpreter::apply_n_ary_function_value,
};
use std::sync::{Arc, Mutex};
use std::time::{SystemTime, UNIX_EPOCH};
use serde_json::to_string;
use std::thread;
use std::time::Duration;

pub fn describe_bridge_fn(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 2 {
        return Err(format!("describe => expected 2 args, got {}", args.len()));
    }
    let suite_name = match args.remove(0) {
        Value::SingleString(s) => s,
        Value::StrArray(a) if a.len() == 1 => a[0].clone(),
        _ => return Err("describe => first arg must be single string".into()),
    };

    {
        let mut gs = GLOBAL_SUITE.lock().unwrap();
        gs.suite_name = Some(suite_name.clone());
        gs.tests.clear();
    }

    let fn_val = match args.remove(0) {
        Value::Function(fb) => fb,
        _ => return Err("describe => second arg must be function".into()),
    };

    let ret = apply_n_ary_function_value(interp, fn_val.clone(), vec![])?;
    if let Value::Function(f2) = ret {
        let _ = apply_n_ary_function_value(interp, f2, vec![]);
    }

    loop {
        let mut total_tasks = 0;
        for poller_name in &["net:check_tasks", "sys:check_tasks", "process:check_tasks"] {
            if let Some(dfn) = interp.get_dynamic_function(poller_name) {
                if let Ok(val) = dfn.lock().unwrap()(interp, vec![]) {
                    if let Value::Int(n) = val {
                        total_tasks += n;
                    }
                }
            }
        }
        if total_tasks == 0 {
            break;
        }
        thread::sleep(Duration::from_millis(30));
    }

    let report = {
        let gs = GLOBAL_SUITE.lock().unwrap();
        let mut out_tests = Vec::new();
        for arc_test in &gs.tests {
            let t = arc_test.lock().unwrap();
            out_tests.push(t.clone());
        }
        FileReport {
            suite: gs.suite_name.clone().unwrap_or_default(),
            tests: out_tests,
        }
    };

    let js = to_string(&report).map_err(|e| format!("describe => JSON err: {}", e))?;
    println!("{}", js);

    Ok(Value::Bool(true))
}

pub fn it_bridge_fn(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 2 {
        return Err(format!("it => expected 2 args, got {}", args.len()));
    }
    let test_name = match args.remove(0) {
        Value::SingleString(s) => s,
        Value::StrArray(a) if a.len()==1 => a[0].clone(),
        _ => return Err("it => first arg must be single string".into()),
    };

    let fn_val = match args.remove(0) {
        Value::Function(fb) => fb,
        _ => return Err("it => second arg must be function".into()),
    };

    let start = current_time_micro();
    let new_test = TestEntry {
        name: test_name.clone(),
        passed: true,
        time_us: 0,
        output: String::new(),
    };

    let arc_test = Arc::new(Mutex::new(new_test));
    CURRENT_TEST_ARC.with(|cell| {
        *cell.borrow_mut() = Some(arc_test.clone());
    });

    let ret = apply_n_ary_function_value(interp, fn_val.clone(), vec![])?;
    if let Value::Function(f2) = ret {
        let _ = apply_n_ary_function_value(interp, f2, vec![]);
    }

    let dur = current_time_micro() - start;
    {
        let mut lock_test = arc_test.lock().unwrap();
        lock_test.time_us = dur;
    }
    {
        let mut gs = GLOBAL_SUITE.lock().unwrap();
        gs.tests.push(arc_test);
    }

    Ok(Value::Bool(true))
}

fn current_time_micro() -> i64 {
    let now = SystemTime::now();
    now.duration_since(UNIX_EPOCH).unwrap().as_micros() as i64
}