KDE Linux 中的 openQA 测试
摘要
KDE Linux 已集成 openQA 用于对其系统镜像进行端到端自动化测试,用基于虚拟机的安装和升级验证(通过 AT-SPI 使用 Selenium)取代了基础测试。
<p><a href="https://lobste.rs/s/ucn4uw/openqa_testing_kde_linux">评论</a></p>
查看缓存全文
缓存时间: 2026/07/14 14:18
# KDE Linux 中的 openQA 测试
来源:https://blogs.kde.org/2026/07/13/openqa-testing-in-kde-linux/
基于 openQA 的测试系统最近已集成到 KDE Linux 中(太棒了!),我觉得是时候写一篇小文章介绍一下了。
KDE Linux 的特性——整个系统作为一个单一签名镜像而不是一堆软件包发布——(理论上)对可靠性非常有利。然而,这引出了一个令人不安的问题:我们如何确保这个镜像在发布给用户之前*真正能正常工作*?OpenQA 就是答案!
长话短说:我们在虚拟机中启动每次构建,运行与系统交互的测试,确保系统能正常安装、升级,并且桌面功能可用。一旦测试通过,用户就会得到一个经过端到端测试的镜像。这取代了相当简陋的基本测试机制——后者仅仅是启动 Live 镜像并检查启动是否被认可、是否有任何单元失败。
## 测试流程
单个构建会经历三个阶段。
`install-system` 获取 Live ISO,在虚拟机中启动它,然后执行真实的安装过程,将系统安装到一个空虚拟磁盘上——就像真实用户做的那样。接着 `sanity-test` 启动那块刚刚安装好系统的磁盘,验证系统能否正常启动并正常运行。与此同时,我们还会并行测试升级路径:`upgrade-system` 阶段会安装上一个版本,并将其升级到当前构建,以检查上一个版本能否成功升级到新构建。每个阶段都会把它的磁盘传递给下一个阶段。
它们通过依赖链连接起来,因此在 openQA 网页界面中,整个运行会显示为一个单一的连接图。如果安装失败,后续阶段就不会运行,因为没有什么可测试的了。
我们的 CI 流水线现在大致如下所示:
CI 流程图。镜像构建失败会上传到 CI 构建产物,而成功的镜像会在门控发布之前运行并行 openQA 测试和升级任务。
## 有趣的架构细节
与标准的 openSUSE 或 Fedora openQA 实例相比,我们有一些不同的做法。
### 使用 Selenium 测试而非 needle 测试
普通的 openQA 测试通过“needles”进行操作。这些不是缝纫针,而是虚拟机的截图,附带一些 JSON 元数据,表示某种期望状态。元数据定义了某些要匹配或忽略的区域,测试代码可以点击匹配到的区域。虽然 needle 在像用户那样与系统交互方面确实有效,但也有缺点。制作并不断更新 needle,以及防止它们在用户界面稍有变化时失效,非常烦人。
幸运的是,我们已经有一种经过实战检验的方式,用于通过测试与用户界面交互:`selenium-webdriver-at-spi`(https://invent.kde.org/sdk/selenium-webdriver-at-spi)。它已经在 KDE 项目的单元测试中广泛使用,因此我们决定使用它,这带来了更大的灵活性、可维护性和一致性。这也使得应用程序开发者未来可以在 KDE Linux 上利用 openQA 运行他们自己的测试。
本质上,我们在被测试的系统上有一个 Python `unittest` 脚本(具体见下面的 sysext 部分),它连接到应用程序。然后利用 AT-SPI2 辅助功能 API 发送点击事件并读取屏幕内容,类似于屏幕阅读软件(如 Orca)的工作方式。
### CI 任务中的临时 worker
OpenQA 实例通常有长期运行的 worker,托管在服务器上。要让所有基础设施启动并运行起来相当麻烦。此外,托管的 worker 还需要与服务器之间进行上传-下载的繁琐流程,涉及大型资源(如 `.iso` 文件和生成的硬盘)。这毫无理由地使事情变得*非常慢*。
我们已经有了 CI 运行器,它们完全适合这项工作,并且可以在需要时启动,从而轻松实现简单的扩展。因此,我们在 CI 运行器中启动一个 openQA worker 容器,该容器向 openQA 服务器提交任务。每个 worker 有自己的 UUID,该 UUID 与任务共享,因此 CI 中运行的 worker 总能分配到正确的任务。
这节省了我们处理带宽的繁琐工作,因为所有资源都在同一个容器内生成和使用,我们只需将所有资源保留在 worker 上,永远不需要上传到服务器。因此,我们节省了 openQA 服务器上的大量存储空间,可以用相当小的托管需求来运行它。
### 使用 systemd 系统扩展注入测试
你可能会问,我们如何将 Selenium 测试放到系统上?这就要用到朴素的 **system extension**(https://www.freedesktop.org/software/systemd/man/latest/systemd-sysext.html),简称 sysext。
我们在 sysext 中包含以下几项内容:
- Python `unittests` 测试本身。
- 一个包含一些系统配置的引导脚本,以便我们设置合适的测试环境。
- 一个 `venv` 虚拟环境,以便利用 Python 生态系统。该环境在引导脚本中创建。
所有这些被打包成一个 EROFS `.img` 文件,我们将其挂载到 worker 的虚拟机上。然后,上游 KDE Linux 中的一个关联 `udev` 规则会自动挂载它,随后一个关联的服务会触发引导脚本。
由于我们能够向系统中注入测试和配置,因此可以测试那些用 needle 无法实现的事情。例如,我们测试关键桌面进程是否曾崩溃、是否有 systemd 服务失败、网络是否正常工作、以及 KDE Linux 附带的命令是否正常工作。所有这些测试都直接利用了系统的内部机制。
### 通过 SSH 与系统交互
为了实际操控系统并让 worker 按顺序执行这些测试,我们需要某种与系统交互的方式。openQA 提供了一些与串行终端交互的设施,但事实证明这非常脆弱且不可靠,存在各种缓冲问题。
作为替代,我们使用 sysext 设置 SSH,并利用 Python 库 **Fabric**(https://www.fabfile.org/)提供的功能,以健壮的方式运行所有测试。
每个测试都在由 `systemd-run` 创建的瞬态 systemd 服务中运行。这会将测试以预期用户身份运行,将其进程分组到一个 cgroup 中,为其提供独立的日志流用于输出,并同步返回服务退出状态。测试框架随后可以在测试失败时收集该单元的日志,从而保持一切整洁有序。
### 在发布前暂存镜像,以及如何测试更新
为了防止用户下载尚未测试通过的镜像,我们在 storage.kde.org 上创建一个暂存目录,作用域限定为镜像构建阶段的作业 ID,以目录树形式存储构建出的产物。其布局与面向公众的目录树镜像,因此一旦测试通过,我们可以简单地将它合并进去。
然而,这在我们尝试测试系统升级时带来了麻烦——因为我们显然无法升级到一个尚未发布的镜像!
解决方法很简单。在 sysext 中,我们只需将 `systemd-sysupdate` 指向我们已经创建的暂存目录。不过,这也有一些缺点。目前,我们无法通过 `kde-linux-sysupdated` 测试增量更新。在不久的将来修复这个问题应该不难,但我们正在等待 KDE Linux 完全托管在 storage.kde.org 上,然后再着手处理。更大的问题是,我们确实没有一个很好的方法来测试来自块存储的更新。这个问题还需要进一步研究,但就目前而言,升级测试已经足够好了。
## 未来计划
我们有几个目标正在努力实现:
- 如上所述,测试增量/分块升级。
- 利用 openQA 让应用程序开发者在 KDE Linux 上测试他们自己的应用程序。
- 将我们所有的 openQA 胶水代码通用化,以便其他项目可以使用我们构建的架构。
- 进一步地,将上述胶水代码从(诚然脆弱的)bash 脚本移植到 Python 或其他更合适的语言。
- 测试使用手动分区和全盘加密的安装。
……可能还有很多我们尚未想到的事情。激动人心的时刻!
相似文章
KDE Linux 本月更新:2026 年 6 月
KDE Linux 2026 年 6 月更新包括 QA 测试进展、改进的 ISO 安装、更好的音频 CD 抓取、日志收集工具、开发者模式以及 CJKV 输入文档。
构建了一个自动化Android QA测试的AI智能体
已构建了一个AI智能体,用于自动化Android设备上的QA测试,从而简化测试流程。
QA Crow
QA Crow 是一个缺陷跟踪和质量保证工具,旨在帮助开发团队管理缺陷积压。
KDE迎来30周年
KDE迎来30周年,庆祝社区驱动的自由软件开发,并邀请用户参加活动、捐款以及参与环保倡议。
KDE Plasma 6.7 发布
KDE Plasma 6.7 已发布,引入了按屏幕虚拟桌面、麦克风音量测试、长按特殊字符、浅色/深色模式切换、越南农历、系统托盘中的后台应用、打印改进以及各种可用性增强。