Compare commits
6
Commits
3517f1f236
..
rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a2698188f | ||
|
|
5111e90740 | ||
|
|
39b2204e67 | ||
|
|
5635dcc558 | ||
|
|
6a2b596f32 | ||
|
|
b94d852d7c |
No files matched your search
@@ -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: Отчёты
|
||||
|
||||
@@ -8,6 +8,7 @@ Dialog {
|
||||
id: buildsDialog
|
||||
|
||||
required property var backend
|
||||
enabled: !buildsDialog.backend.seasonalInstalling
|
||||
property int editIndex: -1
|
||||
property bool loading: false
|
||||
|
||||
|
||||
@@ -19,3 +19,9 @@
|
||||
- Исправлен баг отображения статуса активной сборки на зелёном фоне
|
||||
- Исправлен баг, что во время игры можно было потенциально сменить сборку
|
||||
- Исправлен баг первого запуска после установки лаунчера, если у пользователя была не пустая .minecraft
|
||||
|
||||
## [1.0.2-rc1] — 2026-10-23
|
||||
### Добавлено
|
||||
### Исправлено
|
||||
- Исправлен баг с возможностью изменять текущую сборку во время установки сезонной
|
||||
- Мелкие исправления
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
project(BiLauncher VERSION 1.0.1 LANGUAGES CXX C)
|
||||
project(BiLauncher VERSION 1.0.2 LANGUAGES CXX C)
|
||||
|
||||
# Канал сборки (dev / rc / релиз) не хранится в репозитории, а вычисляется
|
||||
# при сборке — см. cmake/LauncherVersion.cmake. CI передаёт полную версию:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+4
-3
@@ -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()`.
|
||||
|
||||
@@ -244,6 +244,7 @@ void ModLoaderVersionService::startRefresh(ModLoader loader, const QString &game
|
||||
{
|
||||
int outstanding = 2;
|
||||
QHash<QString, QList<LoaderVersionEntry>> merged;
|
||||
QHash<QString, QList<LoaderVersionEntry>> legacy;
|
||||
QHash<QString, QString> 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,
|
||||
state->legacy = parseMavenMetadata(legacy->readAll(), ModLoader::NeoForge,
|
||||
/*legacyNeoForge=*/true);
|
||||
for (auto it = extra.constBegin(); it != extra.constEnd(); ++it)
|
||||
state->merged[it.key()] += it.value();
|
||||
}
|
||||
settle();
|
||||
});
|
||||
|
||||
@@ -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<FakeRequest> 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<QTcpSocket> socket(connection->socket);
|
||||
QPointer<QSslSocket> 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<QTcpSocket> socket, const FakeResponse &response, bool keepAlive)
|
||||
void FakeInternet::respond(QPointer<QSslSocket> socket, const FakeResponse &response, bool keepAlive)
|
||||
{
|
||||
if (!socket || socket->state() != QAbstractSocket::ConnectedState)
|
||||
return;
|
||||
@@ -397,20 +421,28 @@ void FakeInternet::respond(QPointer<QTcpSocket> 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();
|
||||
|
||||
@@ -56,6 +56,7 @@ struct FakeResponse
|
||||
QList<QPair<QByteArray, QByteArray>> 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<FakeRequest> 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<QTcpSocket> socket, const FakeResponse &response, bool keepAlive);
|
||||
void respond(QPointer<QSslSocket> 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<FakeRequest> m_requests;
|
||||
QStringList m_unmatched;
|
||||
struct DeferredResponse
|
||||
{
|
||||
QString url;
|
||||
QPointer<QSslSocket> socket;
|
||||
FakeResponse response;
|
||||
bool keepAlive;
|
||||
};
|
||||
QList<DeferredResponse> m_deferred;
|
||||
QStringList m_diagnostics;
|
||||
QByteArray m_certificate;
|
||||
QByteArray m_key;
|
||||
bool m_started = false;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <QtTest/QTest>
|
||||
|
||||
// В initTestCase(): поднять FakeInternet или пропустить весь тест-класс.
|
||||
// С BILAUNCHER_REQUIRE_FAKE_INTERNET=1 (так в CI на Linux) недоступность —
|
||||
// С BILAUNCHER_REQUIRE_FAKE_INTERNET=1 (так в CI на всех ОС) недоступность —
|
||||
// ошибка, а не пропуск: иначе сломанный перехват тихо выключил бы все сетевые тесты.
|
||||
#define BILAUNCHER_START_FAKE_INTERNET(net) \
|
||||
do { \
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "testenv.h"
|
||||
#include "fakeinternet.h"
|
||||
#include "nettest.h"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
@@ -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<QNetworkReply> r500(get(manager, QStringLiteral("https://a.test/500")));
|
||||
QVERIFY(waitFinished(r500.data()));
|
||||
@@ -114,10 +107,31 @@ private slots:
|
||||
QScopedPointer<QNetworkReply> drop(get(manager, QStringLiteral("https://a.test/drop")));
|
||||
QVERIFY(waitFinished(drop.data()));
|
||||
QVERIFY(drop->error() != QNetworkReply::NoError);
|
||||
}
|
||||
|
||||
void truncatedResponse_data()
|
||||
{
|
||||
QTest::addColumn<int>("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<QNetworkReply> 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()
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "modloaderversionservice.h"
|
||||
|
||||
#include <QSignalSpy>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
|
||||
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<QNetworkAccessManager *>();
|
||||
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<LoaderVersionEntry> 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<LoaderVersionEntry> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ public slots:
|
||||
seedData();
|
||||
// Диалоги создают настоящий LauncherBackend — наружу он не должен ходить.
|
||||
QString error;
|
||||
if (m_net.start(&error))
|
||||
if (!m_net.start(&error))
|
||||
qFatal("FakeInternet: %s", qPrintable(error));
|
||||
m_net.setFallback(FakeResponse::error(503));
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QString error;
|
||||
if (m_net.start(&error))
|
||||
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"}});
|
||||
|
||||
Reference in new issue
Block a user