* fix(windows,engine): 提交键回复等待不再因超时重置会话或丢字 服务端按键处理偶发数百毫秒尖峰时,提交键(空格顶首选 / 确定选词 / 标点顶高亮候选) 的回复等待会走到会话内那条 300ms 同步读;一超时就被判成 TransportUnavailable → DeliveryAmbiguous → Transport 完整重置(丢键 + 吞队列 + 清组合 + 轮换 epoch), 用户看到的是整段已输入内容消失。 - 删除 FANY_IME_COMMIT_REPLY_SESSION_WAIT_MS 那条破坏性兜底读。会话侧改为非阻塞探测, 拿不到回复返回「未就绪」,由 session 自身在 DoEditSession 内交回,沿用同一个 FANY_IME_COMMIT_REPLY_TIMEOUT_MS 分片轮询窗口,不重开窗口、不新造等待原语。 - 交回必须发生在 session 内部:RequestEditSession 用 TF_ES_ASYNCDONTCARE,TSF 异步授权 时调用方读不到 session 的 HRESULT;若靠调用方读返回值,该键会既不丢也不提交, deferred 闸门再不打开(后续按键全部只入队)。也不能改用 TF_ES_SYNC(WinWord 上不成立)。 - 真失败(写失败 / 读失败 / 坏帧 / 字节数不符 / 无 request id / 无管道)仍判 Transport 重置。 - 预取覆盖面扩到 candidate 类 finalize(空格顶首选等),堵住原先只能靠那条 300ms 兜底读的路径。 - 新增按键级延迟诊断 bench_key_latency_diag(build 里有同名 exe 但源码已不在树), 以及两份对照测试:commit_reply_not_ready(回复三态与交回状态机)、 candidate_consistency(同配置下候选集合/顺序/分页逐字一致,引擎侧护栏)。 实测(bench_key_latency_diag):稳态逐键 median 0.6~0.8ms / max 12.5ms,尖峰指向内存 压力下的页回收而非稳态算力。在「结果完全不变」的约束下,预热 / 缓存 / 投机预解码均无效 或反向,故本次不改引擎打分路径。 * fix(windows): 直接标点提交会话补齐未就绪交回,并写准窗口沿用边界 审查 `7aca5e5d` 发现的问题: - `CPunctuationCommitEditSession` 在 `FANY_S_COMMIT_REPLY_NOT_READY` 时只抑制了 token 结算(`applied` / `_FailDeferredKey` 都不走),**没有**像 `CKeyHandlerEditSession` 那样把 请求交回 `_PollCommitReply`。一旦走到这条分支——`_HandleCompositionPunctuation` 的高亮候选 分支要求 `_candidateMode != CANDIDATE_NONE`,而派发该会话的条件是 `_candidateMode == CANDIDATE_NONE`,两者在 session 执行时才求值——deferred token 会永久留在 飞行中,后续所有按键只入队不处理,正是本 PR 在别处修掉的失效模式。现在两处都交回, 并把原因写进注释,避免下一个人再照半个写法抄一遍。 - `_HandOffUnreadyCommitReply` 的两处注释与实现不符或不够具体:交回沿用第一片已经开始的窗口; 只有从未开窗的路径(标点处理函数在派发时高亮候选条件为假、session 执行时条件成立)才在此 开窗,那种情况键一点没等过、`waitedMs` 为 0。另一支(无法保活 → `_FailUnansweredCommitKey`) 只在请求不属于协作轮询时到达,实务上即 `commitReplyMessage == 0`:派发没能用消息窗, 没有可交回的对象。 签名相应扩展:`_RequestDirectPunctuationEditSession` 新增 `commitReplyMessage` / `commitReplyDeadlineTick`,`CPunctuationCommitEditSession` 携带它们。没有行为回退,两个新参数 默认 0 时与改动前一致(而那正是「无消息窗」的既有降级路径)。 验证:windows x64 Release 构建通过,`ctest` 17/17 通过,`clang-format 18.1.8 --dry-run --Werror --style=file` 对本次涉及文件全过。 * fix(windows): 把 _HandOffUnreadyCommitReply 移到公共区,修 CI 编译失败 CI 的 Windows x64 与 Windows Win32 两条腿都挂在同一处编译错误: MetasequoiaIME_AsyncRequests.cpp: error C2248: “CMetasequoiaIME::_HandOffUnreadyCommitReply”: 无法访问 private 成员 上一提交给 CPunctuationCommitEditSession 补「未就绪交回」时调用了这个成员,但它在 private 区。CKeyHandlerEditSession 能调是因为它在 MetasequoiaIME.h 里被显式声明为 friend;而 CPunctuationCommitEditSession 定义在 MetasequoiaIME_AsyncRequests.cpp 的匿名命名空间里 (该文件 31-254 行),头文件里的 friend 声明指不到它,加 friend 也无效。 处置:把该声明移到公共区,与同样由 session 调用的 _FailDeferredKey、_CompleteDeferredKeyReplay、 _HandleCompositionPunctuation 并列,并在注释里写明「它必须公共」的原因,免得下次有人顺手挪回私区。 仅可见性与注释,行为零变化。 验证(triplet 与 CI 一致,且这次看退出码、不截断构建输出): - x64-windows-static:cmake --build build --config Release --parallel 退出码 0; ctest --no-tests=error 17/17。 - x86-windows-static:cmake --build build32 --config Release --parallel 退出码 0; ctest --no-tests=error 17/17。 - clang-format 18.1.8 --dry-run --Werror --style=file 对本次改动文件通过。 另:上一轮「构建通过」是我误报——当时用 Select-String 过滤构建输出又加了 -Last 8,错误行被截掉, 且没有检查 $LASTEXITCODE;ctest 过的是测试目标,与 DLL 编译失败并不冲突。 * fix(windows): 提交回复交回后轮询不再停摆,同步兜底保留有界等待 - 轮询定时器按请求 id 判断是否仍在等待:session 交回时换了新 token, 按旧 token 查会误判为已结算并 KillTimer,交回的请求从此无人再分发。 - 没有轮询可交回的 session(同步 _InvokeKeyHandler 路径)恢复 FANY_IME_COMMIT_REPLY_SESSION_WAIT_MS 的单次有界等待,不再一读不到就丢键重置。 - 确定并输入在提交回复为传输失败时不再继续键入字符并返回 S_OK。 - session 交回占用新槽位时同样受 kMaxPendingAsyncKeys 约束。 - 修正注释:未就绪由 session 自己的完成钩子交回,不是消息处理函数。 - 测试:覆盖轮询分发、等待上限与满表交回;SAL 剥离按最长前缀匹配; 引擎一致性用例③不再清缓存,否则测不到跨输入的缓存泄漏。 --------- Co-authored-by: fanlusky <1730976608@qq.com>
132 lines
7.8 KiB
CMake
132 lines
7.8 KiB
CMake
cmake_minimum_required(VERSION 3.25)
|
|
|
|
# Also usable without the TSF DLL, the Windows SDK or the engine.
|
|
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
|
project(MsimeTsfLocalPolicyTests LANGUAGES CXX)
|
|
enable_testing()
|
|
# windows/CMakeLists.txt sets this for the in-tree build; the sources and headers are UTF-8.
|
|
if(MSVC)
|
|
add_compile_options(/utf-8)
|
|
endif()
|
|
endif()
|
|
|
|
add_executable(test_vim_mode vim_mode.cpp ../src/Key/VimMode.cpp)
|
|
target_include_directories(test_vim_mode PRIVATE ../src)
|
|
target_compile_features(test_vim_mode PRIVATE cxx_std_17)
|
|
add_test(NAME vim_mode COMMAND test_vim_mode)
|
|
|
|
add_executable(test_client_fallback client_fallback.cpp)
|
|
target_include_directories(test_client_fallback PRIVATE ../src)
|
|
target_compile_features(test_client_fallback PRIVATE cxx_std_17)
|
|
add_test(NAME client_fallback COMMAND test_client_fallback)
|
|
|
|
add_executable(test_key_repeat_guard key_repeat_guard.cpp)
|
|
target_include_directories(test_key_repeat_guard PRIVATE ../src)
|
|
target_compile_features(test_key_repeat_guard PRIVATE cxx_std_17)
|
|
add_test(NAME key_repeat_guard COMMAND test_key_repeat_guard)
|
|
|
|
add_executable(test_caps_lock_punctuation_policy caps_lock_punctuation_policy.cpp)
|
|
target_include_directories(test_caps_lock_punctuation_policy PRIVATE ../src)
|
|
target_compile_features(test_caps_lock_punctuation_policy PRIVATE cxx_std_17)
|
|
add_test(NAME caps_lock_punctuation_policy COMMAND test_caps_lock_punctuation_policy)
|
|
|
|
add_executable(test_caps_lock_input_policy caps_lock_input_policy.cpp)
|
|
target_include_directories(test_caps_lock_input_policy PRIVATE ../src)
|
|
target_compile_features(test_caps_lock_input_policy PRIVATE cxx_std_17)
|
|
add_test(NAME caps_lock_input_policy COMMAND test_caps_lock_input_policy)
|
|
|
|
add_executable(test_commit_candidate_and_continue_payload commit_candidate_and_continue_payload.cpp)
|
|
target_include_directories(test_commit_candidate_and_continue_payload PRIVATE ../src)
|
|
target_compile_features(test_commit_candidate_and_continue_payload PRIVATE cxx_std_17)
|
|
add_test(NAME commit_candidate_and_continue_payload COMMAND test_commit_candidate_and_continue_payload)
|
|
|
|
add_executable(test_punctuation_commit_text punctuation_commit_text.cpp)
|
|
target_include_directories(test_punctuation_commit_text PRIVATE ../src)
|
|
target_compile_features(test_punctuation_commit_text PRIVATE cxx_std_17)
|
|
add_test(NAME punctuation_commit_text COMMAND test_punctuation_commit_text)
|
|
|
|
add_executable(test_input_state_shortcuts input_state_shortcuts.cpp)
|
|
target_compile_features(test_input_state_shortcuts PRIVATE cxx_std_17)
|
|
target_compile_definitions(test_input_state_shortcuts PRIVATE MSIME_SHORTCUT_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../engine/contracts/tests/input_state_shortcuts.json")
|
|
add_test(NAME input_state_shortcuts COMMAND test_input_state_shortcuts)
|
|
|
|
# The classifier and the event queue are compiled into this target directly:
|
|
# they must stay free of TSF and DLL lifecycle dependencies.
|
|
add_executable(test_statistics statistics.cpp ../src/Statistics/char_classify.cpp)
|
|
target_include_directories(test_statistics PRIVATE ../src)
|
|
target_compile_features(test_statistics PRIVATE cxx_std_17)
|
|
add_test(NAME statistics COMMAND test_statistics)
|
|
|
|
add_executable(test_key_focus_recovery key_focus_recovery.cpp)
|
|
target_include_directories(test_key_focus_recovery PRIVATE ../src)
|
|
target_compile_features(test_key_focus_recovery PRIVATE cxx_std_17)
|
|
add_test(NAME key_focus_recovery COMMAND test_key_focus_recovery)
|
|
|
|
add_executable(test_deferred_key_failure_policy deferred_key_failure_policy.cpp)
|
|
target_include_directories(test_deferred_key_failure_policy PRIVATE ../src)
|
|
target_compile_features(test_deferred_key_failure_policy PRIVATE cxx_std_17)
|
|
add_test(NAME deferred_key_failure_policy COMMAND test_deferred_key_failure_policy)
|
|
|
|
add_executable(test_caret_anchor_policy caret_anchor_policy.cpp)
|
|
target_include_directories(test_caret_anchor_policy PRIVATE ../src)
|
|
target_compile_features(test_caret_anchor_policy PRIVATE cxx_std_17)
|
|
add_test(NAME caret_anchor_policy COMMAND test_caret_anchor_policy)
|
|
|
|
add_executable(test_focus_announcement_policy focus_announcement_policy.cpp)
|
|
target_include_directories(test_focus_announcement_policy PRIVATE ../src)
|
|
target_compile_features(test_focus_announcement_policy PRIVATE cxx_std_17)
|
|
add_test(NAME focus_announcement_policy COMMAND test_focus_announcement_policy)
|
|
|
|
# Exercise the production reconnect sequencing without loading a TIP into a host.
|
|
# Regenerate the fixture when either handler changes, so a missing recovery step
|
|
# cannot be hidden by a copied implementation in the test.
|
|
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
|
set(reconnect_window_source "${CMAKE_CURRENT_SOURCE_DIR}/../src/IME/MetasequoiaIME_WindowProc.cpp")
|
|
set(reconnect_key_source "${CMAKE_CURRENT_SOURCE_DIR}/../src/Key/KeyEventSink_DeferredKeys.cpp")
|
|
set(reconnect_fixture_generator "${CMAKE_CURRENT_SOURCE_DIR}/generate_reconnect_fixture.py")
|
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
|
|
"${reconnect_window_source}" "${reconnect_key_source}" "${reconnect_fixture_generator}")
|
|
execute_process(
|
|
COMMAND "${Python3_EXECUTABLE}" "${reconnect_fixture_generator}"
|
|
"${reconnect_window_source}" "${reconnect_key_source}"
|
|
"${CMAKE_CURRENT_BINARY_DIR}/reconnect_recovery_source.h"
|
|
COMMAND_ERROR_IS_FATAL ANY)
|
|
add_executable(test_reconnect_recovery reconnect_recovery.cpp)
|
|
target_include_directories(test_reconnect_recovery PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
|
|
target_compile_features(test_reconnect_recovery PRIVATE cxx_std_17)
|
|
add_test(NAME reconnect_recovery COMMAND test_reconnect_recovery)
|
|
|
|
# Link the same inline key classifier used by normal and queued TSF paths.
|
|
if(WIN32)
|
|
add_executable(test_temporary_japanese_input temporary_japanese_input.cpp)
|
|
target_include_directories(test_temporary_japanese_input PRIVATE ../src/Header)
|
|
target_compile_features(test_temporary_japanese_input PRIVATE cxx_std_17)
|
|
add_test(NAME temporary_japanese_input COMMAND test_temporary_japanese_input)
|
|
endif()
|
|
|
|
# Exercise the session-side commit-reply contract without loading a TIP into a host. The paths
|
|
# that decide what an unready committing reply costs are compiled from the production sources
|
|
# (see generate_commit_reply_fixture.py); only the pipe probe, the message window and the clock
|
|
# are doubles. Regenerate whenever one of those paths changes, so a removed step cannot hide
|
|
# behind a copy in the test.
|
|
set(commit_reply_ipc_header "${CMAKE_CURRENT_SOURCE_DIR}/../src/IPC/Ipc.h")
|
|
set(commit_reply_ipc_source "${CMAKE_CURRENT_SOURCE_DIR}/../src/IPC/Ipc.cpp")
|
|
set(commit_reply_ime_header "${CMAKE_CURRENT_SOURCE_DIR}/../src/IME/MetasequoiaIME.h")
|
|
set(commit_reply_async_source "${CMAKE_CURRENT_SOURCE_DIR}/../src/IME/MetasequoiaIME_AsyncRequests.cpp")
|
|
set(commit_reply_session_source "${CMAKE_CURRENT_SOURCE_DIR}/../src/Key/KeyHandlerEditSession.cpp")
|
|
set(commit_reply_fixture_generator "${CMAKE_CURRENT_SOURCE_DIR}/generate_commit_reply_fixture.py")
|
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
|
|
"${commit_reply_ipc_header}" "${commit_reply_ipc_source}" "${commit_reply_ime_header}"
|
|
"${commit_reply_async_source}" "${commit_reply_session_source}" "${commit_reply_fixture_generator}")
|
|
execute_process(
|
|
COMMAND "${Python3_EXECUTABLE}" "${commit_reply_fixture_generator}"
|
|
"${commit_reply_ipc_header}" "${commit_reply_ipc_source}" "${commit_reply_ime_header}"
|
|
"${commit_reply_async_source}" "${commit_reply_session_source}"
|
|
"${CMAKE_CURRENT_BINARY_DIR}/commit_reply_types_source.h"
|
|
"${CMAKE_CURRENT_BINARY_DIR}/commit_reply_source.h"
|
|
COMMAND_ERROR_IS_FATAL ANY)
|
|
add_executable(test_commit_reply_not_ready commit_reply_not_ready.cpp)
|
|
target_include_directories(test_commit_reply_not_ready PRIVATE ../src "${CMAKE_CURRENT_BINARY_DIR}")
|
|
target_compile_features(test_commit_reply_not_ready PRIVATE cxx_std_17)
|
|
add_test(NAME commit_reply_not_ready COMMAND test_commit_reply_not_ready)
|