Files
galeon 5635dcc558
tests / package-smoke-linux (push) Skipped
tests / package-smoke-windows (push) Skipped
tests / macos (push) Successful in 1m22s
tests / linux (pull_request) Successful in 1m54s
tests / package-smoke-macos (push) Skipped
tests / linux (push) Successful in 2m43s
tests / windows (push) Successful in 4m13s
tests / windows (pull_request) Skipped
tests / macos (pull_request) Skipped
tests / package-smoke-linux (pull_request) Skipped
tests / package-smoke-windows (pull_request) Skipped
tests / package-smoke-macos (pull_request) Skipped
autotests fixes
Signed-off-by: galeon <dertiellis@gmail.com>
2026-11-01 21:07:03 +03:00

16 KiB
Raw Permalink Blame History

Автотесты

Тесты лежат в tests/ и собираются только по опции BILAUNCHER_BUILD_TESTS. В обычную сборку и в релизы они не попадают, код лаунчера ради них не менялся.

Как запустить

cmake -S . -B build-tests -G Ninja -DCMAKE_BUILD_TYPE=Debug -DBILAUNCHER_BUILD_TESTS=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.11.1/macos
cmake --build build-tests
ctest --test-dir build-tests --output-on-failure -j 6

Отдельный слой — по метке: ctest --test-dir build-tests -L unit (ещё net, backend, qml). Отдельный тест — напрямую, с фильтром по функции:

build-tests/tests/net/tst_versioninstaller installsModernVersionCompletely

Статические проверки (переводы, qmllint, раздел CHANGELOG для текущей версии):

python3 tests/scripts/static_checks.py build-tests

В Qt Creator проект с тестами открывается как обычно: включите BILAUNCHER_BUILD_TESTS в настройках CMake, тесты появятся в панели «Тесты».

Что покрыто

Метка Тест Что проверяет
unit tst_launcherpaths раскладка папки лаунчера, ensureRootExists и её отказ, если на месте папки файл
unit tst_javaruntime ключи видов Java, JavaRuntimeStore, какая Java нужна какой версии игры
unit tst_modloader ключи и названия модлоадеров
unit tst_localization переключение языков, запасной язык, отсутствующий ключ, язык из settings.json
unit tst_minecraftversion правила Mojang, загрузка json версии, inheritsFrom, нативы, удаление версий
unit tst_authoffline офлайн-UUID как у сервера в offline-mode
unit tst_msaredirect разбор адреса возврата после входа Microsoft
unit tst_gamelauncher командная строка игры, нативы, запуск, падение и остановка процесса
unit tst_javalocator версия java по -version, выбор подходящей, поиск в папке лаунчера
unit tst_zlibreference LD_PRELOAD с эталонным zlib на Linux
unit tst_buildarchiveworker архив, очистка, восстановление, раскатка пака, zip-slip, отмена
unit tst_buildswitcher переключение сборок туда и обратно, продолжение после обрыва, пак
unit tst_gamedirimport выбор версии по launcher_profiles.json и versions/, модлоадер по библиотекам и id, папка не меняется
net tst_fakeinternet самопроверка перехвата HTTPS (см. ниже)
net tst_versionmanifestservice каталог Mojang: разбор, кеш, офлайн, общие запросы
net tst_seasonalbuildservice каталог сезонных сборок: проверки полей, формат, кеш по адресу
net tst_modloaderversionservice Fabric, Quilt, Forge (с рекомендованными), NeoForge обе схемы
net tst_javaruntimeservice Adoptium и Mojang, частичные отказы, кеш, запасной x64 на ARM
net tst_authservice Ely.by: пароль, 2FA, продление токена, authlib-injector
net tst_msaauthservice вся цепочка Microsoft → Xbox → XSTS → Minecraft, коды ошибок XSTS
net tst_versioninstaller установка версии целиком, повторы, контрольные суммы, отмена, очередь
net tst_seasonalpackdownloader загрузка пака: sha256, обрыв, отмена
net tst_javainstaller Temurin zip и tar.gz, дерево Mojang, zip-slip, отмена
net tst_modloaderinstaller Fabric по сети, Forge через подставную java, откат после сбоя
backend tst_launcherbackend всё, что зовёт QML: профили, сборки, настройки, миграция, битые файлы, каталог версий, запуск офлайн / Ely.by с 2FA / Microsoft, Java, сезонная сборка от каталога до раскатки обновления
qml tst_qml Qt Quick Test: DarkCombo, LabelledField, ProgressPanel, LoaderRow, диалоги с настоящим LauncherBackend
qml tst_qml_smoke Main.qml поднимается, как в main.cpp, без единого предупреждения QML, в том числе после смены языка

