use super::support::*;
use std::time::Duration;
#[test]
fn test_headless_large_output() {
let process = HeadlessProcess::spawn().expect("Failed to spawn headless");
let result = process.ipc_eval("1:1000").expect("eval should run");
assert!(result.success, "eval should succeed: {}", result.stderr);
let json = parse_ipc_json(&result);
let value = json["value"]
.as_str()
.expect("value should be present for 1:1000");
let count = value
.split_whitespace()
.filter(|s| s.parse::<u32>().is_ok())
.count();
assert_eq!(
count, 1000,
"output should contain exactly 1000 integers (got {count}): {}",
value
);
}
#[test]
fn test_headless_large_output_stdout_visible() {
let process = HeadlessProcess::spawn().expect("Failed to spawn headless");
let result = process
.ipc_eval_visible(r#"print(1:1000)"#)
.expect("eval should run");
assert!(result.success, "eval should succeed: {}", result.stderr);
let json = parse_ipc_json(&result);
let stdout = json["stdout"]
.as_str()
.expect("stdout should be present for visible print");
let count = stdout
.split_whitespace()
.filter(|s| s.parse::<u32>().is_ok())
.count();
assert_eq!(
count, 1000,
"stdout should contain exactly 1000 integers (got {count}): {}",
stdout
);
}
#[test]
fn test_headless_message_capture() {
let process = HeadlessProcess::spawn().expect("Failed to spawn headless");
let result = process
.ipc_eval(r#"message("hello from message"); invisible(NULL)"#)
.expect("eval should run");
assert!(result.success, "eval should succeed: {}", result.stderr);
let json = parse_ipc_json(&result);
assert!(
json["stderr"]
.as_str()
.is_some_and(|s| s.contains("hello from message")),
"message() output should appear in stderr field: {}",
result.stdout
);
assert!(
json["stdout"].as_str().is_none_or(|s| s.is_empty()),
"message() should not appear in stdout: {}",
result.stdout
);
assert!(
json["value"].as_str().is_none(),
"message() should not produce a value: {}",
result.stdout
);
}
#[test]
fn test_headless_warning_capture() {
let process = HeadlessProcess::spawn().expect("Failed to spawn headless");
let result = process
.ipc_eval(r#"options(warn = 1); warning("test warning"); invisible(NULL)"#)
.expect("eval should run");
assert!(result.success, "eval should succeed: {}", result.stderr);
let json = parse_ipc_json(&result);
assert!(
json["stderr"]
.as_str()
.is_some_and(|s| s.contains("test warning")),
"warning() output should appear in stderr field: {}",
result.stdout
);
assert!(
json["stdout"].as_str().is_none_or(|s| s.is_empty()),
"warning() should not appear in stdout: {}",
result.stdout
);
assert!(
json["value"].as_str().is_none(),
"warning() should not produce a value: {}",
result.stdout
);
}
#[test]
fn test_headless_utf8_multibyte() {
let process = HeadlessProcess::spawn().expect("Failed to spawn headless");
let result = process
.ipc_eval(r#"utf8ToInt("\u65e5\u672c\u8a9e")"#)
.expect("eval should run");
assert!(result.success, "eval should succeed: {}", result.stderr);
let json = parse_ipc_json(&result);
assert_eq!(
json["value"].as_str(),
Some("[1] 26085 26412 35486"),
"utf8ToInt should return expected code points: {}",
result.stdout
);
let result2 = process
.ipc_eval(
r#"cat(paste(sprintf("%02x", as.integer(charToRaw(enc2utf8("\u65e5\u672c\u8a9e")))), collapse = " "))"#,
)
.expect("eval should run");
assert!(result2.success, "eval should succeed: {}", result2.stderr);
let json2 = parse_ipc_json(&result2);
let stdout2 = json2["stdout"]
.as_str()
.expect("stdout should be present for raw-byte check");
assert!(
stdout2.contains("e6 97 a5 e6 9c ac e8 aa 9e"),
"UTF-8 bytes should match expected sequence in stdout: {}",
result2.stdout
);
assert!(
json2["stderr"].as_str().is_none_or(|s| s.is_empty()),
"stderr should be empty for UTF-8 stdout check: {}",
result2.stdout
);
assert!(
json2["value"].as_str().is_none(),
"cat() check should not return value: {}",
result2.stdout
);
}
#[test]
fn test_sessions_dir_override_writer() {
let tmp = tempfile::TempDir::new().expect("create temp dir");
let sessions_dir = tmp.path().to_str().expect("sessions_dir is valid utf-8");
let process = HeadlessProcess::spawn_with_sessions_dir(sessions_dir)
.expect("Failed to spawn headless with sessions dir override");
let session_file = tmp.path().join(format!("{}.json", process.pid));
let start = std::time::Instant::now();
loop {
if session_file.exists() {
break;
}
assert!(
start.elapsed() < Duration::from_secs(5),
"session file not found at {} within timeout",
session_file.display()
);
std::thread::sleep(Duration::from_millis(50));
}
let result = process.ipc_eval("1 + 1").expect("eval should work");
assert!(result.success, "eval should succeed: {}", result.stderr);
assert!(
result.stdout.contains("[1] 2"),
"should return result: {}",
result.stdout
);
}
#[test]
fn test_headless_lib_paths_valid() {
let process =
HeadlessProcess::spawn_with_args(&["--vanilla"]).expect("Failed to spawn headless");
let result = process
.ipc_eval("length(.libPaths()) > 0L")
.expect("eval should run");
assert!(result.success, "eval should succeed: {}", result.stderr);
let json = parse_ipc_json(&result);
assert_eq!(
json["value"].as_str(),
Some("[1] TRUE"),
".libPaths() should not be empty: {}",
result.stdout
);
let result2 = process
.ipc_eval("all(dir.exists(.libPaths()))")
.expect("eval should run");
assert!(result2.success, "eval should succeed: {}", result2.stderr);
let json2 = parse_ipc_json(&result2);
assert_eq!(
json2["value"].as_str(),
Some("[1] TRUE"),
"all .libPaths() entries should exist on disk: {}",
result2.stdout
);
#[cfg(not(target_os = "macos"))]
let result3 = process
.ipc_eval(r#"R.home("library") %in% .libPaths()"#)
.expect("eval should run");
#[cfg(target_os = "macos")]
let result3 = process
.ipc_eval(r#"dir.exists(R.home("library"))"#)
.expect("eval should run");
assert!(result3.success, "eval should succeed: {}", result3.stderr);
let json3 = parse_ipc_json(&result3);
#[cfg(not(target_os = "macos"))]
assert_eq!(
json3["value"].as_str(),
Some("[1] TRUE"),
r#"R.home("library") should be in .libPaths(): {}"#,
result3.stdout
);
#[cfg(target_os = "macos")]
assert_eq!(
json3["value"].as_str(),
Some("[1] TRUE"),
r#"R.home("library") should exist on disk: {}"#,
result3.stdout
);
}