fix #23 by adding validating on first startup and close #30 by adding
tests / package-smoke-linux (push) Skipped
tests / package-smoke-windows (push) Skipped
tests / package-smoke-macos (push) Skipped
tests / macos (push) Successful in 1m20s
tests / linux (push) Successful in 2m14s
tests / windows (push) Failing after 23m21s

version number

Signed-off-by: galeon <dertiellis@gmail.com>
This commit is contained in:
galeon committed 2026-10-23 09:55:15 +03:00
1 parent bc205fa2e7
commit 1d781b5324
19 files changed
+805 -10

No files matched your search

+2 -1
View File
@@ -11,6 +11,7 @@ foreach(test IN ITEMS
tst_javalocator
tst_zlibreference
tst_buildarchiveworker
tst_buildswitcher)
tst_buildswitcher
tst_gamedirimport)
bilauncher_add_test(${test} LABEL unit SOURCES ${test}.cpp)
endforeach()
+243
View File
@@ -0,0 +1,243 @@
#include "testenv.h"
#include "fixtures.h"
#include "gamedirimport.h"
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QJsonArray>
class TestGameDirImport : public QObject
{
Q_OBJECT
QString m_game;
void writeVersion(const QString &id, const QJsonObject &json)
{
QVERIFY(Fixtures::writeJson(m_game + "/versions/" + id + "/" + id + ".json", json));
}
void writeVanilla(const QString &id) { writeVersion(id, {{"id", id}, {"type", "release"}}); }
void writeLoaderProfile(const QString &id, const QString &game, const QString &library)
{
QJsonArray libraries;
if (!library.isEmpty())
libraries.append(QJsonObject{{"name", library}});
writeVersion(id, {{"id", id}, {"inheritsFrom", game}, {"libraries", libraries}});
}
void writeProfiles(const QJsonObject &profiles)
{
QVERIFY(Fixtures::writeJson(m_game + "/launcher_profiles.json", {{"profiles", profiles}}));
}
static QJsonObject profile(const QString &versionId, const QString &lastUsed)
{
return {{"type", "custom"}, {"lastVersionId", versionId}, {"lastUsed", lastUsed}};
}
// Мтайм ставим явно: файлы, записанные подряд, легко получают одну секунду.
void touch(const QString &id, int secondsAgo)
{
QFile file(m_game + "/versions/" + id + "/" + id + ".json");
QVERIFY(file.open(QIODevice::ReadWrite));
QVERIFY(file.setFileTime(QDateTime::currentDateTime().addSecs(-secondsAgo),
QFileDevice::FileModificationTime));
}
private slots:
void init() { m_game = TestEnv::freshDir(QStringLiteral("game")); }
void missingOrEmptyFolderGivesNothing()
{
QVERIFY(!GameDirImport::detect(m_game + "/nope"));
QVERIFY(!GameDirImport::detect(m_game));
QVERIFY(!GameDirImport::detect(QString()));
}
void sharedFoldersAndLauncherJunkAreNotABuild()
{
Fixtures::write(m_game + "/libraries/x/y.jar", "lib");
Fixtures::write(m_game + "/assets/indexes/5.json", "{}");
Fixtures::write(m_game + "/launcher_log.txt", "log");
Fixtures::write(m_game + "/webcache2/a", "a");
QVERIFY(Fixtures::writeJson(m_game + "/launcher_profiles.json", {{"profiles", QJsonObject{}}}));
QVERIFY(QDir().mkpath(m_game + "/saves")); // пустая папка — не признак
QVERIFY(!GameDirImport::detect(m_game));
}
void contentWithoutVersionGivesEmptyBuild()
{
Fixtures::write(m_game + "/saves/World/level.dat", "w");
const auto found = GameDirImport::detect(m_game);
QVERIFY(found);
QCOMPARE(found->source, QStringLiteral("content"));
QVERIFY(found->minecraftVersion.isEmpty());
QVERIFY(found->resolvedVersionId.isEmpty());
QVERIFY(found->loader.isEmpty());
}
void lastUsedProfileWins()
{
writeVanilla("1.20.1");
writeVanilla("1.21.1");
writeVanilla("1.19.4");
touch("1.19.4", 0); // самая свежая по мтайму, но профили важнее
writeProfiles({{"a", profile("1.20.1", "2026-01-01T10:00:00.000Z")},
{"b", profile("1.21.1", "2026-03-01T10:00:00.000Z")},
// Версии нет на диске — пропускается, даже будучи последней.
{"c", profile("1.8.9", "2026-05-01T10:00:00.000Z")},
// latest-release без lastVersionId не разрешить без манифеста.
{"d", QJsonObject{{"type", "latest-release"}, {"lastUsed", "2026-06-01T10:00:00Z"}}}});
const auto found = GameDirImport::detect(m_game);
QVERIFY(found);
QCOMPARE(found->source, QStringLiteral("launcher_profiles"));
QCOMPARE(found->minecraftVersion, QStringLiteral("1.21.1"));
QVERIFY(found->resolvedVersionId.isEmpty());
QVERIFY(found->loader.isEmpty());
}
void withoutProfilesNewestVersionWins()
{
writeVanilla("1.20.1");
writeVanilla("1.21.1");
touch("1.20.1", 0);
touch("1.21.1", 3600);
Fixtures::write(m_game + "/launcher_profiles.json", "{ broken");
const auto found = GameDirImport::detect(m_game);
QVERIFY(found);
QCOMPARE(found->source, QStringLiteral("versions"));
QCOMPARE(found->minecraftVersion, QStringLiteral("1.20.1"));
}
void brokenVersionJsonIsSkipped()
{
writeVanilla("1.20.1");
Fixtures::write(m_game + "/versions/1.21.1/1.21.1.json", "not json");
touch("1.20.1", 3600);
touch("1.21.1", 0);
const auto found = GameDirImport::detect(m_game);
QVERIFY(found);
QCOMPARE(found->minecraftVersion, QStringLiteral("1.20.1"));
}
void modsPreferLoaderProfile()
{
writeVanilla("1.20.1");
writeLoaderProfile("fabric-loader-0.15.0-1.20.1", "1.20.1", "net.fabricmc:fabric-loader:0.15.0");
// Последней запускали ванилу, но моды лежат — значит, играли с Fabric.
writeProfiles({{"vanilla", profile("1.20.1", "2026-03-01T10:00:00.000Z")}});
Fixtures::write(m_game + "/mods/sodium.jar", "jar");
const auto found = GameDirImport::detect(m_game);
QVERIFY(found);
QCOMPARE(found->source, QStringLiteral("versions"));
QCOMPARE(found->minecraftVersion, QStringLiteral("1.20.1"));
QCOMPARE(found->resolvedVersionId, QStringLiteral("fabric-loader-0.15.0-1.20.1"));
QCOMPARE(found->loader, QStringLiteral("fabric"));
QCOMPARE(found->loaderVersion, QStringLiteral("0.15.0"));
// Без модов тот же набор даёт ванилу из профилей.
QVERIFY(QFile::remove(m_game + "/mods/sodium.jar"));
QCOMPARE(GameDirImport::detect(m_game)->resolvedVersionId, QString());
}
void fabricFixtureIsRecognised()
{
const QJsonObject json = Fixtures::readJson("versions/fabric-loader-0.15.0-1.20.1.json");
const auto loader = GameDirImport::loaderOf(json);
QCOMPARE(loader.key, QStringLiteral("fabric"));
QCOMPARE(loader.version, QStringLiteral("0.15.0"));
}
void loaderFromLibraries_data()
{
QTest::addColumn<QString>("game");
QTest::addColumn<QString>("library");
QTest::addColumn<QString>("key");
QTest::addColumn<QString>("version");
QTest::newRow("quilt") << "1.20.1" << "org.quiltmc:quilt-loader:0.26.0" << "quilt" << "0.26.0";
QTest::newRow("forge-1.20.1") << "1.20.1" << "net.minecraftforge:forge:1.20.1-47.2.0:universal"
<< "forge" << "47.2.0";
QTest::newRow("forge-1.12.2") << "1.12.2" << "net.minecraftforge:forge:1.12.2-14.23.5.2860"
<< "forge" << "14.23.5.2860";
// Как в каталоге модлоадеров: хвост "-1.7.10" остаётся.
QTest::newRow("forge-1.7.10") << "1.7.10" << "net.minecraftforge:forge:1.7.10-10.13.4.1614-1.7.10"
<< "forge" << "10.13.4.1614-1.7.10";
QTest::newRow("neoforge") << "1.21.1" << "net.neoforged:neoforge:21.1.77:universal"
<< "neoforge" << "21.1.77";
QTest::newRow("neoforge-1.20.1") << "1.20.1" << "net.neoforged:forge:1.20.1-47.1.106:universal"
<< "neoforge" << "47.1.106";
QTest::newRow("optifine") << "1.20.1" << "optifine:OptiFine:1.20.1_HD_U_I6" << "" << "";
}
void loaderFromLibraries()
{
QFETCH(QString, game);
QFETCH(QString, library);
QFETCH(QString, key);
QFETCH(QString, version);
const QJsonObject json{{"id", "custom"}, {"inheritsFrom", game},
{"libraries", QJsonArray{QJsonObject{{"name", "com.google:guava:31.0"}},
QJsonObject{{"name", library}}}}};
const auto loader = GameDirImport::loaderOf(json);
QCOMPARE(loader.key, key);
QCOMPARE(loader.version, version);
}
void loaderFromIdWhenLibrariesSilent_data()
{
QTest::addColumn<QString>("id");
QTest::addColumn<QString>("game");
QTest::addColumn<QString>("key");
QTest::addColumn<QString>("version");
QTest::newRow("fabric") << "fabric-loader-0.16.10-1.21.1" << "1.21.1" << "fabric" << "0.16.10";
QTest::newRow("quilt") << "quilt-loader-0.26.0-1.20.1" << "1.20.1" << "quilt" << "0.26.0";
QTest::newRow("forge") << "1.20.1-forge-47.2.0" << "1.20.1" << "forge" << "47.2.0";
QTest::newRow("neoforge") << "neoforge-21.1.77" << "1.21.1" << "neoforge" << "21.1.77";
QTest::newRow("unknown") << "1.20.1-OptiFine_HD_U_I6" << "1.20.1" << "" << "";
}
void loaderFromIdWhenLibrariesSilent()
{
QFETCH(QString, id);
QFETCH(QString, game);
QFETCH(QString, key);
QFETCH(QString, version);
const auto loader = GameDirImport::loaderOf({{"id", id}, {"inheritsFrom", game}});
QCOMPARE(loader.key, key);
QCOMPARE(loader.version, version);
}
void unknownInheritorKeepsResolvedId()
{
writeVanilla("1.20.1");
writeLoaderProfile("1.20.1-OptiFine_HD_U_I6", "1.20.1", "optifine:OptiFine:1.20.1_HD_U_I6");
writeProfiles({{"of", profile("1.20.1-OptiFine_HD_U_I6", "2026-03-01T10:00:00Z")}});
const auto found = GameDirImport::detect(m_game);
QVERIFY(found);
QCOMPARE(found->minecraftVersion, QStringLiteral("1.20.1"));
QCOMPARE(found->resolvedVersionId, QStringLiteral("1.20.1-OptiFine_HD_U_I6"));
QVERIFY(found->loader.isEmpty());
}
void detectionDoesNotTouchGameDir()
{
writeVanilla("1.20.1");
writeLoaderProfile("fabric-loader-0.15.0-1.20.1", "1.20.1", "net.fabricmc:fabric-loader:0.15.0");
writeProfiles({{"f", profile("fabric-loader-0.15.0-1.20.1", "2026-03-01T10:00:00Z")}});
Fixtures::write(m_game + "/mods/a.jar", "a");
Fixtures::write(m_game + "/saves/W/level.dat", "w");
const QStringList before = Fixtures::listTree(m_game);
QVERIFY(GameDirImport::detect(m_game));
QCOMPARE(Fixtures::listTree(m_game), before);
}
};
BILAUNCHER_TEST_MAIN(TestGameDirImport)
#include "tst_gamedirimport.moc"