Ночью и перед релизом ещё собирается Release, ставится через cmake --install и запускается на 10 секунд (tests/scripts/smoke_installed.sh / .ps1). Это ловит то, чего не видят тесты: недоложенные деплоем модули Qt и плагины, ссылки на библиотеки вне дерева, неподписанный бандл.

Как тесты изолированы

Файлы. LauncherPaths строит пути от домашней папки. Общий main тестов (tests/common/testenv.h) до создания приложения направляет BILAUNCHER_HOME (а заодно HOME и USERPROFILE) на временную папку, и каждый процесс работает в своей, на всех ОС, параллельно. BILAUNCHER_HOME нужна ради Windows: там QDir::homePath() берёт профиль у системы и HOME с USERPROFILE не читает. Дочерние процессы наследуют переменную. Если папка лаунчера или папка игры по умолчанию всё же оказалась вне песочницы, процесс сразу завершается с кодом 2 и ничего не трогает. Дымовая проверка установленной сборки на Windows (smoke_installed.ps1) тоже запускает лаунчер с BILAUNCHER_HOME на временную папку.

Сеть. Адреса серверов в сервисах зашиты константами, а свои QNetworkAccessManager они создают сами. Зато прокси и SSL-настройки у Qt общие на процесс, и код лаунчера их не трогает. FakeInternet (tests/common/fakeinternet.h) ставит прокси на локальный порт и отключает проверку сертификата. Каждое https-соединение приходит к нему как CONNECT host:443, переводится в TLS с тестовым сертификатом из tests/fixtures/tls/ и обслуживается по таблице маршрутов. Запрос на адрес без маршрута получает 404 и попадает в unmatched(). При старте сервер проверяет себя сам. Если TLS-бэкенд Qt не умеет серверный режим, сетевые тесты пропускаются с понятным сообщением, а в CI на всех ОС (BILAUNCHER_REQUIRE_FAKE_INTERNET=1) это ошибка. QML-тесты также завершаются ошибкой, если стенд не поднялся.

FakeMojang (tests/common/fakemojang.h) публикует через FakeInternet крошечную, но устроенную как настоящая версию игры: json, client.jar, библиотеки, натив под текущую ОС, индекс ресурсов, сами ресурсы, конфиг логирования.

Java. fake_java (tests/fake_java/main.cpp, без Qt) копируется в нужную папку как bin/java (java.exe/javaw.exe). На -version она отвечает версией из fake-java.cfg рядом с собой, а при обычном запуске пишет аргументы в файл, печатает заданные строки, создаёт файлы (так изображается установщик Forge) и выходит с нужным кодом либо падает.

Код лаунчера. tests/CMakeLists.txt собирает все .cpp из корня, кроме main.cpp, в статическую библиотеку-QML-модуль bilauncher_testlib. Новый файл попадёт в тесты сам. QML-файлы берутся тоже все, ресурсы — из строк RESOURCES корневого CMakeLists.txt. Тестовая библиотека собирается без Qt WebEngine, так что backend.microsoftAvailable в тестах всегда false.

Найденные проблемы (QEXPECT_FAIL)

Тест, который описывает правильное поведение, но пока падает, помечен QEXPECT_FAIL. В отчёте он выглядит как XFAIL и прогон не валит. Когда ошибку исправят, тест станет XPASS и прогон упадёт: тогда пометку нужно снять.

