- Added scripts to prepare and verify the Microsoft ConPTY package for Windows. - Updated build workflows to include checks for bundled ConPTY in installers. - Enhanced application support info to display ConPTY status and version. - Modified the portable updater to handle ConPTY files during updates. - Updated documentation to reflect changes in ConPTY usage and requirements. - Introduced tests for ConPTY backend functionality to ensure reliability.
127 lines
3.8 KiB
Rust
127 lines
3.8 KiB
Rust
#![cfg(windows)]
|
|
|
|
use portable_pty::win::{ConptyBackend, configure_bundled_conpty, conpty_status};
|
|
use portable_pty::{PtySize, native_pty_system};
|
|
use std::path::PathBuf;
|
|
|
|
const CASE_ENV: &str = "NYATERM_CONPTY_TEST_CASE";
|
|
|
|
fn bundled_dll() -> PathBuf {
|
|
let arch = if cfg!(target_arch = "aarch64") {
|
|
"arm64"
|
|
} else {
|
|
"x64"
|
|
};
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
.join("resources/windows/conpty")
|
|
.join(arch)
|
|
.join("conpty.dll")
|
|
}
|
|
|
|
#[test]
|
|
fn conpty_backend_cases() {
|
|
if let Ok(case) = std::env::var(CASE_ENV) {
|
|
run_case(&case);
|
|
return;
|
|
}
|
|
|
|
assert!(bundled_dll().is_file(), "run pnpm prepare:conpty first");
|
|
for case in ["bundled", "missing_dll", "corrupt_dll", "missing_host"] {
|
|
let child = std::process::Command::new(std::env::current_exe().unwrap())
|
|
.args(["--exact", "conpty_backend_cases", "--nocapture"])
|
|
.env(CASE_ENV, case)
|
|
.stdout(std::process::Stdio::piped())
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
let temp = std::env::temp_dir().join(format!("nyaterm-conpty-test-{}", child.id()));
|
|
let output = child.wait_with_output().unwrap();
|
|
let _ = std::fs::remove_dir_all(temp);
|
|
assert!(
|
|
output.status.success(),
|
|
"{case} failed:\n{}\n{}",
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
}
|
|
}
|
|
|
|
fn run_case(case: &str) {
|
|
let temp = std::env::temp_dir().join(format!("nyaterm-conpty-test-{}", std::process::id()));
|
|
std::fs::create_dir_all(&temp).unwrap();
|
|
let dll = match case {
|
|
"bundled" => bundled_dll(),
|
|
"missing_dll" => temp.join("missing.dll"),
|
|
"corrupt_dll" => {
|
|
let path = temp.join("conpty.dll");
|
|
std::fs::write(&path, b"not a DLL").unwrap();
|
|
let source_dir = bundled_dll().parent().unwrap().to_path_buf();
|
|
for arch in if cfg!(target_arch = "aarch64") {
|
|
&["arm64"][..]
|
|
} else {
|
|
&["x64", "arm64"][..]
|
|
} {
|
|
let host_dir = temp.join(arch);
|
|
std::fs::create_dir_all(&host_dir).unwrap();
|
|
std::fs::copy(
|
|
source_dir.join(arch).join("OpenConsole.exe"),
|
|
host_dir.join("OpenConsole.exe"),
|
|
)
|
|
.unwrap();
|
|
}
|
|
path
|
|
}
|
|
"missing_host" => {
|
|
let path = temp.join("conpty.dll");
|
|
std::fs::copy(bundled_dll(), &path).unwrap();
|
|
path
|
|
}
|
|
other => panic!("unknown ConPTY test case: {other}"),
|
|
};
|
|
configure_bundled_conpty(dll).unwrap();
|
|
assert!(conpty_status().last_used.is_none());
|
|
|
|
let pair = native_pty_system()
|
|
.openpty(PtySize {
|
|
rows: 24,
|
|
cols: 80,
|
|
pixel_width: 0,
|
|
pixel_height: 0,
|
|
})
|
|
.unwrap();
|
|
pair.master
|
|
.resize(PtySize {
|
|
rows: 25,
|
|
cols: 81,
|
|
pixel_width: 0,
|
|
pixel_height: 0,
|
|
})
|
|
.unwrap();
|
|
|
|
let status = conpty_status();
|
|
let expected = if case == "bundled" {
|
|
ConptyBackend::Bundled
|
|
} else {
|
|
ConptyBackend::System
|
|
};
|
|
assert_eq!(status.last_used, Some(expected));
|
|
assert_eq!(
|
|
status.active_bundled,
|
|
usize::from(expected == ConptyBackend::Bundled)
|
|
);
|
|
assert_eq!(
|
|
status.active_system,
|
|
usize::from(expected == ConptyBackend::System)
|
|
);
|
|
assert_eq!(
|
|
status.fallback_reason.is_some(),
|
|
expected == ConptyBackend::System
|
|
);
|
|
|
|
drop(pair);
|
|
let status = conpty_status();
|
|
assert_eq!(status.active_bundled, 0);
|
|
assert_eq!(status.active_system, 0);
|
|
assert_eq!(status.last_used, Some(expected));
|
|
}
|