From b94d852d7c195343310c2bb83a8933b62262f595 Mon Sep 17 00:00:00 2001
From: galeon
Date: Mon, 26 Oct 2026 07:09:27 +0000
Subject: [PATCH 1/3] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?=
=?UTF-8?q?=D1=8C=20README.md?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: galeon
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index b749f26..fed95f8 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@
-Сезонные сборки: централизованное управление сборками со стороны команды разработчика, полная автоматизация скачивания, установки и разрешения зависиомстей для конечных пользователей
+Сезонные сборки: централизованное управление сборками со стороны команды разработчика, полная автоматизация скачивания, установки и разрешения зависимостей для конечных пользователей
From 6a2b596f329b195496db2463ce64bc4a217cdc21 Mon Sep 17 00:00:00 2001
From: galeon
Date: Sun, 1 Nov 2026 01:13:07 +0300
Subject: [PATCH 2/3] fix #33
Signed-off-by: galeon
---
BuildsDialog.qml | 1 +
Main.qml | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/BuildsDialog.qml b/BuildsDialog.qml
index 3731d21..a86b0f6 100644
--- a/BuildsDialog.qml
+++ b/BuildsDialog.qml
@@ -8,6 +8,7 @@ Dialog {
id: buildsDialog
required property var backend
+ enabled: !buildsDialog.backend.seasonalInstalling
property int editIndex: -1
property bool loading: false
diff --git a/Main.qml b/Main.qml
index 9b0ce5e..0a9562f 100644
--- a/Main.qml
+++ b/Main.qml
@@ -427,7 +427,7 @@ Window {
anchors.rightMargin: 0
anchors.bottomMargin: -218
hoverEnabled: true
- enabled: !backend.gameRunning && !backend.switching
+ enabled: !backend.gameRunning && !backend.switching && !backend.seasonalInstalling
opacity: enabled ? 1.0 : 0.45
contentItem: Text {
From 5635dcc558e79f09e53f914ccc691c810f0ccb9a Mon Sep 17 00:00:00 2001
From: galeon
Date: Sun, 1 Nov 2026 21:07:03 +0300
Subject: [PATCH 3/3] autotests fixes
Signed-off-by: galeon
---
.gitea/workflows/tests.yml | 7 ++-
doc/testing.md | 7 +--
modloaderversionservice.cpp | 9 ++--
tests/common/fakeinternet.cpp | 54 ++++++++++++++++++-----
tests/common/fakeinternet.h | 14 +++++-
tests/common/nettest.h | 2 +-
tests/net/tst_fakeinternet.cpp | 34 +++++++++-----
tests/net/tst_modloaderversionservice.cpp | 48 +++++++++++++-------
tests/net/tst_seasonalpackdownloader.cpp | 2 +-
tests/qml/tst_qml.cpp | 5 ++-
tests/qml/tst_qml_smoke.cpp | 4 +-
11 files changed, 134 insertions(+), 52 deletions(-)
diff --git a/.gitea/workflows/tests.yml b/.gitea/workflows/tests.yml
index 1055ce5..6afa466 100644
--- a/.gitea/workflows/tests.yml
+++ b/.gitea/workflows/tests.yml
@@ -12,12 +12,13 @@ concurrency:
group: tests-${{ github.ref }}
cancel-in-progress: true
+env:
+ BILAUNCHER_REQUIRE_FAKE_INTERNET: "1"
+
jobs:
linux:
runs-on: linux
timeout-minutes: 90
- env:
- BILAUNCHER_REQUIRE_FAKE_INTERNET: "1"
steps:
- uses: actions/checkout@v4
@@ -81,7 +82,9 @@ jobs:
ctest --test-dir $build --output-on-failure --output-junit "$build\junit.xml"
$code = $LASTEXITCODE
New-Item -ItemType Directory -Force test-results | Out-Null
+ git rev-parse HEAD | Set-Content -Encoding UTF8 test-results\commit.txt
Copy-Item "$build\junit.xml" test-results\ -ErrorAction SilentlyContinue
+ Copy-Item "$build\Testing\Temporary\LastTest.log" test-results\ -ErrorAction SilentlyContinue
if ($code) { throw "ctest" }
- name: Отчёты
diff --git a/doc/testing.md b/doc/testing.md
index 1127a9d..6fa45fc 100644
--- a/doc/testing.md
+++ b/doc/testing.md
@@ -87,7 +87,8 @@ CMake, тесты появятся в панели «Тесты».
в TLS с тестовым сертификатом из `tests/fixtures/tls/` и обслуживается по таблице маршрутов.
Запрос на адрес без маршрута получает 404 и попадает в `unmatched()`. При старте сервер проверяет
себя сам. Если TLS-бэкенд Qt не умеет серверный режим, сетевые тесты пропускаются с понятным
-сообщением, а в CI на Linux (`BILAUNCHER_REQUIRE_FAKE_INTERNET=1`) это ошибка.
+сообщением, а в CI на всех ОС (`BILAUNCHER_REQUIRE_FAKE_INTERNET=1`) это ошибка.
+QML-тесты также завершаются ошибкой, если стенд не поднялся.
`FakeMojang` (`tests/common/fakemojang.h`) публикует через `FakeInternet` крошечную, но устроенную
как настоящая версию игры: json, `client.jar`, библиотеки, натив под текущую ОС, индекс ресурсов,
@@ -112,7 +113,6 @@ CMake, тесты появятся в панели «Тесты».
| Тест | Проблема |
|------|----------|
| `tst_versioninstaller::retryOfLastFileIsNotLost` | если временный сбой (503, таймаут) или неверная контрольная сумма пришлись на последний файл, `retryOrFail()` откладывает повтор на 400 мс, а `pump()` сразу видит пустую очередь и завершает установку ошибкой «файлы отсутствуют» |
-| `tst_modloaderversionservice::neoForgeLegacyArtifactArrivesFirst` | если ответ старого артефакта `net.neoforged:forge` приходит раньше основного, основной затирает слитые записи (`state->merged = parsed`), и версии NeoForge для 1.20.1 пропадают |
| `tst_buildarchiveworker::dotFilesSurviveRoundTrip` | `QZipReader` срезает ведущую точку в именах записей, и после переключения сборок `.fabric/`, `.mixin.out/` возвращаются как `fabric/`, `mixin.out/` |
| `tst_javainstaller::cancelDuringDownload` | `JavaInstaller::cancel()` шлёт `canceled` дважды: `abort()` синхронно доводит ответ до обработчика, который уже шлёт сигнал |
| `tst_gamelauncher::extractNatives` | битый jar с нативами не считается ошибкой: `QZipReader::isReadable()` проверяет только, что файл открылся |
@@ -157,7 +157,8 @@ WebEngine задерживала бы релизы. Перед выпуском
4. Для сети поднимите `FakeInternet` в `initTestCase()` через `BILAUNCHER_START_FAKE_INTERNET(m_net)`
и опишите маршруты: `m_net.route("https://host/path", FakeResponse::json(...))`. Для префикса
поставьте `*` в конце, для ошибок, задержек и обрывов есть `FakeResponse::error`, `delayMs`,
- `hang`, `drop`, `truncateTo`.
+ `hang`, `drop`, `truncateTo`. Для заданного порядка ответов установите `deferred = true`
+ и вызовите `releaseResponse(url)` после обработки другого ответа клиентом.
5. Крупные ответы серверов кладите в `tests/fixtures/`. Если в json нужны имя ОС, архитектура или
классификатор нативов, используйте подстановки `%OS%`, `%OTHER%`, `%ARCH%`, `%NATIVE%` и
`Fixtures::readTemplate()`.
diff --git a/modloaderversionservice.cpp b/modloaderversionservice.cpp
index 1ea8b9a..c49bd18 100644
--- a/modloaderversionservice.cpp
+++ b/modloaderversionservice.cpp
@@ -244,6 +244,7 @@ void ModLoaderVersionService::startRefresh(ModLoader loader, const QString &game
{
int outstanding = 2;
QHash> merged;
+ QHash> legacy;
QHash recommended;
bool primaryOk = false;
QString warning;
@@ -258,6 +259,8 @@ void ModLoaderVersionService::startRefresh(ModLoader loader, const QString &game
return;
}
+ for (auto it = state->legacy.cbegin(); it != state->legacy.cend(); ++it)
+ state->merged[it.key()] += it.value();
for (auto it = state->merged.begin(); it != state->merged.end(); ++it) {
const QString promoted = state->recommended.value(it.key());
if (promoted.isEmpty())
@@ -317,10 +320,8 @@ void ModLoaderVersionService::startRefresh(ModLoader loader, const QString &game
connect(legacy, &QNetworkReply::finished, this, [legacy, state, settle] {
legacy->deleteLater();
if (legacy->error() == QNetworkReply::NoError) {
- const auto extra = parseMavenMetadata(legacy->readAll(), ModLoader::NeoForge,
- /*legacyNeoForge=*/true);
- for (auto it = extra.constBegin(); it != extra.constEnd(); ++it)
- state->merged[it.key()] += it.value();
+ state->legacy = parseMavenMetadata(legacy->readAll(), ModLoader::NeoForge,
+ /*legacyNeoForge=*/true);
}
settle();
});
diff --git a/tests/common/fakeinternet.cpp b/tests/common/fakeinternet.cpp
index 410aa90..78232fe 100644
--- a/tests/common/fakeinternet.cpp
+++ b/tests/common/fakeinternet.cpp
@@ -201,12 +201,32 @@ void FakeInternet::unroute(const QString &pattern)
void FakeInternet::reset()
{
+ m_deferred.clear();
+ m_diagnostics.clear();
m_routes.clear();
m_requests.clear();
m_unmatched.clear();
m_fallback = FakeResponse::error(404, "fake internet: no route");
}
+bool FakeInternet::releaseResponse(const QString &url)
+{
+ for (qsizetype i = 0; i < m_deferred.size(); ++i) {
+ if (m_deferred.at(i).url == url) {
+ const auto pending = m_deferred.takeAt(i);
+ respond(pending.socket, pending.response, pending.keepAlive);
+ return true;
+ }
+ }
+ return false;
+}
+
+QString FakeInternet::diagnostics() const
+{
+ return QStringLiteral("TLS backend: %1\nRequests: %2\n%3")
+ .arg(QSslSocket::activeBackend(), requestedUrls().join(u' '), m_diagnostics.join(u'\n'));
+}
+
QList FakeInternet::requests() const
{
return m_requests;
@@ -366,9 +386,13 @@ void FakeInternet::dispatch(Connection *connection, FakeRequest request)
}
const bool keepAlive = request.header("connection").toLower() != "close";
- QPointer socket(connection->socket);
+ QPointer socket(connection->socket);
if (response.hang)
return;
+ if (response.deferred) {
+ m_deferred.append({url, socket, response, keepAlive});
+ return;
+ }
if (response.delayMs > 0) {
QTimer::singleShot(response.delayMs, this,
[this, socket, response, keepAlive] { respond(socket, response, keepAlive); });
@@ -378,7 +402,7 @@ void FakeInternet::dispatch(Connection *connection, FakeRequest request)
emit requestServed(url);
}
-void FakeInternet::respond(QPointer socket, const FakeResponse &response, bool keepAlive)
+void FakeInternet::respond(QPointer socket, const FakeResponse &response, bool keepAlive)
{
if (!socket || socket->state() != QAbstractSocket::ConnectedState)
return;
@@ -397,20 +421,28 @@ void FakeInternet::respond(QPointer socket, const FakeResponse &resp
head += close ? "Connection: close\r\n" : "Connection: keep-alive\r\n";
head += "\r\n";
- socket->write(head);
if (response.truncateTo >= 0) {
- socket->write(response.body.left(response.truncateTo));
- socket->flush();
- socket->disconnectFromHost();
- // Schannel (Windows) на disconnectFromHost шлёт только TLS close_notify и держит
- // TCP открытым, ожидая ответного close_notify, — клиент тогда висит до таймаута.
- // Обрыв на то и обрыв: если сокет не закрылся сам, рвём соединение.
- QTimer::singleShot(200, socket, [socket] {
- if (socket && socket->state() != QAbstractSocket::UnconnectedState)
+ m_diagnostics << QStringLiteral("truncate: declared=%1, sent=%2")
+ .arg(response.body.size()).arg(response.body.left(response.truncateTo).size());
+ // Сначала отправляем TLS-записи с неполным телом, затем рвём TCP.
+ // disconnectFromHost() выполняет штатное завершение TLS, а здесь
+ // моделируется обрыв. Готовность определяется буферами, не задержкой.
+ connect(socket, &QSslSocket::encryptedBytesWritten, socket, [this, socket](qint64) {
+ QTimer::singleShot(0, socket, [this, socket] {
+ if (!socket || socket->state() != QAbstractSocket::ConnectedState)
+ return;
+ if (socket->bytesToWrite() || socket->encryptedBytesToWrite())
+ return;
+ m_diagnostics << QStringLiteral("truncate: TLS buffers drained, aborting TCP");
socket->abort();
+ m_diagnostics << QStringLiteral("truncate: state after abort=%1").arg(socket->state());
+ });
});
+ socket->write(head);
+ socket->write(response.body.left(response.truncateTo));
return;
}
+ socket->write(head);
socket->write(response.body);
if (close)
socket->disconnectFromHost();
diff --git a/tests/common/fakeinternet.h b/tests/common/fakeinternet.h
index 6e7f401..20d57c5 100644
--- a/tests/common/fakeinternet.h
+++ b/tests/common/fakeinternet.h
@@ -56,6 +56,7 @@ struct FakeResponse
QList> headers;
int delayMs = 0; // ответить с задержкой
bool hang = false; // не отвечать вовсе (для отмены и таймаутов)
+ bool deferred = false; // ждать явного releaseResponse(), без таймера
bool drop = false; // оборвать соединение без ответа
int truncateTo = -1; // объявить полный размер, отдать столько байт и оборвать
@@ -95,6 +96,8 @@ public:
// Сбросить маршруты и журнал.
void reset();
+ bool releaseResponse(const QString &url);
+ QString diagnostics() const;
QList requests() const;
// Сколько раз был запрошен адрес "https://host/path" (query не учитывается).
@@ -122,7 +125,7 @@ private:
void onReadyRead(Connection *connection);
void processBuffer(Connection *connection);
void dispatch(Connection *connection, FakeRequest request);
- void respond(QPointer socket, const FakeResponse &response, bool keepAlive);
+ void respond(QPointer socket, const FakeResponse &response, bool keepAlive);
void startTls(Connection *connection);
const Route *match(const FakeRequest &request) const;
@@ -130,6 +133,15 @@ private:
FakeResponse m_fallback;
QList m_requests;
QStringList m_unmatched;
+ struct DeferredResponse
+ {
+ QString url;
+ QPointer socket;
+ FakeResponse response;
+ bool keepAlive;
+ };
+ QList m_deferred;
+ QStringList m_diagnostics;
QByteArray m_certificate;
QByteArray m_key;
bool m_started = false;
diff --git a/tests/common/nettest.h b/tests/common/nettest.h
index 63b2918..e432301 100644
--- a/tests/common/nettest.h
+++ b/tests/common/nettest.h
@@ -8,7 +8,7 @@
#include
// В initTestCase(): поднять FakeInternet или пропустить весь тест-класс.
-// С BILAUNCHER_REQUIRE_FAKE_INTERNET=1 (так в CI на Linux) недоступность —
+// С BILAUNCHER_REQUIRE_FAKE_INTERNET=1 (так в CI на всех ОС) недоступность —
// ошибка, а не пропуск: иначе сломанный перехват тихо выключил бы все сетевые тесты.
#define BILAUNCHER_START_FAKE_INTERNET(net) \
do { \
diff --git a/tests/net/tst_fakeinternet.cpp b/tests/net/tst_fakeinternet.cpp
index 7e7e395..e1a36c0 100644
--- a/tests/net/tst_fakeinternet.cpp
+++ b/tests/net/tst_fakeinternet.cpp
@@ -3,6 +3,7 @@
#include "testenv.h"
#include "fakeinternet.h"
+#include "nettest.h"
#include
#include
@@ -30,9 +31,7 @@ class TestFakeInternet : public QObject
private slots:
void initTestCase()
{
- QString error;
- if (!m_net.start(&error))
- QSKIP(qPrintable(QStringLiteral("FakeInternet недоступен: ") + error));
+ BILAUNCHER_START_FAKE_INTERNET(m_net);
}
void init() { m_net.reset(); }
@@ -100,12 +99,6 @@ private slots:
response.drop = true;
return response;
}());
- m_net.route(QStringLiteral("https://a.test/short"), [] {
- FakeResponse response = FakeResponse::bytes(QByteArray(1000, 'x'));
- response.truncateTo = 10;
- return response;
- }());
-
QNetworkAccessManager manager;
QScopedPointer r500(get(manager, QStringLiteral("https://a.test/500")));
QVERIFY(waitFinished(r500.data()));
@@ -114,10 +107,31 @@ private slots:
QScopedPointer drop(get(manager, QStringLiteral("https://a.test/drop")));
QVERIFY(waitFinished(drop.data()));
QVERIFY(drop->error() != QNetworkReply::NoError);
+ }
+ void truncatedResponse_data()
+ {
+ QTest::addColumn("bytes");
+ QTest::newRow("empty") << 0;
+ QTest::newRow("short") << 10;
+ QTest::newRow("multiple-tls-records") << 65536;
+ QTest::newRow("last-byte-missing") << 256 * 1024 - 1;
+ }
+
+ void truncatedResponse()
+ {
+ QFETCH(int, bytes);
+ const QByteArray body(256 * 1024, 'x');
+ FakeResponse cut = FakeResponse::bytes(body);
+ cut.truncateTo = bytes;
+ m_net.route(QStringLiteral("https://a.test/short"), cut);
+ QNetworkAccessManager manager;
QScopedPointer shortReply(get(manager, QStringLiteral("https://a.test/short")));
- QVERIFY(waitFinished(shortReply.data()));
+ QVERIFY2(waitFinished(shortReply.data()),
+ qPrintable(m_net.diagnostics() + "\nClient: " + shortReply->errorString()));
QVERIFY(shortReply->error() != QNetworkReply::NoError);
+ QCOMPARE(shortReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
+ QCOMPARE(shortReply->readAll(), body.left(bytes));
}
void plainHttpThroughProxy()
diff --git a/tests/net/tst_modloaderversionservice.cpp b/tests/net/tst_modloaderversionservice.cpp
index b6e86b2..b15751e 100644
--- a/tests/net/tst_modloaderversionservice.cpp
+++ b/tests/net/tst_modloaderversionservice.cpp
@@ -7,6 +7,8 @@
#include "modloaderversionservice.h"
#include
+#include
+#include
namespace {
const QString kFabric = QStringLiteral("https://meta.fabricmc.net/v2/versions/loader/1.20.1");
@@ -55,6 +57,25 @@ class TestModLoaderVersionService : public QObject
return QTest::qWaitFor([&] { return r.calls > 0; }, 15000);
}
+ bool loadNeoForgeInOrder(ModLoaderVersionService &service, CallResult &result,
+ const QString &first, const QString &second)
+ {
+ QStringList completed;
+ QObject observer;
+ auto *network = service.findChild();
+ if (!network)
+ qFatal("ModLoaderVersionService has no network manager");
+ connect(network, &QNetworkAccessManager::finished, &observer, [&](QNetworkReply *reply) {
+ completed << reply->url().toString();
+ });
+ service.ensureLoaded(ModLoader::NeoForge, "1.20.1", result.callback());
+ if (!QTest::qWaitFor([&] { return completed.contains(first) && m_net.hits(second) == 1; }, 15000))
+ return false;
+ if (!m_net.releaseResponse(second))
+ return false;
+ return QTest::qWaitFor([&] { return result.calls == 1; }, 15000);
+ }
+
private slots:
void initTestCase() { BILAUNCHER_START_FAKE_INTERNET(m_net); }
@@ -167,33 +188,30 @@ private slots:
void neoForgeLegacyArtifactArrivesLast()
{
// Ранние сборки под 1.20.1 — из старого артефакта net.neoforged:forge.
- FakeResponse legacySlow = xml(Fixtures::read("loaders/neoforge-legacy-maven-metadata.xml"));
- legacySlow.delayMs = 400;
- m_net.route(kNeoLegacyMeta, legacySlow);
+ FakeResponse legacy = xml(Fixtures::read("loaders/neoforge-legacy-maven-metadata.xml"));
+ legacy.deferred = true;
+ m_net.route(kNeoLegacyMeta, legacy);
ModLoaderVersionService service;
CallResult r;
- QVERIFY(load(service, ModLoader::NeoForge, "1.20.1", r));
+ QVERIFY(loadNeoForgeInOrder(service, r, kNeoMeta, kNeoLegacyMeta));
QVERIFY(r.ok);
- const QList legacy = service.versions(ModLoader::NeoForge, "1.20.1");
- QCOMPARE(loaderVersions(legacy), QStringList({"47.1.106", "47.1.79"}));
- QCOMPARE(legacy.at(0).installerUrl,
+ const QList versions = service.versions(ModLoader::NeoForge, "1.20.1");
+ QCOMPARE(loaderVersions(versions), QStringList({"47.1.106", "47.1.79"}));
+ QCOMPARE(versions.at(0).installerUrl,
QUrl("https://maven.neoforged.net/releases/net/neoforged/forge/1.20.1-47.1.106/forge-1.20.1-47.1.106-installer.jar"));
}
void neoForgeLegacyArtifactArrivesFirst()
{
- // Тот же каталог, но маленький legacy-ответ приходит раньше основного.
- FakeResponse primarySlow = xml(Fixtures::read("loaders/neoforge-maven-metadata.xml"));
- primarySlow.delayMs = 400;
- m_net.route(kNeoMeta, primarySlow);
+ // Основной ответ выпускается только после обработки legacy клиентом.
+ FakeResponse primary = xml(Fixtures::read("loaders/neoforge-maven-metadata.xml"));
+ primary.deferred = true;
+ m_net.route(kNeoMeta, primary);
ModLoaderVersionService service;
CallResult r;
- QVERIFY(load(service, ModLoader::NeoForge, "1.20.1", r));
+ QVERIFY(loadNeoForgeInOrder(service, r, kNeoLegacyMeta, kNeoMeta));
QVERIFY(r.ok);
QCOMPARE(service.versions(ModLoader::NeoForge, "1.21.1").size(), 2);
- // В startRefresh() основной ответ делает state->merged = parsed и затирает
- // уже слитые записи legacy — версии NeoForge для 1.20.1 пропадают.
- QEXPECT_FAIL("", "гонка: основной maven-metadata NeoForge затирает уже пришедший legacy", Continue);
QCOMPARE(service.versions(ModLoader::NeoForge, "1.20.1").size(), 2);
}
diff --git a/tests/net/tst_seasonalpackdownloader.cpp b/tests/net/tst_seasonalpackdownloader.cpp
index 4e973a0..a999ad4 100644
--- a/tests/net/tst_seasonalpackdownloader.cpp
+++ b/tests/net/tst_seasonalpackdownloader.cpp
@@ -94,7 +94,7 @@ private slots:
m_net.route(kPack, cut);
Run run(downloader);
downloader.download(QUrl(kPack), m_dir + "/b.zip", Fixtures::sha256(QByteArray(4096, 'x')), 4096, "B");
- QVERIFY(run.waitEnd());
+ QVERIFY2(run.waitEnd(), qPrintable(m_net.diagnostics()));
QCOMPARE(run.failed.count(), 1);
QVERIFY(!QFile::exists(m_dir + "/b.zip"));
}
diff --git a/tests/qml/tst_qml.cpp b/tests/qml/tst_qml.cpp
index 908adbb..2809b0f 100644
--- a/tests/qml/tst_qml.cpp
+++ b/tests/qml/tst_qml.cpp
@@ -34,8 +34,9 @@ public slots:
seedData();
// Диалоги создают настоящий LauncherBackend — наружу он не должен ходить.
QString error;
- if (m_net.start(&error))
- m_net.setFallback(FakeResponse::error(503));
+ if (!m_net.start(&error))
+ qFatal("FakeInternet: %s", qPrintable(error));
+ m_net.setFallback(FakeResponse::error(503));
}
void qmlEngineAvailable(QQmlEngine *engine)
diff --git a/tests/qml/tst_qml_smoke.cpp b/tests/qml/tst_qml_smoke.cpp
index ff0ba67..afdfe0f 100644
--- a/tests/qml/tst_qml_smoke.cpp
+++ b/tests/qml/tst_qml_smoke.cpp
@@ -49,8 +49,8 @@ private slots:
void initTestCase()
{
QString error;
- if (m_net.start(&error))
- m_net.setFallback(FakeResponse::error(503));
+ QVERIFY2(m_net.start(&error), qPrintable(error));
+ m_net.setFallback(FakeResponse::error(503));
const QString game = TestEnv::freshDir(QStringLiteral("game"));
Fixtures::writeJson(LauncherPaths::settingsFile(), QJsonObject{{"gameDir", game}, {"language", "en"}});
QQuickStyle::setStyle(QStringLiteral("Basic"));