use crate::suite::{TestEntry, FileReport, GLOBAL_SUITE, CURRENT_TEST_ARC, mark_fail};
use mumu::{
parser::interpreter::Interpreter,
parser::types::{FunctionValue, Value},
};
use serde_json::to_string;
use std::sync::{Arc, Mutex};
use std::time::{SystemTime, UNIX_EPOCH};
use std::{thread, time::Duration};
fn call_any_function(
interp: &mut Interpreter,
func: Box<FunctionValue>,
args: Vec<Value>,
) -> Result<Value, String> {
match *func {
FunctionValue::Named(ref name) => {
if let Some(df) = interp.get_dynamic_function(name) {
(df.lock().unwrap())(interp, args)
} else {
mumu::parser::interpreter::apply::apply_function_value(
interp,
Box::new(FunctionValue::Named(name.clone())),
args,
)
}
}
other => {
mumu::parser::interpreter::apply::apply_function_value(interp, Box::new(other), args)
}
}
}
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(),
other => return Err(format!("describe => first arg must be single string, got {:?}", other)),
};
{
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,
other => return Err(format!("describe => second arg must be function, got {:?}", other)),
};
if interp.is_verbose() {
eprintln!("[describe] => invoking suite body for {:?}", suite_name);
}
match call_any_function(interp, fn_val.clone(), vec![]) {
Ok(ret) => {
if let Value::Function(f2) = ret {
if interp.is_verbose() {
eprintln!("[describe] => invoking suite continuation for {:?}", suite_name);
}
if let Err(e) = call_any_function(interp, f2, vec![]) {
mark_fail(&format!("describe continuation error: {e}"));
}
}
}
Err(e) => {
mark_fail(&format!("describe body error: {e}"));
}
}
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(),
other => return Err(format!("it => first arg must be single string, got {:?}", other)),
};
let fn_val = match args.remove(0) {
Value::Function(fb) => fb,
other => return Err(format!("it => second arg must be function, got {:?}", other)),
};
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());
});
if interp.is_verbose() {
eprintln!("[it] => running test {:?}", test_name);
}
match call_any_function(interp, fn_val.clone(), vec![]) {
Ok(ret) => {
if let Value::Function(f2) = ret {
if let Err(e) = call_any_function(interp, f2, vec![]) {
mark_fail(&format!("it continuation error: {e}"));
}
}
}
Err(e) => {
mark_fail(&format!("it body error: {e}"));
}
}
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
}