#![cfg(not(target_arch = "wasm32"))]
use runmat_core::{ExecutionStreamKind, RunError, RunMatSession, SessionExecutionResult};
use runmat_gc::gc_test_context;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
fn stdout_stream(result: &SessionExecutionResult) -> String {
result
.streams
.iter()
.filter(|entry| entry.stream == ExecutionStreamKind::Stdout)
.map(|entry| entry.text.as_str())
.collect::<String>()
}
fn stderr_stream(result: &SessionExecutionResult) -> String {
result
.streams
.iter()
.filter(|entry| entry.stream == ExecutionStreamKind::Stderr)
.map(|entry| entry.text.as_str())
.collect::<String>()
}
fn unique_temp_path(prefix: &str) -> PathBuf {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
std::env::temp_dir().join(format!("runmat_{prefix}_{nanos}.txt"))
}
#[test]
fn fprintf_rm138_repro_is_stable_end_to_end() {
let mut engine = gc_test_context(RunMatSession::new).unwrap();
let script = r#"
price = 42.5;
fprintf("Price: $%.2f\n", price);
x = single(3.14);
fprintf("Value: %.4f\n", double(x));
"#;
let result = runmat_core::execute_text_request_for_testing(&mut engine, script).unwrap();
assert_eq!(stdout_stream(&result), "Price: $42.50\nValue: 3.1400\n");
}
#[test]
fn disp_inline_cast_argument_is_stable_end_to_end() {
let mut engine = gc_test_context(RunMatSession::new).unwrap();
let script = r#"
x = single(3.14);
disp(double(x));
"#;
let result = runmat_core::execute_text_request_for_testing(&mut engine, script).unwrap();
let rendered = stdout_stream(&result);
let parsed: f64 = rendered
.trim()
.parse()
.expect("disp output should parse as f64");
let expected = f64::from(
"3.14"
.parse::<f32>()
.expect("parse single precision literal"),
);
assert!(
(parsed - expected).abs() < 1e-6,
"expected {expected}, got {parsed}"
);
}
#[test]
fn fprintf_stream_routing_is_correct() {
let mut engine = gc_test_context(RunMatSession::new).unwrap();
let result = runmat_core::execute_text_request_for_testing(
&mut engine,
"fprintf('out'); fprintf(2, 'err');",
)
.unwrap();
assert_eq!(stdout_stream(&result), "out");
assert_eq!(stderr_stream(&result), "err");
}
#[test]
fn fprintf_grouping_and_i_flag_smoke() {
let mut engine = gc_test_context(RunMatSession::new).unwrap();
let result = runmat_core::execute_text_request_for_testing(
&mut engine,
"fprintf('%''d|%Id', 12345, 42);",
)
.unwrap();
assert_eq!(stdout_stream(&result), "12,345|42");
}
#[test]
fn fprintf_file_roundtrip_and_count_work_end_to_end() {
let mut engine = gc_test_context(RunMatSession::new).unwrap();
let path = unique_temp_path("fprintf_core_roundtrip");
let path_text = path.to_string_lossy();
let script = format!(
"fid = fopen('{}', 'w'); n = fprintf(fid, 'hello-%d', 7); fclose(fid); fprintf('|%d|', n);",
path_text
);
let result = runmat_core::execute_text_request_for_testing(&mut engine, &script).unwrap();
assert!(
result.error.is_none(),
"expected no execution error, got {:?}",
result.error
);
assert_eq!(stdout_stream(&result), "|7|");
let bytes = std::fs::read(&path).expect("written file should exist");
assert_eq!(bytes, b"hello-7");
let _ = std::fs::remove_file(path);
}
#[test]
fn fprintf_encoding_alias_smoke_utf8_underscore() {
let mut engine = gc_test_context(RunMatSession::new).unwrap();
let path = unique_temp_path("fprintf_core_utf8_alias");
let path_text = path.to_string_lossy();
let script = format!(
"fid = fopen('{}', 'w', 'native', 'utf_8'); fprintf(fid, '%s', 'é'); fclose(fid);",
path_text
);
let result = runmat_core::execute_text_request_for_testing(&mut engine, &script).unwrap();
assert!(
result.error.is_none(),
"expected no execution error, got {:?}",
result.error
);
let bytes = std::fs::read(&path).expect("utf8 alias output should exist");
assert_eq!(bytes, "é".as_bytes());
let _ = std::fs::remove_file(path);
}
#[test]
fn fprintf_format_error_propagates_to_session_boundary() {
let mut engine = gc_test_context(RunMatSession::new).unwrap();
let request = runmat_core::abi::ExecutionRequest::for_source(
runmat_core::abi::SourceInput::Text {
name: "<test>".to_string(),
text: "fprintf('%q', 1);".to_string(),
},
engine.compat_mode(),
runmat_core::abi::HostExecutionPolicy::default(),
engine.workspace_handle(),
);
match futures::executor::block_on(engine.execute_request(request)) {
Ok(outcome) => {
assert!(
outcome.diagnostics.iter().any(|d| d.severity
== runmat_core::abi::DiagnosticSeverity::Error
&& d.code == "RunMat:fprintf:InvalidFormat"),
"expected stable unsupported-format identifier in diagnostics: {:?}",
outcome.diagnostics
);
}
Err(RunError::Runtime(err)) => assert_eq!(
err.identifier(),
Some("RunMat:fprintf:InvalidFormat"),
"expected stable unsupported-format identifier in runtime error"
),
Err(other) => panic!("expected runtime formatting error, got {other:?}"),
}
}