mirror of
https://github.com/qingjian-team/qingjian.git
synced 2026-11-15 08:15:14 +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 自己把字符插进文档),不用为它单开一条路。
128 lines
3.8 KiB
Rust
128 lines
3.8 KiB
Rust
//! 传输层测试:帧编解码,以及在内存流上跑 serve 循环;不碰命名管道,跨平台可跑。
|
|
|
|
use std::io::{Cursor, Read, Write};
|
|
use std::path::PathBuf;
|
|
|
|
use qingjian_core::Language;
|
|
use qingjian_platform::protocol::{
|
|
ClientMessage, KeyEvent, PROTOCOL_VERSION, ServerMessage, SessionId,
|
|
};
|
|
use qingjian_windows_server::ipc::{read_message, serve, write_message};
|
|
use qingjian_windows_server::{AssemblySpec, Router, RouterConfig, assembly};
|
|
|
|
const SESSION: SessionId = SessionId(1);
|
|
|
|
fn router() -> Router {
|
|
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");
|
|
Router::new(engine, RouterConfig::default())
|
|
}
|
|
|
|
fn letter(c: char) -> KeyEvent {
|
|
KeyEvent::new(c.to_ascii_uppercase() as u32, Some(c), Default::default())
|
|
}
|
|
|
|
#[test]
|
|
fn codec_round_trips_a_message() {
|
|
let original = ClientMessage::Key {
|
|
session: SESSION,
|
|
event: letter('n'),
|
|
};
|
|
let mut buffer = Vec::new();
|
|
write_message(&mut buffer, &original).unwrap();
|
|
|
|
let mut reader = Cursor::new(buffer);
|
|
let decoded: Option<ClientMessage> = read_message(&mut reader).unwrap();
|
|
assert_eq!(decoded, Some(original));
|
|
let end: Option<ClientMessage> = read_message(&mut reader).unwrap();
|
|
assert_eq!(end, None);
|
|
}
|
|
|
|
#[test]
|
|
fn serve_runs_the_open_type_loop_over_a_stream() {
|
|
let mut input = Vec::new();
|
|
write_message(
|
|
&mut input,
|
|
&ClientMessage::OpenSession {
|
|
session: SESSION,
|
|
app: None,
|
|
protocol: PROTOCOL_VERSION,
|
|
},
|
|
)
|
|
.unwrap();
|
|
for c in "nihao".chars() {
|
|
write_message(
|
|
&mut input,
|
|
&ClientMessage::Key {
|
|
session: SESSION,
|
|
event: letter(c),
|
|
},
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
let mut router = router();
|
|
let mut stream = Duplex {
|
|
inbound: Cursor::new(input),
|
|
outbound: Vec::new(),
|
|
};
|
|
serve(&mut stream, &mut router).unwrap();
|
|
|
|
let mut responses = Vec::new();
|
|
let mut out = Cursor::new(stream.outbound);
|
|
while let Some(message) = read_message::<_, ServerMessage>(&mut out).unwrap() {
|
|
responses.push(message);
|
|
}
|
|
// 开会话先回一条 `SessionOpened`(把按键行为设置带下来),之后五个按键各回一条 `KeyResult`。
|
|
assert_eq!(responses.len(), 6, "一条 SessionOpened 加五条 KeyResult");
|
|
assert!(
|
|
matches!(responses.first(), Some(ServerMessage::SessionOpened { .. })),
|
|
"首条应是 SessionOpened,实际:{:?}",
|
|
responses.first()
|
|
);
|
|
|
|
let ServerMessage::KeyResult { frame, .. } = responses.last().unwrap() else {
|
|
panic!("末条应是 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:?}"
|
|
);
|
|
}
|
|
|
|
/// 预置输入 + 输出缓冲拼成的流(先发后收够测)。
|
|
struct Duplex {
|
|
inbound: Cursor<Vec<u8>>,
|
|
outbound: Vec<u8>,
|
|
}
|
|
|
|
impl Read for Duplex {
|
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
self.inbound.read(buf)
|
|
}
|
|
}
|
|
|
|
impl Write for Duplex {
|
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
|
self.outbound.write(buf)
|
|
}
|
|
fn flush(&mut self) -> std::io::Result<()> {
|
|
self.outbound.flush()
|
|
}
|
|
}
|