openQA для разработчиков и сопровождающих
KDE Linux использует openQA для автоматической проверки каждого системного образа перед публикацией. openQA загружает образ в виртуальной машине и убеждается, что его можно установить, обновить и без сбоев использовать.
В openQA принята своя терминология: задание (job) — один запуск набора тестов для образа, а рабочий узел (worker) — машина или контейнер, который это задание выполняет. Эти и другие понятия — тестовые модули, группы заданий, ресурсы, веб-интерфейс — разъясняются в документации openQA.
Принцип работы тестов
Тестовый рабочий узел работает рядом с конвейером CI и поэтому может использовать образы и виртуальные диски, полученные при сборке, не выгружая эти большие файлы куда-либо ещё. Тесты передаются в виртуальную машину через системное расширение и взаимодействуют с ней по SSH.
Тесты рабочего стола обращаются к программному интерфейсу специальных возможностей через selenium-webdriver-at-spi, что позволяет им работать с приложениями и проверять их состояние, не полагаясь на одно лишь сравнение снимков экрана. Набор тестов проверяет также сбои системных служб, аварийные завершения процессов рабочего стола, неполадки сети и ухудшения в работе команд KDE Linux.
Конвейер CI и работа сопровождающего
В защищённой ветке по умолчанию основного репозитория KDE Linux конвейер работает так:
- Задание imaging собирает, подписывает и размещает образ и его канал sysupdate на
storage.kde.org. - Задание trigger-openqa запускает конвейер в os-autoinst-distri-kdelinux, передавая ему адреса подготовленного образа и канала sysupdate.
- Нисходящий конвейер выполняет сценарии
installиupgrade, объявленные вtests/tests.toml, — каждый для обычной и для зашифрованной установки. - Задание publish в основном репозитории выполняется только после успешного прохождения конвейера openQA. Оно делает уже подготовленный образ общедоступным.
Тем самым сбой openQA не даёт опубликовать образ. Начните с нисходящего конвейера, открыв его из задания trigger-openqa, а затем откройте из журнала CI задание openQA, завершившееся сбоем. На странице задания тестовые модули показаны по порядку, и по ней видно, где произошёл сбой: при установке, в проверках работоспособности установленной системы или при обновлении.
Загрузка подготовленного образа
Ссылка после сообщения журнала «In case of failure, inspect and download the image and sysupdate tree at…» открывает обзор хранилища для тестируемой сборки. Загрузите оттуда образ .iso, чтобы запустить его вручную или передать локальному стеку openQA. Дерево sysupdate содержит подготовленные файлы обновления, которые использует тест обновления.
Разбор задания, завершившегося сбоем
Откройте задание в веб-интерфейсе openQA и сначала выберите модуль, завершившийся сбоем. Ссылка на веб-интерфейс появляется после сообщения журнала «<name> test job is now running.». В сведениях о задании приводятся результат модуля, снимки экрана или видеозапись (если есть) и диагностический вывод. На вкладке Logs & Assets просмотрите или загрузите autoinst-log.txt — журнал рабочего узла openQA и подсистемы тестирования. Это основной журнал самого задания.
Команды, которые тест выполняет в испытуемой системе, KDE Linux запускает во временных службах systemd, а журнал службы записывает в результат теста как диагностический вывод. Вывод команд и журнал смотрите в autoinst-log.txt. Журналы, собранные средством collect-logs, можно загрузить отдельно — файлом *kde-linux-collected-logs.tar.zst.
Локальный запуск тестов
Собранный локально образ можно проверить в openQA до отправки изменения. Это обязательно при работе из форка за пределами kde-linux/kde-linux: его конвейер CI собирает тестовый образ, но конвейер openQA не запускает. Это полезно и при разработке тестов для собственных изменений.
В репозитории тестов openQA для KDE Linux есть локальный стек, включающий и веб-интерфейс openQA, и рабочий узел. Ему нужны Python 3.14, Podman и podman-compose, которые входят в состав KDE Linux.
Клонируйте репозиторий тестов, затем поместите в его корневой каталог нужный для проверки образ KDE Linux .iso. Если образа нет, средство запуска тестов загрузит последний общедоступный образ.
Запустите локальный стек из корневого каталога репозитория:
./qa-mock up
Когда стек будет готов, откройте http://localhost:1080 — это веб-интерфейс openQA. Задания автоматически не отправляются, поэтому откройте оболочку в контейнере и отправьте сценарий install. Команды flow, job и build-sysext выполняются внутри этого контейнера.
./qa-mock enter ./qa flow install
Чтобы вместо него запустить сценарий upgrade:
./qa flow upgrade
Для зашифрованной установки передайте FDE_INSTALL=1 как параметр openQA. Суффикс варианта (flavor) отличает зашифрованные задания в веб-интерфейсе:
./qa flow install --var FDE_INSTALL=1 --flavor-suffix=-encrypted ./qa flow upgrade --var FDE_INSTALL=1 --flavor-suffix=-encrypted
Ключ --var NAME=VALUE можно указывать несколько раз, но каждая переменная должна быть объявлена в списке variables выбранного сценария в tests/tests.toml.
Набор sanity-test использует виртуальный диск, созданный набором install-system. Чтобы сосредоточиться на отдельных тестах, измените список tests набора в tests/tests.toml или объявите там другой набор и сценарий. Можно также запустить отдельный набор командой ./qa job --name <suite>, не забывая, от каких дисков он зависит. Команды flow, job и build-sysext принимают ключ --manifest-path <path>, чтобы использовать другой манифест вместо tests/tests.toml. Пути к тестам в манифесте указываются относительно его каталога. Обязательные параметры команды job выводит ./qa job --help.
Например, соберите системное расширение и запустите задание install-system с образом .iso из каталога репозитория. Идентификатор сборки и вариант замените на значения своего образа:
./qa build-sysext
./qa job \
--live ./kde-linux_<build-id>.iso \
--hdd kde-linux_<build-id>.qcow2 \
--sysext ./openqa-sysext.img \
--build <build-id> \
--variant <variant> \
--name install-system \
--flavor live
Закончив, выйдите из оболочки контейнера, а затем на хост-системе остановите стек и удалите его локальные тома.
./qa-mock down -v
./qa-mock передаёт аргументы в podman-compose без изменений. Например, ./qa-mock up -d запускает стек в фоновом режиме.
Проверка обновления между локальными сборками
Поместите базовый образ ISO в корневой каталог репозитория тестов. Запуская стек с хост-системы, задайте в переменной KDE_LINUX_OUTPUT каталог mkosi.output со сборкой, до которой нужно обновиться:
KDE_LINUX_OUTPUT=~/Repositories/kde-linux/mkosi.output ./qa-mock up
Войдите в контейнер и запустите сценарий обновления, заменив <build-id> идентификатором сборки базового образа ISO:
./qa-mock enter
./qa flow upgrade \
--upgrade-from /casedir/kde-linux_<build-id>.iso \
--upgrade-to /kde-linux-output
Оба ключа указываются только вместе. Репозиторий монтируется в /casedir, а выбранный каталог mkosi.output — в /kde-linux-output только для чтения. Самая новая сборка из этого каталога отдаётся виртуальной машине как временный источник sysupdate и должна быть новее базового образа ISO. Чтобы проверить зашифрованную установку, добавьте --var FDE_INSTALL=1 --flavor-suffix=-encrypted.
Объявление наборов и сценариев
В tests/tests.toml задаются дистрибутив (name и distri), наборы и сценарии. Набор — это упорядоченный список тестов, выполняемых в одном задании openQA. Его действие action определяет, как задание использует виртуальный диск:
installзагружает live-образ и создаёт диск установленной системы.upgradeзагружает установленную систему с диска и задаётDO_UPGRADEдля тестов, выполняющих обновление.testзагружает установленную систему с диска для проверки.
Сценарий выполняет наборы в объявленном порядке и передаёт диск установленной системы от задания к заданию. В KDE Linux объявлены такие сценарии:
[flows.install] suites = ["install-system", "sanity-test"] variables = ["FDE_INSTALL"] [flows.upgrade] suites = ["install-system", "upgrade-system", "sanity-test"] variables = ["FDE_INSTALL"]
Сценарий install устанавливает и проверяет тестируемую сборку. Сценарий upgrade сначала устанавливает более старый образ, обновляет его до тестируемой сборки, а затем выполняет проверки работоспособности. Для подготовленной сборки CI базой служит последний опубликованный образ. Без --upgrade-from и --upgrade-to локальный запуск обновления по умолчанию берёт целью последнюю общедоступную сборку, а базой — более старый локальный образ ISO, если он есть, иначе загружает предыдущий образ.
Перед каждым заданием рабочий узел создаёт временный каталог тестов (CASEDIR) с планом запуска main.pm для выбранного набора. Тесты выполняются в порядке манифеста, включая повторяющиеся записи. Чтобы изменить план запуска, измените манифест.
Написание теста
Описания тестов и их код лежат в os-autoinst-distri-kdelinux. Скрипты тестов добавляйте в tests/, рядом с родственными тестами, и регистрируйте в массиве tests подходящего набора в tests/tests.toml. Например:
[suites.example]
action = "test"
tests = [
{ path = "common/bootup.py", type = "openqa" },
{ path = "common/basic_test.py", type = "python" },
{ path = "kdelinux/app/firefox.py", type = "selenium", user = "installed", timeout = 180 },
]
Тесты openqa выполняются на рабочем узле и обращаются к API openQA напрямую. Тесты python и selenium выполняются в испытуемой системе: системное расширение само упаковывает их скрипты, а рабочий узел создаёт вызывающие их обёртки тестов openQA.
Тип теста выбирают по тому, что именно проверяется:
- Для средств командной строки, служб, состояния файловой системы и прочего поведения без графического интерфейса подойдёт обычный
unittestна Python. - Если же проверяемое поведение требует работы с графическим приложением, нужен Selenium через
selenium-webdriver-at-spi.
Обычные тесты на Python
Создайте unittest в tests/, например tests/kdelinux/system/example.py. Он должен проверять состояние испытуемой системы и записывать результаты в XML-файл JUnit — тот автоматически попадёт в ресурсы openQA и в отчёт GitLab CI.
import unittest
from lib.sut import openqa_junit_xml
class ExampleTests(unittest.TestCase):
def test_expected_behaviour(self):
self.assertTrue(True)
if __name__ == "__main__":
openqa_junit_xml.run(ExampleTests, "example")
Добавьте запись в массив tests нужного набора:
{ path = "kdelinux/system/example.py", type = "python" },
Автоматически созданная обёртка собирает результат JUnit и вывод команды, чтобы сбои были видны в задании openQA и в артефактах CI.
Графические тесты на Selenium
Графические тесты — это тоже классы unittest на Python, но они находят элементы интерфейса через драйвер Appium/Selenium и работают с ними по программному интерфейсу специальных возможностей. Средство запуска Selenium само включает эту подсистему и запускает драйвер.
Например, графический тест создаёт драйвер для своего приложения, работает с элементами через интерфейс специальных возможностей, а в конце закрывает драйвер:
import unittest
from appium import webdriver
from appium.options.common.base import AppiumOptions
from appium.webdriver.common.appiumby import AppiumBy
from lib.sut import openqa_junit_xml
class ExampleTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
options = AppiumOptions()
options.set_capability("app", "org.kde.example.desktop")
cls.driver = webdriver.Remote("http://127.0.0.1:4723", options=options)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
def test_expected_behaviour(self):
self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "example-control").click()
if __name__ == "__main__":
openqa_junit_xml.run(ExampleTests, "example")
Сохраните скрипт, например, как tests/kdelinux/app/example.py и добавьте его в нужный набор с type = "selenium". Для приложений рабочего стола выберите тестового пользователя установленной системы:
{ path = "kdelinux/app/example.py", type = "selenium", user = "installed", timeout = 180 },
Пока набор в разработке, запускайте содержащий его сценарий локально.
Параметры тестов и собственные обёртки
Для автоматически создаваемых обёрток тестов испытуемой системы в записях манифеста можно задать user (root, live, installed или plasma_setup), timeout в секундах (по умолчанию 90), artifacts — список путей для сбора файлов, а также флаги openQA fatal = true или always_run = true. Чтобы тест оставался объявленным, но не входил в план запуска, задайте enabled = false.
Если запуск теста требует особой обвязки, поместите обёртку рядом с его скриптом для испытуемой системы и укажите на неё через override:
{ path = "kdelinux/system/collect_logs.py", type = "selenium", override = "kdelinux/system/collect_logs.openqa.py" },
Такая собственная обёртка сама отвечает за свои флаги, пользователя, тайм-аут и сбор артефактов и должна передавать относительный путь к скрипту испытуемой системы в CliTest.run_python() или CliTest.run_selenium(). Пример — существующий collect_logs.openqa.py.
Подробнее о написании тестов Selenium — Appium automation testing и GUI Testing with selenium-webdriver-at-spi.
Дополнительные сведения
Подробный разбор сценария тестирования и его устройства — в статье openQA Testing in KDE Linux. Инфраструктура тестирования разрабатывается в репозитории os-autoinst-distri-kdelinux.
Статья предоставлена Thomas Duckworth и распространяется под лицензией CC-BY-4.0.