17 · 测试与开发闭环¶
Omarchy 的测试不是一种技术覆盖所有层。CLI router、纯数据模型、系统脚本和真实 Wayland 桌面分别使用不同的成本与隔离级别。
三层测试金字塔¶
flowchart TB
Acceptance[VM 图形验收<br/>6 条 workflow]
Shell[Shell/脚本契约<br/>190 个 runnable 文件]
CLI[CLI router + theme pipeline<br/>1 个大 suite]
CLI --> Shell --> Acceptance
test/all 只运行 CLI 与 shell suite,并且即使前一 suite 失败也继续,最后汇总。acceptance 明确排除在日常 all 之外,因为它需要可丢弃 VM 和真实桌面。
CLI suite 负责什么¶
test/cli 集中约束:
- help/group 渲染;
- route、alias、hidden command;
- trailing
--help不执行目标; - 每个
omarchy-*executable 都有摘要; - removed/redundant metadata 不再出现;
- theme template/color/OSC 与同步命令;
- theme state migrations。
Router 是一个全局注册表,集中测试适合捕获跨命令 collision;具体命令行为则更多落到独立 shell test 文件。
shell runner 的失败粒度¶
test/shell 自动发现 test/shell.d/*-test.sh,跳过作为库的 base-test.sh。每个文件第一条失败 assertion 就退出,但 runner 继续剩余文件,最后汇总。
为什么不在一个文件内继续?后续 assertion 往往依赖之前建立的状态,前置失败后继续会产生噪声。为什么 runner 又继续其他文件?独立领域不应被一个 packaging failure 全部隐藏。
flowchart LR
Runner --> A[Test file A]
Runner --> B[Test file B]
Runner --> C[Test file C]
A -->|首个 assertion 失败| StopA[终止 A]
StopA --> Runner
B --> Runner
C --> Runner
Runner --> Summary[统一失败摘要]
固定快照有 191 个 *-test.sh(含 base),即约 190 个 runnable shell test 文件。
fake HOME,real source¶
系统脚本测试常用:
mktemp -d建 fakeHOME;OMARCHY_PATH="$ROOT"指向真实 checkout;- 在临时
bin/放sudo、pacman、gsettings等 stub; - stub 把参数写 call log;
- 执行真实
bin/omarchy-*,检查 log 与生成文件。
这种方法既不修改开发机,又比 mock 被测函数更接近真实 Bash 边界。测试的世界是假的,被测脚本本身是真的。
QML 逻辑为什么能用 Node 测¶
MenuModel、BarModel 等把解析/合并/布局写成 plain JS,并在 Node 环境检测到 module 时导出 CommonJS。QML 正常 import 时忽略这段。
run_node_test 给 heredoc 加 assertion prelude,再让 Node 加载同一文件:
const menu = requireFromRoot("shell/plugins/menu/MenuModel.js")
const parsed = menu.parseMenuJsonc('{ "root": { "label": "Go" }, }')
assertEqual(parsed.length, 1, "trailing comma parses")
把纯逻辑从 QML component 生命周期分离,既提升可测性,也迫使数据变换具有明确输入输出。
compositor gate¶
只检查 WAYLAND_DISPLAY 环境变量不够:sandbox 可能继承变量却无法访问 socket,已死亡 compositor 也可能留下 socket。require_compositor 会:
- 检查 runtime socket;
- 在有 Hyprland signature 时重试
hyprctl -j monitors; - 不可达则以成功 skip 结束该测试;
- 可达时禁 core dump,避免 QGuiApplication 断连留下垃圾。
静态契约应放在 gate 之前,只有真正需要渲染/连接的部分才跳过,避免 headless CI 失去全部覆盖。
migration 测试¶
迁移测试构造 legacy fake HOME,直接以 bash -euo pipefail 运行具体迁移,并至少验证:
- legacy state 被正确转换;
- 第二次运行仍安全;
- 非 legacy 或已定制状态不被误改;
- marker 语义由 runner 测试另行覆盖。
这比只断言脚本包含某条命令强,因为真正验证了状态转换。
图形 acceptance¶
test/acceptance 通常通过 sibling omarchy-iso 创建的 disposable VM 运行。SSH 进入后,它从 user systemd/runtime 重新发现 Hyprland signature、Wayland display 与 D-Bus,而不依赖 SSH 环境继承。
六条 runnable workflow 覆盖:session、apps、menu、panels、shell surfaces、system。测试使用:
hyprctl查询窗口/monitor;wtype模拟会话内输入;grim截图与 OCR/像素证据;- host 通过 QMP 发送全局 key combo(guest 工具不能可靠模拟全部 compositor global binding);
- artifact 目录收集截图和日志。
每个 workflow 有 timeout,某一项失败不阻止其余验收。视觉验证不能被纯 JS 测试替代:布局、焦点、layer surface、字体与 GPU 都是运行时事实。
什么测试放在哪里¶
| 变化 | 首选测试 |
|---|---|
| 新 CLI route/metadata | test/cli |
| Bash 命令副作用 | 独立 shell test + stub world |
| JS parse/merge/layout | Node-driven shell test |
| QML manifest/entrypoint 契约 | 静态 shell test,必要时 compositor gate |
| migration | fake HOME 状态转换 + 二次运行 |
| 快捷键打开真实 UI | VM acceptance |
| GPU/固件/物理音频 | 物理硬件验证 + 可做的结构测试 |
关键源码索引¶
test/all— suite 汇总且继续运行test/cli— router/theme suitetest/shell— 文件发现与失败汇总test/shell.d/base-test.sh— assertions、stub 与 Node bridgetest/acceptance— 图形会话发现与 workflow runnertest/acceptance.d/base-test.sh— screenshot/窗口/输入 helperdocs/testing.md— 上游测试约定
最后两章把分散机制重新串成路径与经验。先看:18 · 端到端调用链。