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
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:
1 parent
bc205fa2e7
commit
1d781b5324
19 files changed
+805
-10
No files matched your search
@@ -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()
|
||||
@@ -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"
|
||||
Reference in new issue
Block a user