Omarchy 图形化验收测试指南:在 disposable VM 中驱动真实桌面的自动化验证
本文面向需要在 Omarchy 发行版仓库中编写、运行或维护图形化验收测试套件的开发者与 Agent。它讲解该套件与普通 CLI / shell 单元测试的分层定位、目录结构、运行载体(disposable VM)、共享断言库、截图取证约定与键盘输入策略。读完本文,你将掌握如何用一条命令在隔离虚拟机中启动完整桌面会话验证、如何在 test/acceptance.d/ 下新增独立验收场景,以及何时必须重建全新 ISO 而非复用已安装基座。
验收测试在 Omarchy 测试体系中的定位
Omarchy 仓库把测试体系划分为三层,职责边界清晰,详见 docs/testing.md:
./test/cli—— 一个大型脚本,负责 CLI 路由(帮助渲染、别名、隐藏命令、--help永不执行目标)、bin/下所有omarchy-*可执行文件的元数据 lint,以及主题渲染与同步管线;./test/shell—— 运行test/shell.d/*-test.sh,每个文件是一个独立套件,覆盖某个 shell 插件、bin/命令、配置不变量或仍在生效的迁移脚本;- Acceptance(验收) —— 需要真实桌面"真实干活"才能验证的一切。它刻意排除在
./test/all之外,必须在 VM 而非开发会话中运行。
验收套件之所以需要独立载体,是因为它做的是普通测试无法做的事:启动和关闭真实应用、临时改变桌面配置、验证 Wayland 图层是否真正可见。它对应 agents/skills/acceptance-tests.md(本文档),这是编写或运行 test/acceptance.d/ 下图形验收套件前的必读说明。
套件组成与目录约定
验收套件由两部分构成:
- 运行器:
test/acceptance,一个 in-guest 脚本。它在 live Omarchy 会话内运行(通常是由omarchy-iso-test安装的 VM,通过 SSH 访问),负责等待会话就绪、发现环境、逐个调度测试文件并汇总结果; - 测试文件:
test/acceptance.d/*-test.sh。每个文件是一组聚焦的验收断言,例如会话健康(session-test.sh)、shell 表面(shell-surfaces-test.sh)、控制面板(panels-test.sh)、菜单与栏位行为(menu-test.sh)、代表性应用启动(apps-test.sh)、打印机(cups-test.sh)、安全配置(security-test.sh)与系统设置(system-test.sh)等。
所有测试文件共享同一断言库 test/acceptance.d/base-test.sh,其中的共享帮助函数如下表:
| 函数 | 作用 |
|---|---|
pass "描述" |
打印 ok - 描述(TAP 风格通过) |
fail "描述" [细节] |
打印可选细节与 not ok - 描述,先截取 failure-<step>.png 再 exit 1 |
screenshot "名称" |
用 grim(10 秒超时)截屏到 $ARTIFACTS/<名称>.png |
screen_contains "文本" |
2x 缩放截屏后经 tesseract(--psm 11)OCR,grep -Fi 判断屏幕是否含目标文本 |
wait_until "描述" 秒 命令… |
每秒轮询命令直至成功,超时则 fail 并附带超时详情 |
window_present / window_absent |
通过 hyprctl -j clients + jq 按 class 正则判断窗口存在性 |
layer_present / layer_absent |
通过 hyprctl -j layers 判断某 namespace 图层是否被映射 |
layer_on_screen / layer_off_screen |
更进一步,用监视器缩放后的逻辑坐标判断图层是否真的落在可见区域内(而非被停泊到屏外) |
close_windows "class" |
按地址关闭所有匹配窗口,先尝试 quattro Lua 调度器再退回经典 closewindow |
launch_app "命令" |
setsid -f 脱离会话后台启动应用 |
fail 是"文件级"语义:一个文件里第一个失败断言即终止该文件,避免后续断言针对已被破坏的状态继续报告。base-test.sh 也拒绝被直接执行(仅能被 source),并把产物目录 ARTIFACTS 默认设为 /tmp/omarchy-acceptance,可用环境变量 OMARCHY_ACCEPTANCE_DIR 覆盖。
为什么必须跑在 disposable VM 中
套件会打开/关闭应用、临时修改桌面配置(例如把顶栏挪到左侧),因此绝不能在当前活跃开发会话中运行——那会污染开发者正在使用的桌面。文档明确要求:通过同级仓库 omarchy-iso 在 disposable(一次性)VM 中运行。
两个关键运行形态:
形态一:仅改验收测试本身 —— 复用已安装基座并只同步套件源码:
cd ../omarchy-iso
./bin/omarchy-iso-test release/<iso>.iso --reuse-base --sync-omarchy ../omarchy --no-preview
--reuse-base:复用先前安装的基座,避免每次重建系统,加速纯测试改动;--sync-omarchy ../omarchy:只把当前仓库的验收测试同步进 VM;--no-preview:跑完后不自动弹出截图预览。
当验收运行还必须覆盖本地 bin/、config/ 或 shell/ 源码时,把 --sync-omarchy 换成 --sync-all ../omarchy。这里有个值得注意的取舍:test/acceptance 运行器把 OMARCHY_PATH 默认指向安装树 /usr/share/omarchy,而不是当前 checkout——因为 qs(Quickshell)按 config 路径匹配 shell 实例,若套件指向其他树,会把正在运行的会话 shell 误判为"未运行"。因此测试其他树源码时必须显式传入 OMARCHY_PATH。
形态二:涉及包清单、安装流程、最终化或出厂默认配置的改动 —— 必须从本地 checkout 构建全新 ISO,且不能使用 --reuse-base:
cd ../omarchy-iso
./bin/omarchy-iso-make --no-boot-offer --local-source ../omarchy ../omarchy-pkgs
./bin/omarchy-iso-test release/<generated-iso>.iso --no-preview
这两条命令分别完成"构建携带本地来源的全新 ISO"与"在该 ISO 上做无预览的完整验收"。
测试文件编写约定
仓库对每个验收测试文件约定了明确的行为规范,写进 agents/skills/acceptance-tests.md 并在现有测试中落实:
1. 把无关的验收工作流拆到独立文件。 单一关注点便于失败定位。运行器会在一个文件失败后继续运行其余文件(而不是整体中止),从而尽可能保留诊断覆盖——这一点与 ./test/shell 的"跨文件继续、文件内首败即止"策略一致(见 docs/testing.md)。
2. 用 trap 恢复被改动的用户状态。 例如 menu-test.sh 会把用户的 ~/.config/omarchy/shell.json 备份到临时文件、在测试中通过菜单把栏位改成垂直(left),再通过 trap ... EXIT 在退出时恢复原文件并 reloadConfig;panels-test.sh 为天气面板备份/恢复 weather.json,并先把所有可能开着的面板 hide 掉。凡是测试打开过的东西,测试结束前都要关闭。
3. 每个视觉上不同的状态都要留证。 通过截图与关键节点绑定:成功路径按 success-<step>.png 命名(例如 success-bar-hidden、success-emoji-picker-search、success-menu-06-bar-left),失败辅助函数则捕获 failure-<step>.png。凡是涉及输入的场景,也应保留"输入后的状态"截图,而不只是打开瞬间。
4. 屏幕文本断言要走 OCR。 session-test.sh 用 screen_contains 检查面板标签;shell-surfaces-test.sh 用它确认剪贴板搜索结果、提醒消息弹窗、通知内容等。OCR 在 2x 缩放下进行,原因是 tesseract 在原生分辨率下经常漏掉小号文字(例如天气面板的明细标签)。这是一种"所见即所断"的兜底手段,与 hyprctl 的几何/存在性断言互补。
5. 测试运行器本身对"不打扰"有要求。 预判到某些断言可能"误伤"开发者机器,验收测试通常先断言环境干净:例如 apps-test.sh 会先检查目标窗口不存在才启动(避免歧义,也避免在开发机上强制关窗),session-test.sh 允许用 OMARCHY_ACCEPTANCE_IGNORE_UNITS 提供一个正则来豁免个别失败 unit(开发机上偶有噪音,全新 VM 应保持干净)。
运行器机制:环境发现、等待会话与超时控制
test/acceptance 作为套件入口,解决了"通过 SSH 进入图形会话"这一特殊约束:
- 环境自举:通过
systemctl --user show-environment提取DISPLAY、LANG,通过扫描$XDG_RUNTIME_DIR/hypr下最新签名找到并导出HYPRLAND_INSTANCE_SIGNATURE,再据此探测 Wayland socket——SSH 会话不会继承图形会话环境; - 等待会话:
wait_for_session轮询hyprctl -j monitors,直到 Hyprland 可应答,默认最长 300 秒(OMARCHY_ACCEPTANCE_BOOT_TIMEOUT),覆盖首次启动仍在收敛的场景; - 逐个调度:收集
test/acceptance.d/*-test.sh(跳过base-test.sh),按序用timeout执行,单个文件默认 420 秒(OMARCHY_ACCEPTANCE_TEST_TIMEOUT);文件失败不中断其余文件,最后汇总ok/not ok并以退出码反映整体成败; - 产物输出:截图与日志归入由
OMARCHY_ACCEPTANCE_DIR指定的目录(运行器与base-test.sh均以它为准),ISO harness 会把它们统一收集进其带时间戳的test-runs/目录,并在运行结束后自动打开截图——除非传了--no-preview。
键盘输入的两种层次:QMP 与 in-guest wtype
验收测试需要验证快捷键与输入行为,但键盘输入存在可靠性的层次差异,这是文档特别强调的一个易错点:
- 合成器级快捷键(global keybinding) 必须由 ISO harness 用 QMP 虚拟键盘输入在宿主机层面注入硬件按键组合来验证——因为只有这样的输入才会真正经过 Hyprland 的全局绑定解析;
- in-guest 的
wtype适合把文本敲进当前获得焦点的控件(例如在表情选择器里搜索rocket、在剪贴板面板里输入检索词),但它不能可靠地证明一个全局 Hyprland 键绑定生效。
shell-surfaces-test.sh 里注释明确说明:快捷键的宿主机证明由 harness 用 QMP 按键组合单独完成,而 guest 内文件专注 UI 行为本身(打开、输入、选择、关闭)。因此写测试时,要按验证目标选择正确的输入通道,别用 wtype 去断言全局快捷键。
与现有验收场景的对照参考
为便于理解"一个真实验收文件长什么样",仓库中已有的代表性场景可直接对照阅读:
- session-test.sh:合成器报出至少一块监视器、
omarchy-shellping 可应答、13 个核心插件(audio/background/bar/bluetooth/clipboard/emojis/menu/monitor/network/notifications/power/reminders/weather)均已加载、栏与背景图层真正在屏、栏隐藏时"停泊屏外但图层仍映射"、PipeWire 存活、根文件系统为 btrfs、omarchy-version可用、系统与用户级均无失败 unit; - shell-surfaces-test.sh:表情选择器完整搜索—选中流程、剪贴板历史写入—搜索—复制回出、系统菜单分支、壁纸/主题选择器预览后取消、提醒流程逐屏走完但按 Escape 放弃、真实通知渲染与
dismissAll清理、应用菜单的"打开—搜索—启动命中文—关窗"完整回路; - panels-test.sh:用固定坐标驱动真实 Open-Meteo 天气接口而非 IP 地理定位、蓝牙/网络/音频/监视器面板逐一开合、无电池硬件时电源面板走受支持的"不出现"路径、Tab 在面板间导航、淡出期间重开面板的焦点再夺取(focus prime);
- menu-test.sh:从根菜单经样式子菜单到达菜单栏位置设置,把栏位切到
left后验证几何从水平变为垂直,再恢复用户原配置; - apps-test.sh:
foot、Chromium、Neovim(经xdg-terminal-exec --app-id=org.omarchy.nvim)、Omawrite 四类代表性应用"启动—出窗—关窗"的最小覆盖,同时说明完整核心包清单由系统验收文件另行覆盖。
这些文件与 docs/testing.md、docs/file-layout.md 一起,构成了在 Omarchy 上落地"真实桌面真实验证"的完整可参考范本:先读 agents/skills/acceptance-tests.md 理解运行边界,再对照上述文件掌握断言写法,最后经 omarchy-iso 在 disposable VM 中执行即可。
关键环境变量速查
| 变量 | 默认值 | 作用 |
|---|---|---|
OMARCHY_ACCEPTANCE_DIR |
/tmp/omarchy-acceptance |
截图与测试产物目录 |
OMARCHY_ACCEPTANCE_BOOT_TIMEOUT |
300(秒) |
等待 Hyprland 会话可应答的最长时间 |
OMARCHY_ACCEPTANCE_TEST_TIMEOUT |
420(秒) |
单个测试文件的执行超时 |
OMARCHY_ACCEPTANCE_IGNORE_UNITS |
空 | 豁免"失败 unit"检查的正则(开发机适用,新 VM 应干净) |
OMARCHY_PATH |
/usr/share/omarchy(运行器内) |
指定被验证的 Omarchy 安装树;qs 按 config 路径匹配会话 shell,改指其他树须显式传入 |
以上变量均在 test/acceptance 与各测试文件的源码中可见,前两者亦在 agents/skills/acceptance-tests.md 的描述范围内。
简言之:验收测试是 Omarchy"质量最后一道闸门",它把真实 ISO、真实安装、真实 Wayland 会话串成一条可重复、可留证、可定位的验证流水线——而写好它的关键,正是守住"VM 隔离、状态还原、视觉取证、输入通道分层"这几条纪律。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00