#![cfg(not(target_arch = "wasm32"))]
use futures::executor::block_on;
use runmat_core::{abi, CompatMode, RunError, RunMatSession};
use runmat_gc::{gc_test_context, GcConfig};
use runmat_time::Instant;
use std::convert::TryInto;
use std::thread;
#[test]
fn test_jit_vs_interpreter_execution() {
gc_test_context(|| {
let mut jit_engine = RunMatSession::with_options(true, false).unwrap();
let jit_result =
runmat_core::execute_text_request_for_testing(&mut jit_engine, "x = 5 + 3").unwrap();
let mut interp_engine = RunMatSession::with_options(false, false).unwrap();
let interp_result =
runmat_core::execute_text_request_for_testing(&mut interp_engine, "x = 5 + 3").unwrap();
assert!(jit_result.error.is_none());
assert!(interp_result.error.is_none());
assert!(!interp_result.used_jit);
});
}
#[test]
fn test_hotspot_compilation_simulation() {
gc_test_context(|| {
let mut engine = RunMatSession::with_options(true, false).unwrap();
let code = "result = 10 * 5 + 2";
let mut _jit_executions = 0;
let mut interp_executions = 0;
for _ in 0..15 {
let result = runmat_core::execute_text_request_for_testing(&mut engine, code).unwrap();
assert!(result.error.is_none());
if result.used_jit {
_jit_executions += 1;
} else {
interp_executions += 1;
}
}
let stats = engine.stats();
assert_eq!(stats.total_executions, 15);
assert_eq!(stats.jit_compiled + stats.interpreter_fallback, 15);
assert!(
interp_executions > 0 || _jit_executions > 0,
"Should have some executions"
);
});
}
#[test]
fn exact_stop_time_regression_stays_numeric_after_session_execution() {
gc_test_context(|| {
let mut engine = RunMatSession::with_options(true, false).unwrap();
let code = "\
g = 9.81;
h0 = 2.0;
ratio = sqrt(0.8);
t0 = sqrt(2*h0/g);
t1 = 2*sqrt(2*(0.8*h0)/g);
exactStopTime = t0 + t1/(1-ratio)";
for _ in 0..12 {
let result = runmat_core::execute_text_request_for_testing(&mut engine, code).unwrap();
assert!(result.error.is_none());
let value = result.value.as_ref().expect("final assignment emits value");
let numeric: f64 = value.try_into().unwrap();
assert!(
(numeric - 11.458330203035164).abs() < 1e-10,
"unexpected result: {numeric}"
);
let exact_entry = result
.workspace
.values
.iter()
.find(|entry| entry.name == "exactStopTime")
.expect("workspace includes exactStopTime");
let preview = exact_entry
.preview
.as_ref()
.expect("exactStopTime has scalar preview");
assert_eq!(preview.values.len(), 1);
assert!(
(preview.values[0] - 11.458330203035164).abs() < 1e-10,
"unexpected workspace preview: {}",
preview.values[0]
);
}
});
}
#[test]
fn test_gc_integration_during_execution() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let config = GcConfig::low_latency();
engine.configure_gc(config).unwrap();
for i in 0..10 {
let code = format!("matrix{i} = [1, 2; 3, 4]");
let result = runmat_core::execute_text_request_for_testing(&mut engine, &code);
assert!(result.is_ok());
}
let gc_stats = engine.gc_stats();
let _allocations = gc_stats
.total_allocations
.load(std::sync::atomic::Ordering::Relaxed);
});
}
#[test]
fn test_error_recovery_and_continued_execution() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let result1 = runmat_core::execute_text_request_for_testing(&mut engine, "x = 1");
assert!(result1.is_ok());
let result2 = runmat_core::execute_text_request_for_testing(&mut engine, "y = [1, 2,"); match result2 {
Err(RunError::Syntax(_)) => {}
other => panic!("expected syntax error for incomplete matrix literal, got {other:?}"),
}
let result3 = runmat_core::execute_text_request_for_testing(&mut engine, "z = 3");
assert!(result3.is_ok());
let stats = engine.stats();
assert_eq!(stats.total_executions, 3);
});
}
#[test]
fn test_complex_mathematical_operations() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let complex_operations = [
"a = 1 + 2 * 3",
"b = (4 + 5) * 6",
"c = 7 - 8 / 2",
"d = 10 + 20 - 5",
];
for op in &complex_operations {
let result = runmat_core::execute_text_request_for_testing(&mut engine, op);
assert!(result.is_ok(), "Failed to execute: {op}");
assert!(result.unwrap().error.is_none());
}
let stats = engine.stats();
assert_eq!(stats.total_executions, complex_operations.len());
});
}
#[test]
fn test_strict_mode_rejects_runmat_extensions() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
engine.set_compat_mode(CompatMode::Strict);
let err = runmat_core::execute_text_request_for_testing(&mut engine, "t = spawn(1);")
.expect_err("strict mode should reject RunMat extensions");
let RunError::Semantic(err) = err else {
panic!("expected semantic strict-mode error");
};
assert_eq!(
err.identifier.as_deref(),
Some("RunMat:SpawnExtensionDisabled")
);
});
}
#[test]
fn test_request_host_policy_disables_top_level_await() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let err = block_on(engine.execute_request(abi::ExecutionRequest {
source: abi::SourceInput::Text {
name: "request-await-policy.m".to_string(),
text: "y = await(1);".to_string(),
},
compatibility: CompatMode::Matlab,
host_policy: abi::HostExecutionPolicy {
top_level_await: false,
dynamic_eval: true,
},
requested_outputs: runmat_hir::RequestedOutputCount::Zero,
workspace: abi::WorkspaceHandle(uuid::Uuid::from_u128(13)),
}))
.expect_err("request should reject top-level await when host policy disables it");
let RunError::Semantic(err) = err else {
panic!("expected semantic top-level-await policy error");
};
assert_eq!(
err.identifier.as_deref(),
Some("RunMat:AwaitContextInvalid")
);
});
}
#[test]
fn test_await_passes_through_non_spawn_operand_at_runtime() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let result = runmat_core::execute_text_request_for_testing(&mut engine, "y = await(1);")
.expect("execution should complete successfully");
assert!(
result.error.is_none(),
"await pass-through for non-task values should not error"
);
let readback = runmat_core::execute_text_request_for_testing(&mut engine, "y")
.expect("readback should succeed");
assert_eq!(readback.value, Some(runmat_builtins::Value::Num(1.0)));
});
}
#[test]
fn test_spawn_handle_is_consumed_after_await() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let first_await = runmat_core::execute_text_request_for_testing(
&mut engine,
"t = spawn(41 + 1); first = await(t);",
)
.expect("first await execution should complete successfully");
assert!(
first_await.error.is_none(),
"first await should not report runtime errors"
);
let first = runmat_core::execute_text_request_for_testing(&mut engine, "first")
.expect("first readback should succeed");
assert_eq!(first.value, Some(runmat_builtins::Value::Num(42.0)));
let second_await =
runmat_core::execute_text_request_for_testing(&mut engine, "second = await(t);");
match second_await {
Err(RunError::Runtime(err)) => {
assert_eq!(err.identifier(), Some("RunMat:AwaitOperandInvalid"));
}
Err(other) => panic!("expected runtime await-handle error, got: {other:?}"),
Ok(exec) => {
if let Some(err) = exec.error {
assert_eq!(err.identifier(), Some("RunMat:AwaitOperandInvalid"));
} else {
let second =
runmat_core::execute_text_request_for_testing(&mut engine, "second")
.expect_err(
"second binding should be absent when consumed handle is ignored",
);
let RunError::Semantic(err) = second else {
panic!("expected semantic undefined-variable error");
};
assert_eq!(err.identifier.as_deref(), Some("RunMat:UndefinedVariable"));
}
}
}
});
}
#[test]
fn test_control_flow_execution() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let result =
runmat_core::execute_text_request_for_testing(&mut engine, "if 1 > 0; x = 10; end")
.expect("if-statement execution should succeed");
assert!(
result.error.is_none(),
"if-statement should not report runtime errors"
);
let result = runmat_core::execute_text_request_for_testing(
&mut engine,
"if 0 > 1; y = 20; else; y = 30; end",
)
.expect("if-else execution should succeed");
assert!(
result.error.is_none(),
"if-else should not report runtime errors"
);
let result = runmat_core::execute_text_request_for_testing(
&mut engine,
"for i = 1:3; z = i * 2; end",
)
.expect("for-loop execution should succeed");
assert!(
result.error.is_none(),
"for-loop should not report runtime errors"
);
let result =
runmat_core::execute_text_request_for_testing(&mut engine, "while 0 < 1; break; end")
.expect("while-loop execution should succeed");
assert!(
result.error.is_none(),
"while-loop should not report runtime errors"
);
let x = runmat_core::execute_text_request_for_testing(&mut engine, "x")
.expect("x readback should succeed");
assert_eq!(x.value, Some(runmat_builtins::Value::Num(10.0)));
let y = runmat_core::execute_text_request_for_testing(&mut engine, "y")
.expect("y readback should succeed");
assert_eq!(y.value, Some(runmat_builtins::Value::Num(30.0)));
let z = runmat_core::execute_text_request_for_testing(&mut engine, "z")
.expect("z readback should succeed");
assert_eq!(z.value, Some(runmat_builtins::Value::Num(6.0)));
});
}
#[test]
fn test_memory_usage_under_load() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
for i in 0..50 {
let code = format!("var{} = [{}; {}; {}]", i, i, i + 1, i + 2);
let result = runmat_core::execute_text_request_for_testing(&mut engine, &code);
assert!(result.is_ok());
}
let gc_stats = engine.gc_stats();
let _allocations = gc_stats
.total_allocations
.load(std::sync::atomic::Ordering::Relaxed);
let _ = runmat_gc::gc_collect_major();
let result = runmat_core::execute_text_request_for_testing(&mut engine, "final = 42");
assert!(result.is_ok());
});
}
#[test]
fn test_execution_timing_accuracy() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let start = Instant::now();
let result = runmat_core::execute_text_request_for_testing(&mut engine, "x = 1 + 1");
let elapsed = start.elapsed();
assert!(result.is_ok());
let exec_result = result.unwrap();
assert!(exec_result.execution_time_ms <= elapsed.as_millis() as u64 + 100);
});
}
#[test]
fn test_verbose_mode_output() {
gc_test_context(|| {
let mut verbose_engine = RunMatSession::with_options(true, true).unwrap();
let mut quiet_engine = RunMatSession::with_options(true, false).unwrap();
let code = "test = 1 + 2";
let verbose_result =
runmat_core::execute_text_request_for_testing(&mut verbose_engine, code);
let quiet_result = runmat_core::execute_text_request_for_testing(&mut quiet_engine, code);
assert!(verbose_result.is_ok());
assert!(quiet_result.is_ok());
assert_eq!(
verbose_result.unwrap().error.is_none(),
quiet_result.unwrap().error.is_none()
);
});
}
#[test]
fn test_statistics_accuracy() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let num_executions = 7;
for i in 0..num_executions {
let code = format!("val{i} = {i}");
let result = runmat_core::execute_text_request_for_testing(&mut engine, &code);
assert!(result.is_ok());
}
let stats = engine.stats();
assert_eq!(stats.total_executions, num_executions);
assert!(stats.average_execution_time_ms >= 0.0);
let expected_avg = stats.total_execution_time_ms as f64 / num_executions as f64;
assert!((stats.average_execution_time_ms - expected_avg).abs() < 0.001);
});
}
#[test]
fn test_engine_state_isolation() {
gc_test_context(|| {
let mut engine1 = RunMatSession::new().unwrap();
let mut engine2 = RunMatSession::new().unwrap();
runmat_core::execute_text_request_for_testing(&mut engine1, "x = 10").unwrap();
runmat_core::execute_text_request_for_testing(&mut engine2, "y = 20").unwrap();
let stats1 = engine1.stats();
let stats2 = engine2.stats();
assert_eq!(stats1.total_executions, 1);
assert_eq!(stats2.total_executions, 1);
engine1.reset_stats();
assert_eq!(engine1.stats().total_executions, 0);
assert_eq!(engine2.stats().total_executions, 1); });
}
#[test]
fn test_concurrent_engine_usage() {
gc_test_context(|| {
use std::sync::{Arc, Mutex};
let results = Arc::new(Mutex::new(Vec::new()));
let mut handles = vec![];
for thread_id in 0..3 {
let results_clone = Arc::clone(&results);
let handle = thread::spawn(move || {
let mut engine = RunMatSession::new().unwrap();
let code = format!("thread_var = {thread_id} + 1");
let result = runmat_core::execute_text_request_for_testing(&mut engine, &code);
let mut results_guard = results_clone.lock().unwrap();
results_guard.push((thread_id, result.is_ok()));
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let results_guard = results.lock().unwrap();
assert_eq!(results_guard.len(), 3);
for (_thread_id, success) in results_guard.iter() {
assert!(success, "Thread execution failed");
}
});
}
#[test]
fn test_matrix_operations_integration() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let matrix_tests = [
"A = [1, 2; 3, 4]",
"B = [5, 6; 7, 8]",
"row_vec = [1, 2, 3]",
"col_vec = [1; 2; 3]",
"scalar = 42",
];
for test in &matrix_tests {
let result = runmat_core::execute_text_request_for_testing(&mut engine, test);
assert!(result.is_ok(), "Matrix operation failed: {test}");
let exec_result = result.unwrap();
assert!(exec_result.error.is_none());
}
let stats = engine.stats();
assert_eq!(stats.total_executions, matrix_tests.len());
});
}
#[test]
fn test_performance_degradation_detection() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let mut execution_times = Vec::new();
for i in 0..20 {
let code = format!("perf_test_{i} = {i} * 2 + 1");
let result = runmat_core::execute_text_request_for_testing(&mut engine, &code).unwrap();
execution_times.push(result.execution_time_ms);
}
let first_batch_avg: f64 =
execution_times[0..5].iter().map(|&x| x as f64).sum::<f64>() / 5.0;
let last_batch_avg: f64 = execution_times[15..20]
.iter()
.map(|&x| x as f64)
.sum::<f64>()
/ 5.0;
let baseline = first_batch_avg.max(1.0);
assert!(
last_batch_avg < baseline * 3.0,
"Performance degraded: first batch avg = {first_batch_avg}, last batch avg = {last_batch_avg}"
);
});
}
#[test]
fn test_repl_function_definition_and_call_same_statement() {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let result = runmat_core::execute_text_request_for_testing(
&mut engine,
"function y = double(x); y = x * 2; end; result = double(21)",
);
assert!(
result.is_ok(),
"Function definition and call should succeed"
);
let exec_result = result.unwrap();
if let Some(error) = &exec_result.error {
panic!("Function call failed with error: {error}");
}
});
}
#[test]
fn test_repl_function_persistence() {
let handle = std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let result1 = runmat_core::execute_text_request_for_testing(
&mut engine,
"function y = add(a, b); y = a + b; end",
);
assert!(result1.is_ok(), "Function definition should succeed");
let result2 =
runmat_core::execute_text_request_for_testing(&mut engine, "x = add(10, 20)");
assert!(result2.is_ok(), "Function call should succeed");
let exec_result = result2.unwrap();
if let Some(error) = &exec_result.error {
panic!("Function call failed with error: {error}");
}
});
})
.expect("spawn repl function thread");
handle.join().expect("join repl function thread");
}
#[test]
fn test_debug_function_context() {
let handle = std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
gc_test_context(|| {
let mut engine = RunMatSession::new().unwrap();
let result1 = runmat_core::execute_text_request_for_testing(
&mut engine,
"function y = test_func(x); y = x + 1; end",
);
assert!(result1.is_ok(), "Function definition should succeed");
let result2 = runmat_core::execute_text_request_for_testing(
&mut engine,
"builtin_test = abs(-5)",
);
assert!(result2.is_ok(), "Builtin call should succeed");
let result3 = runmat_core::execute_text_request_for_testing(
&mut engine,
"user_func_test = test_func(10)",
);
assert!(result3.is_ok(), "Function call should succeed");
let exec_result = result3.unwrap();
assert!(
exec_result.error.is_none(),
"Function call should not error"
);
});
})
.expect("spawn debug function thread");
handle.join().expect("join debug function thread");
}