cua-driver 跨操作系统工具面冒烟测试:per-OS 全工具 PASS/FAIL/SKIP 探测与基线回归对比
【免费下载链接】cuaScale computer-use 2.0 with open-source drivers, cross-OS fleets, and benchmarks for training, evaluation, and data generation.项目地址: https://gitcode.com/GitHub_Trending/cua/cua
本篇基于 smoke 测试说明文档 讲解 cua-driver 的跨操作系统 CLI 冒烟测试体系:它与 MCP stdio 集成测试如何分工、macOS 冒烟脚本如何逐个探测全部已注册工具并归类为 PASS/FAIL/SKIP,以及如何通过基线结果文件 diff 捕获工具覆盖度回归。读完你可以理解这套"约 30 秒扫完全工具面"的验证策略,并能按同样方式为自己关心的 OS 增加一条冒烟跑道。
两层验证:MCP 集成测试 vs CLI 冒烟探测
smoke 目录的 README 开宗明义:这里的 runner 是对每一台宿主 OS 上 cua-driver完整工具面做一遍广撒网式的 PASS / FAIL / SKIP 探测。它明确说明了与 Rust 集成测试的分工边界:
- 集成测试(位于
libs/cua-driver/rust/crates/cua-driver/tests/)驱动 cua-driver 走MCP stdio 回路,断言具体的 UIA / AX 状态变化,属于"深而准"的行为验证; - 冒烟 runner驱动 cua-driver 走CLI(
cua-driver call <tool> <json>),走的是另一条代码路径——CLI 参数解析器加工具解析——用约 30 秒对每一个注册工具做一次广覆盖扫描。
两条路径的入口差异可以在源码中印证:CLI 侧的call子命令解析与工具名提取实现在 cli.rs(如finite_tool_name_from_args(&args(&["call", "click", r#"{\"x\":1}"#]))一类用例所覆盖的逻辑)。正因 CLI 与 MCP stdio 的入参解析、工具解析链路不同,冒烟测试的价值不是"重复集成测试",而是确保每一个注册工具都能被 CLI 正确寻址并干净退出——这是集成测试断言式覆盖难以低成本穷举的层面。
目录定位:smoke runner 在 fixture 体系中的位置
smoke 目录是 cua-driver 测试 fixture 体系的一部分。该 README 给出的整体布局是:
tests/fixtures/ ├── shared/ │ ├── scenarios.json # scenario ids, titles, and expected controls │ └── web/index.html # shared DOM for webview-style harnesses ├── apps/ │ ├── cross-platform/ │ │ ├── electron/ # Chromium/Electron host, CDP port 9223 │ │ └── tauri/ # native webview/Tauri host │ ├── linux/ │ │ └── gtk3/ # PyGObject GTK3 app │ ├── macos/ │ │ ├── appkit/ # single-file Swift AppKit app │ │ ├── swiftui/ # single-file SwiftUI app │ │ └── wkwebview/ # native WKWebView host for shared DOM │ └── windows/ │ ├── wpf/ # .NET WPF app │ ├── winui3/ # unpackaged WinUI3 app │ └── webview2/ # WPF + WebView2 host for shared DOM ├── build/ # host build scripts └── smoke/ # lightweight local smoke runners关键点在于:fixture 是source-first的——构建脚本把本地产物暂存(stage)到libs/cua-driver/rust/test-apps/harness-<name>/,二进制本身不入库。smoke runner 所依赖的"受害应用"(victim app)正是这一暂存机制的产物。fixture 应用与暂存输出的映射关系在 fixture 应用总表中维护,例如 AppKit 源码在apps/macos/appkit,暂存输出为harness-appkit;完整应用矩阵(AppKit / SwiftUI / WKWebView / WPF / WinUI3 / WebView2 / GTK3 / Electron / Tauri)与各自覆盖的可访问性面(AX、UIA、AT-SPI 等)见该文件的表格。
构建方式在 fixture 构建说明中给出:macOS 上运行libs/cua-driver/tests/fixtures/build/macos.sh(支持--skip <name>、--only <name>选择目标),Windows 上运行tests/fixtures/build/windows.ps1(支持-Skip参数)。宿主依赖方面,macOS 需要 Xcode 命令行工具,Electron 需要 Node.js/npm,Tauri 需要 Rust 工具链。
macOS 冒烟脚本:从前置条件到运行流程
当前 smoke 目录中随仓库提交的文件为 macos.sh。其脚本头部注释声明了它镜像scripts/linux-smoke.sh的形状与分类方式(即不同 OS 的 runner 保持同一结构、同一判定口径)。
前置条件(Prerequisites)
smoke README 与脚本头部注释一致地列出了三项 macOS 前置条件:
tests/fixtures/build/macos.sh已运行,产出了libs/cua-driver/rust/test-apps/harness-appkit/(AppKit harness 应用);libs/cua-driver/rust/target/release/cua-driver已构建(debug 版本也可以,脚本会按 release → debug 顺序自动探测可执行文件);- TCC 辅助功能(Accessibility)权限已授予 cua-driver 二进制。
第三条附带一个重要的判定语义说明:如果 Accessibility 权限没给,AX 类工具会返回空树,但调用仍然干净退出——脚本会因此把它们记为PASS(因为退出码为 0 且输出无错误标记),并在输出中提示这一情况。这意味着读结果时要警惕"假阳性":全绿不代表无障碍权限一定就绪。
运行流程
脚本的主流程可以概括为六个阶段(见 macos.sh 全文):
- 定位二进制并校验。从仓库根推导
target/release/cua-driver,不存在则回退 debug;再校验 harness 可执行文件CuaTestHarness.AppKit.app/Contents/MacOS/CuaTestHarness.AppKit是否就绪,缺失时直接打印对应的构建命令并退出。 - 拉起受害应用。后台启动 AppKit harness,
sleep 1.5等待窗口就绪,并通过list_windows工具以 harness 的 PID 反查真实窗口号(用python3解析返回 JSON 中title含 "AppKit" 的窗口),随后执行start_session建立macos-smoke会话。 - group 1:无参/配置类工具。遍历
check_permissions、get_screen_size、get_cursor_position、get_config、get_recording_state、list_apps、list_windows及带 session 参数的get_agent_cursor_state——这类工具信息性强、总是安全。 - group 2:setter 工具。如
set_config {"max_image_dimension":1024}、set_agent_cursor_enabled、set_agent_cursor_theme(theme_id: cua.default)、set_agent_cursor_motion、stop_recording。 - group 3:应用生命周期。
launch_app启动 TextEdit,pgrep -n TextEdit拿到 PID 后kill_app回收;若拿不到 PID 则直接把kill_app记为 FAIL。 - group 4:per-window 状态。对真实解析出的
window_id调用get_window_state(capture_mode: tree)、get_accessibility_tree、zoom(200x200 区域);窗口号解析失败时这三个工具统一记 SKIP。 - group 5:输入合成。
move_cursor之后对 harness 窗口做click/double_click/right_click/drag(delivery_mode: foreground)/scroll/type_text/press_key/hotkey(["cmd","a"])/bring_to_front——注释说明这些坐标点击即便 AX 目标不精确也会走通 CGEvent 路径;set_value因缺少element_index(需要快照才能取得)记 SKIP,注明改由harness_appkit_text_input集成测试覆盖。 - group 6:别名与特殊项。
type_text_chars是有意不注册进工具注册表的废弃别名,由 mcp-server 的 invoke 层解析为type_text,故记 SKIP;page(需要带--remote-debugging-port的 Chromium)与replay_trajectory(需要已录制轨迹文件)同样记 SKIP。
判定逻辑:PASS / FAIL / SKIP 的精确口径
分类核心在run_tool函数(macos.sh L39-L65),口径值得逐条记住:
- SKIP:输出中匹配
unsupported_on_platform、is Windows-only、is Linux-only、is macOS-only——即"该工具在本平台是有意为之的跨平台 stub",说明调用正确但在此 OS 上无意义,不算 FAIL; - PASS:退出码 0且输出既不以
❌开头、也不以Error:开头; - FAIL:退出码非 0(记录
exit=<code>与首行前 100 字符),或退出码 0 但输出带错误标记(记录为exit0+❌); - 无参数工具的 JSON 参数默认为
{},且注释专门解释了为什么不写 bash 惯用法${2:-{}}:bash 会把它解析为${2:-{}'加字面},结果得到{而非{},破坏下游 JSON 解析——这是作者在 Linux 版脚本中踩过的同一个坑。
汇总阶段(macos.sh L198-L218)用 awk 保留每个工具最后一次的判定(后写的record()覆盖先写的),按工具名排序输出对齐表格,再统计Tools probed / PASS / FAIL / SKIP,最后以[[ "$fail" -eq 0 ]]作为脚本退出条件——即任何 FAIL 都会让 runner 以非零码退出,可直接挂进 CI。
两个工程细节值得一提:脚本顶部只set -u而不开set -e,因为冒烟测试的预期就是"允许个别工具失败但继续扫完";结果行以TOOL|VERDICT|REASON写入mktemp临时文件,且注释明确说明这是因为 macOS 自带 bash 3.2 没有关联数组,只能以换行分隔的行记录、最后排序去重。
基线 diff:用结果文件捕获覆盖度回归
smoke README 定义了第二个核心机制:每个脚本会为当前驱动版本在results/子目录下检入一份基线结果文件(如results/macos.txt),用于对比捕获工具覆盖度的回归:
./macos.sh > /tmp/run.txt diff results/macos.txt /tmp/run.txt需要说明的是,就当前仓库快照而言,smoke/目录下实际提交的只有 README.md 与 macos.sh,基线文件按上述约定在首次运行后生成并提交即可生效。这套 diff 工作流的意义在于:冒烟测试不追求断言每个工具的返回内容,只追求工具面的行为稳定性——某个工具从 PASS 变 FAIL、或某个原本 SKIP 的平台 stub 突然报错,都会在 diff 中立刻显形。
与 Rust 集成测试的衔接:fixture 消费链
smoke runner 是同一 fixture 体系里最"宽"的一层,其上下游都在 Rust 集成测试说明中有明确定位:
- 测试按前缀命名分层:
protocol_*_test.rs、session_capture_scope_test.rs、schema_*_test.rs默认运行(headless),harness_<toolkit>_test.rs与desktop_scope_<os>_test.rs标记#[ignore],需要暂存的 harness 应用与真实交互式桌面; - 跨平台规范矩阵是
cross_platform_behavior_test.rs,对 Electron / Tauri(以及 macOS 上的 WKWebView)运行相同的外部状态场景,每行在cases.jsonl中声明动作、AX/PX 定位方式、前台/后台投递、作用域与外部 oracle,观察结果写入results.jsonl,Rust reporter 校验两份文件并渲染summary.md; - CI / VM runner 通过环境变量
CUA_TEST_DRIVER_BIN、CUA_TEST_APPS_ROOT、CUA_TEST_WORKSPACE_ROOT指向工作区外的构建产物,并设置CUA_TEST_REQUIRE_FIXTURES=1把"fixture 缺失"从静默 SKIP 变为硬失败; - 规范化的 OS 级 E2E 入口另有其位:macOS 为
libs/cua-driver/tests/runners/macos-lume/run-all.sh(内部委托 scripts/ci/macos/run-rust-e2e.sh,后者会先调用tests/fixtures/build/macos.sh构建 fixture),Linux 为 scripts/ci/linux/run-rust-e2e.sh,Windows 为scripts/ci/windows/run-rust-e2e.ps1 -RequireGui。
因此三者形成清晰的金字塔:Rust 集成测试做行为级精确断言(MCP stdio 路径),CLI smoke runner做工具面级广覆盖(CLI 解析路径),基线 diff把后者的输出钉成可回归对比的资产。fixture 维护规则(见 fixture README 的 Maintenance Rules)也呼应了这一设计:新共享 ID 必须先加入shared/scenarios.json才能在测试中断言;行为证据统一通过 Rust testkit 与规范 OS runner 记录,"不要添加第二套 Python 或 shell 断言层"——smoke runner 正是被允许的例外之一,因为它验证的是 CLI 入口这条独立代码路径而非断言 UI 状态。
小结
smoke README 用不到三十行定义了一套完整的跨 OS 验证策略,其要点可以浓缩为四条:
- 路径互补:CLI 冒烟与 MCP 集成测试分别覆盖"工具寻址与退出行为"和"UIA/AX 状态断言",互不替代;
- 三态口径:PASS = 退出码 0 且无错误标记;FAIL = 非零退出或带错误标记的输出;SKIP = 有意的跨平台 stub 或缺 fixture,且在汇总表中给出理由;
- 快速全扫:单个 runner 约 30 秒遍历全部注册工具,FAIL 数非零即非零退出,可直接入 CI;
- 基线 diff:
results/下的版本化基线文件让工具面行为漂移变成一行diff可查的回归信号。
如需继续深入,可从 macos.sh 的run_tool判定实现、cli.rs 的call子命令解析,以及 Rust 集成测试目录说明 中的测试分层与环境变量约定三个文件入手。
【免费下载链接】cuaScale computer-use 2.0 with open-source drivers, cross-OS fleets, and benchmarks for training, evaluation, and data generation.项目地址: https://gitcode.com/GitHub_Trending/cua/cua
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考