mirror of
https://github.com/qingjian-team/qingjian.git
synced 2026-11-06 16:13:55 +00:00
- `ipc_loop.rs` / `pipe.rs`:开会话现在多回一条 `SessionOpened`,期望的条数从 5 改成 6 (`pipe.rs` 那个是读 6 条、只取其中的 `KeyResult`) - `protocol_loop.rs`:`EngineClient::open` 改成返回元组之后这个文件没跟着改 —— 它是 `#![cfg(unix)]`,Windows 上根本编不到,只有 CI 会红 - `Activate`:`connect()` 已经把 Server 下发的设置应用了,紧接着的 `apply_input_settings(InputSettings::default())` 又把它盖成缺省值,要等下一拍 `SyncMode` 才改回来 —— 关掉内置英文模式的人每次激活都会先登记上中 / 英按钮、之后也不会撤。缺省值改成只在没连上 Server 时用。 - `eats_key`:字母判定回到原来的口径(组句外 Shift + 大写归应用,V / U / I 例外), `push_shifted` / `apply_chinese` 那条属于另一个改动,这个 PR 不带。 另外按 review 去掉了「`-` `=` 由壳自己插入」那个提交:main 上已经有通用机制 (Server 放行没吃掉的按键时由 DLL 自己把字符插进文档),不用为它单开一条路。
103 lines
3.3 KiB
Rust
103 lines
3.3 KiB
Rust
//! 真命名管道的端到端测试(仅 Windows):监听线程 + 文件句柄客户端,走完「开会话 → 敲 nihao → 收候选」。
|
|
|
|
#![cfg(windows)]
|
|
|
|
use std::fs::OpenOptions;
|
|
use std::path::PathBuf;
|
|
use std::thread;
|
|
use std::time::Duration;
|
|
|
|
use qingjian_core::Language;
|
|
use qingjian_platform::protocol::{
|
|
ClientMessage, KeyEvent, PROTOCOL_VERSION, ServerMessage, SessionId,
|
|
};
|
|
use qingjian_windows_server::ipc::pipe::serve_pipe;
|
|
use qingjian_windows_server::ipc::{read_message, write_message};
|
|
use qingjian_windows_server::{AssemblySpec, Router, RouterConfig, assembly};
|
|
|
|
const SESSION: SessionId = SessionId(1);
|
|
|
|
fn letter(c: char) -> KeyEvent {
|
|
KeyEvent::new(c.to_ascii_uppercase() as u32, Some(c), Default::default())
|
|
}
|
|
|
|
/// 客户端重试打开管道,直到监听线程建好实例。
|
|
fn connect(name: &str) -> std::fs::File {
|
|
for _ in 0..50 {
|
|
match OpenOptions::new().read(true).write(true).open(name) {
|
|
Ok(file) => return file,
|
|
Err(_) => thread::sleep(Duration::from_millis(50)),
|
|
}
|
|
}
|
|
panic!("连不上管道 {name}");
|
|
}
|
|
|
|
#[test]
|
|
fn named_pipe_round_trips_the_open_type_loop() {
|
|
let name = format!(r"\\.\pipe\qingjian-test-{}", std::process::id());
|
|
|
|
// 监听线程服务完一个客户端后阻塞等下一个,随进程退出即可。
|
|
let server_name = name.clone();
|
|
thread::spawn(move || {
|
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../..");
|
|
let engine = assembly::assemble(&AssemblySpec {
|
|
glossary: Some((
|
|
Language::English,
|
|
root.join("assets/sample/glossary-en.tsv"),
|
|
)),
|
|
..AssemblySpec::new(root.join("assets/sample/dict.tsv"))
|
|
})
|
|
.expect("assemble engine from sample data");
|
|
let mut router = Router::new(engine, RouterConfig::default());
|
|
let (work_tx, work_rx) = std::sync::mpsc::channel();
|
|
let _ = serve_pipe(&server_name, &mut router, work_tx, work_rx);
|
|
});
|
|
|
|
let mut client = connect(&name);
|
|
|
|
write_message(
|
|
&mut client,
|
|
&ClientMessage::OpenSession {
|
|
session: SESSION,
|
|
app: None,
|
|
protocol: PROTOCOL_VERSION,
|
|
},
|
|
)
|
|
.unwrap();
|
|
for c in "nihao".chars() {
|
|
write_message(
|
|
&mut client,
|
|
&ClientMessage::Key {
|
|
session: SESSION,
|
|
event: letter(c),
|
|
},
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
// 开会话先回一条 `SessionOpened`,之后五个按键各回一条 `KeyResult`。
|
|
let mut last_frame = None;
|
|
for _ in 0..6 {
|
|
let message: ServerMessage = read_message(&mut client)
|
|
.expect("read response")
|
|
.expect("server closed early");
|
|
if let ServerMessage::KeyResult { frame, .. } = message {
|
|
last_frame = Some(frame);
|
|
}
|
|
}
|
|
|
|
let frame = last_frame.expect("至少一条 KeyResult");
|
|
let preedit: String = frame.preedit.iter().map(|s| s.text.as_str()).collect();
|
|
assert_eq!(preedit, "ni'hao");
|
|
let texts: Vec<&str> = frame
|
|
.candidates
|
|
.items
|
|
.iter()
|
|
.map(|c| c.text.as_str())
|
|
.collect();
|
|
assert!(
|
|
texts.contains(&"你好"),
|
|
"候选里应有「你好」,实际:{texts:?}"
|
|
);
|
|
}
|