Тест Проблема
tst_versioninstaller::retryOfLastFileIsNotLost если временный сбой (503, таймаут) или неверная контрольная сумма пришлись на последний файл, retryOrFail() откладывает повтор на 400 мс, а pump() сразу видит пустую очередь и завершает установку ошибкой «файлы отсутствуют»
tst_buildarchiveworker::dotFilesSurviveRoundTrip QZipReader срезает ведущую точку в именах записей, и после переключения сборок .fabric/, .mixin.out/ возвращаются как fabric/, mixin.out/
tst_javainstaller::cancelDuringDownload JavaInstaller::cancel() шлёт canceled дважды: abort() синхронно доводит ответ до обработчика, который уже шлёт сигнал
tst_gamelauncher::extractNatives битый jar с нативами не считается ошибкой: QZipReader::isReadable() проверяет только, что файл открылся

Ещё одна особенность, которую тесты обходят: синглтон Localization запоминает JS-движок, который его создал. Второй QQmlApplicationEngine после уничтожения первого оставляет этот указатель висячим, поэтому дымовой тест держит один движок на весь прогон, как и приложение.

CI: .gitea/workflows/tests.yml

Тесты идут на тех же host-раннерах, что собирают релизы. Отдельные машины и Docker не нужны: Qt, CMake и Ninja там уже есть, пути берутся из QT_ROOT и QT_TOOLS в config.yaml раннера.

Событие Что запускается
push в любую ветку, pull request Linux: сборка с тестами, статические проверки, все тесты
ночью (02:00 UTC) и вручную (Run workflow) Linux, Windows и macOS, плюс дымовая проверка установленного дерева на всех трёх

Windows и macOS на каждый коммит не запускаются: вместимость у раннеров 1, и часовая сборка с WebEngine задерживала бы релизы. Перед выпуском версии запустите workflow вручную и дождитесь зелёного прогона. release.yml от тестов не зависит.

Новый push в ту же ветку отменяет незаконченный прогон (concurrency). Отчёт JUnit (junit.xml), лог qmllint и полный лог ctest прикладываются к прогону как артефакты.

На Linux-раннере полезен ccache: с ним повторная сборка занимает минуты. ci/setup-runner-linux.sh ставит его вместе с xvfb, который нужен дымовой проверке, если деплой Qt не положил плагин offscreen.

qmllint. В коде уже есть 53 предупреждения, а код ради тестов не правится. Поэтому проверка работает как храповик: она падает, только если предупреждений стало больше числа в tests/qmllint-baseline.txt. Если часть исправили, опустите число.

Как добавить тест

  1. Создайте tests/<слой>/tst_<имя>.cpp с классом на QtTest и в конце файла напишите BILAUNCHER_TEST_MAIN(Класс) (или BILAUNCHER_GUI_TEST_MAIN, если нужен QGuiApplication). Затем #include "tst_<имя>.moc".
  2. Добавьте имя в список в tests/<слой>/CMakeLists.txt.
  3. В init() вызывайте TestEnv::resetLauncherRoot(), папку игры берите из TestEnv::freshDir("game").
  4. Для сети поднимите FakeInternet в initTestCase() через BILAUNCHER_START_FAKE_INTERNET(m_net) и опишите маршруты: m_net.route("https://host/path", FakeResponse::json(...)). Для префикса поставьте * в конце, для ошибок, задержек и обрывов есть FakeResponse::error, delayMs, hang, drop, truncateTo. Для заданного порядка ответов установите deferred = true и вызовите releaseResponse(url) после обработки другого ответа клиентом.
  5. Крупные ответы серверов кладите в tests/fixtures/. Если в json нужны имя ОС, архитектура или классификатор нативов, используйте подстановки %OS%, %OTHER%, %ARCH%, %NATIVE% и Fixtures::readTemplate().

У moc есть ограничение: сырые строки R"(...)" и длинные вложенные инициализаторы {{...}}, разбитые на строки, внутри файла с Q_OBJECT сбивают его разбор («Not a signal or slot declaration» или пустой .moc). Такие данные выносите в фикстуры или собирайте по шагам.


При создании этого документа использовался ИИ.