#![cfg(target_os = "linux")]
mod common;
use std::{
io::Read,
process::{Command, Stdio},
thread,
time::{Duration, Instant},
};
use common::{socat_available, wait_with_timeout, PtyPair};
#[test]
fn save_cli_flag_persists_profile() {
if !socat_available() {
eprintln!("skipping save_cli_flag_persists_profile: socat not available");
return;
}
let pty = match PtyPair::new() {
Ok(p) => p,
Err(e) => {
eprintln!("skipping save_cli_flag_persists_profile: {e}");
return;
}
};
let tempdir = tempfile::tempdir().expect("create tempdir");
let profile_path = tempdir.path().join("default.toml");
let binary = assert_cmd::cargo::cargo_bin("rtcom");
let mut child = Command::new(&binary)
.arg(&pty.master)
.args(["-b", "9600", "--save", "-c"])
.arg(&profile_path)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("spawn rtcom");
let mut loaded: Option<rtcom_config::Profile> = None;
for _ in 0..30 {
if profile_path.exists() {
if let Ok(p) = rtcom_config::read(&profile_path) {
if p.serial.baud == 9600 {
loaded = Some(p);
break;
}
}
}
thread::sleep(Duration::from_millis(100));
}
let status = wait_with_timeout(&mut child, Duration::from_secs(2));
if status.is_none() {
let _ = child.kill();
let _ = child.wait();
}
assert!(
loaded.is_some(),
"profile was not written with baud=9600 at {}",
profile_path.display()
);
}
#[test]
fn profile_load_controls_initial_config() {
if !socat_available() {
eprintln!("skipping profile_load_controls_initial_config: socat not available");
return;
}
let pty = match PtyPair::new() {
Ok(p) => p,
Err(e) => {
eprintln!("skipping profile_load_controls_initial_config: {e}");
return;
}
};
let tempdir = tempfile::tempdir().expect("create tempdir");
let profile_path = tempdir.path().join("pre.toml");
let mut pre = rtcom_config::Profile::default();
pre.serial.baud = 19_200;
rtcom_config::write(&profile_path, &pre).expect("seed profile");
let stderr = run_rtcom_collecting_stderr(&[
pty.master.as_os_str().to_str().expect("pty path utf-8"),
"-c",
profile_path.to_str().expect("profile path utf-8"),
]);
assert!(
!stderr.contains("unreadable"),
"profile at {} unexpectedly reported unreadable; stderr was:\n{}",
profile_path.display(),
stderr,
);
assert!(
!stderr.contains("parse error") && !stderr.contains("Parse"),
"profile load surfaced a parse error; stderr was:\n{stderr}",
);
}
#[test]
fn missing_config_path_is_not_fatal() {
if !socat_available() {
eprintln!("skipping missing_config_path_is_not_fatal: socat not available");
return;
}
let pty = match PtyPair::new() {
Ok(p) => p,
Err(e) => {
eprintln!("skipping missing_config_path_is_not_fatal: {e}");
return;
}
};
let stderr = run_rtcom_collecting_stderr(&[
pty.master.as_os_str().to_str().expect("pty path utf-8"),
"-c",
"/nonexistent/path/to/profile.toml",
]);
assert!(
!stderr.contains("unreadable"),
"missing profile unexpectedly reported unreadable; stderr was:\n{stderr}",
);
assert!(
!stderr.contains("--save failed"),
"missing profile unexpectedly flagged a save error; stderr was:\n{stderr}",
);
}
fn run_rtcom_collecting_stderr(args: &[&str]) -> String {
let binary = assert_cmd::cargo::cargo_bin("rtcom");
let mut child = Command::new(&binary)
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("spawn rtcom");
let status = wait_with_timeout(&mut child, Duration::from_secs(2));
if status.is_none() {
let _ = child.kill();
let _ = child.wait();
}
let mut stderr_buf = String::new();
if let Some(mut stderr) = child.stderr.take() {
let start = Instant::now();
let mut buf = [0u8; 4096];
loop {
match stderr.read(&mut buf) {
Ok(n) if n > 0 => stderr_buf.push_str(&String::from_utf8_lossy(&buf[..n])),
_ => break,
}
if start.elapsed() > Duration::from_secs(1) {
break;
}
}
}
stderr_buf
}