1 parent
ebf8ff7869
commit
41599fa82f
67 files changed
+8775
-4
No files matched your search
@@ -0,0 +1,353 @@
|
||||
#include "testenv.h"
|
||||
#include "fixtures.h"
|
||||
|
||||
#include "localization.h"
|
||||
#include "minecraftversion.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QJsonArray>
|
||||
#include <QSysInfo>
|
||||
|
||||
namespace {
|
||||
|
||||
void writeVersion(const QString &gameDir, const QString &id, const QByteArray &json, bool withJar)
|
||||
{
|
||||
const QString dir = gameDir + "/versions/" + id;
|
||||
Fixtures::write(dir + u'/' + id + ".json", json);
|
||||
if (withJar)
|
||||
Fixtures::write(dir + u'/' + id + ".jar", "jar-" + id.toUtf8());
|
||||
}
|
||||
|
||||
QString otherOs()
|
||||
{
|
||||
return Fixtures::otherOsName();
|
||||
}
|
||||
|
||||
// Современный ванильный json (1.19+): arguments, natives-классификаторы, logging, quickPlay.
|
||||
QByteArray modernVanilla()
|
||||
{
|
||||
return Fixtures::readTemplate(QStringLiteral("versions/modern-1.20.1.json"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class TestMinecraftVersion : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QString m_game;
|
||||
|
||||
private slots:
|
||||
void init() { m_game = TestEnv::freshDir(QStringLiteral("game")); }
|
||||
|
||||
// --- правила ---------------------------------------------------------------
|
||||
|
||||
void rulesAllow_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("rules");
|
||||
QTest::addColumn<QStringList>("features");
|
||||
QTest::addColumn<bool>("allowed");
|
||||
|
||||
QTest::newRow("no rules") << QByteArray("[]") << QStringList{} << true;
|
||||
QTest::newRow("allow all") << QByteArray("[{\"action\":\"allow\"}]") << QStringList{} << true;
|
||||
QTest::newRow("disallow all") << QByteArray("[{\"action\":\"disallow\"}]") << QStringList{} << false;
|
||||
QTest::newRow("allow this os")
|
||||
<< QByteArray("[{\"action\":\"allow\",\"os\":{\"name\":\"%OS%\"}}]") << QStringList{} << true;
|
||||
QTest::newRow("allow other os only")
|
||||
<< QByteArray("[{\"action\":\"allow\",\"os\":{\"name\":\"%OTHER%\"}}]") << QStringList{} << false;
|
||||
QTest::newRow("allow all but this os")
|
||||
<< QByteArray("[{\"action\":\"allow\"},{\"action\":\"disallow\",\"os\":{\"name\":\"%OS%\"}}]")
|
||||
<< QStringList{} << false;
|
||||
QTest::newRow("allow all but other os")
|
||||
<< QByteArray("[{\"action\":\"allow\"},{\"action\":\"disallow\",\"os\":{\"name\":\"%OTHER%\"}}]")
|
||||
<< QStringList{} << true;
|
||||
QTest::newRow("arch matches")
|
||||
<< QByteArray("[{\"action\":\"allow\",\"os\":{\"arch\":\"%ARCH%\"}}]") << QStringList{} << true;
|
||||
QTest::newRow("arch differs")
|
||||
<< QByteArray("[{\"action\":\"allow\",\"os\":{\"arch\":\"sparc\"}}]") << QStringList{} << false;
|
||||
QTest::newRow("os version regex any")
|
||||
<< QByteArray("[{\"action\":\"allow\",\"os\":{\"version\":\".*\"}}]") << QStringList{} << true;
|
||||
QTest::newRow("os version regex none")
|
||||
<< QByteArray("[{\"action\":\"allow\",\"os\":{\"version\":\"^nope$\"}}]") << QStringList{} << false;
|
||||
QTest::newRow("os version invalid regex")
|
||||
<< QByteArray("[{\"action\":\"allow\",\"os\":{\"version\":\"(\"}}]") << QStringList{} << false;
|
||||
QTest::newRow("feature missing")
|
||||
<< QByteArray("[{\"action\":\"allow\",\"features\":{\"is_demo_user\":true}}]") << QStringList{} << false;
|
||||
QTest::newRow("feature present")
|
||||
<< QByteArray("[{\"action\":\"allow\",\"features\":{\"is_demo_user\":true}}]")
|
||||
<< QStringList{"is_demo_user"} << true;
|
||||
QTest::newRow("feature must be absent")
|
||||
<< QByteArray("[{\"action\":\"allow\",\"features\":{\"has_custom_resolution\":false}}]")
|
||||
<< QStringList{} << true;
|
||||
QTest::newRow("last match wins")
|
||||
<< QByteArray("[{\"action\":\"disallow\"},{\"action\":\"allow\"}]") << QStringList{} << true;
|
||||
}
|
||||
|
||||
void rulesAllow()
|
||||
{
|
||||
QFETCH(QByteArray, rules);
|
||||
QFETCH(QStringList, features);
|
||||
QFETCH(bool, allowed);
|
||||
rules = Fixtures::applyTemplate(rules);
|
||||
const QSet<QString> set(features.cbegin(), features.cend());
|
||||
QCOMPARE(VersionLoader::rulesAllow(QJsonDocument::fromJson(rules).array(), set), allowed);
|
||||
}
|
||||
|
||||
void platformNames()
|
||||
{
|
||||
const QString os = VersionLoader::osName();
|
||||
QVERIFY(QStringList({"windows", "osx", "linux"}).contains(os));
|
||||
QVERIFY(!VersionLoader::osArch().isEmpty());
|
||||
const QString classifier = VersionLoader::nativeClassifier();
|
||||
QVERIFY(classifier.startsWith(QStringLiteral("natives-")));
|
||||
#if defined(Q_OS_MACOS)
|
||||
QCOMPARE(os, QStringLiteral("osx"));
|
||||
QVERIFY(classifier.startsWith(QStringLiteral("natives-macos")));
|
||||
#elif defined(Q_OS_WIN)
|
||||
QCOMPARE(os, QStringLiteral("windows"));
|
||||
#else
|
||||
QCOMPARE(os, QStringLiteral("linux"));
|
||||
#endif
|
||||
if (QSysInfo::currentCpuArchitecture() == QLatin1String("arm64"))
|
||||
QVERIFY(classifier.endsWith(QStringLiteral("-arm64")));
|
||||
}
|
||||
|
||||
// --- load -------------------------------------------------------------------
|
||||
|
||||
void loadModernVanilla()
|
||||
{
|
||||
writeVersion(m_game, "1.20.1", modernVanilla(), true);
|
||||
Fixtures::write(m_game + "/assets/log_configs/client-1.12.xml", "<xml/>");
|
||||
|
||||
QString error;
|
||||
const MinecraftVersion v = VersionLoader::load(m_game, "1.20.1", {}, &error);
|
||||
QVERIFY2(error.isEmpty(), qPrintable(error));
|
||||
QVERIFY(v.isValid());
|
||||
QCOMPARE(v.id, QStringLiteral("1.20.1"));
|
||||
QCOMPARE(v.mainClass, QStringLiteral("net.minecraft.client.main.Main"));
|
||||
QCOMPARE(v.type, QStringLiteral("release"));
|
||||
QCOMPARE(v.assetIndexId, QStringLiteral("5"));
|
||||
QCOMPARE(v.assetsKind, QStringLiteral("5"));
|
||||
QCOMPARE(v.javaMajor, 17);
|
||||
QCOMPARE(v.clientJarPath, m_game + "/versions/1.20.1/1.20.1.jar");
|
||||
QCOMPARE(v.loggingArgument, QStringLiteral("-Dlog4j.configurationFile=${path}"));
|
||||
QCOMPARE(v.loggingConfigPath, m_game + "/assets/log_configs/client-1.12.xml");
|
||||
QVERIFY(v.supportsQuickPlay);
|
||||
QVERIFY(v.hasCustomResolutionArgs);
|
||||
|
||||
// jvm: правило по ОС, своего -cp у версии есть — лаунчер своё не добавляет.
|
||||
QVERIFY(v.jvmArgs.contains("-Dthis.os=yes"));
|
||||
QVERIFY(!v.jvmArgs.contains("-Dother.os=yes"));
|
||||
QCOMPARE(v.jvmArgs.count("-cp"), 1);
|
||||
QVERIFY(!v.jvmArgs.contains("-Dminecraft.launcher.brand=${launcher_name}"));
|
||||
|
||||
// game: без фич — ни --demo, ни --width, ни quickPlay.
|
||||
QVERIFY(v.gameArgs.contains("--username"));
|
||||
QVERIFY(!v.gameArgs.contains("--demo"));
|
||||
QVERIFY(!v.gameArgs.contains("--width"));
|
||||
QVERIFY(!v.gameArgs.contains("--quickPlayMultiplayer"));
|
||||
|
||||
// Библиотеки: чужая ОС отфильтрована, натив только текущей платформы.
|
||||
QStringList names;
|
||||
for (const MinecraftLibrary &lib : v.libraries)
|
||||
names << lib.name;
|
||||
QVERIFY(names.contains("com.mojang:brigadier:1.1.8"));
|
||||
QVERIFY(names.contains("org.lwjgl:lwjgl:3.3.1"));
|
||||
QVERIFY(names.contains("org.lwjgl:lwjgl:3.3.1:" + VersionLoader::nativeClassifier()));
|
||||
QVERIFY(!names.contains("org.lwjgl:lwjgl:3.3.1:natives-other"));
|
||||
QVERIFY(!names.contains("only.other:lib:1.0"));
|
||||
|
||||
for (const MinecraftLibrary &lib : v.libraries) {
|
||||
if (lib.name == "com.mojang:brigadier:1.1.8") {
|
||||
QCOMPARE(lib.path, QStringLiteral("com/mojang/brigadier/1.1.8/brigadier-1.1.8.jar"));
|
||||
QCOMPARE(lib.sha1, QStringLiteral("5244ce82c3337bba4a196a3ce858bfaecc74404a"));
|
||||
QCOMPARE(lib.size, qint64(77392));
|
||||
QVERIFY(!lib.native);
|
||||
}
|
||||
if (lib.name.endsWith(VersionLoader::nativeClassifier()))
|
||||
QVERIFY(lib.native);
|
||||
}
|
||||
}
|
||||
|
||||
void featuresEnableArguments()
|
||||
{
|
||||
writeVersion(m_game, "1.20.1", modernVanilla(), true);
|
||||
const QSet<QString> features{"has_custom_resolution", "is_quick_play_multiplayer", "is_demo_user"};
|
||||
const MinecraftVersion v = VersionLoader::load(m_game, "1.20.1", features, nullptr);
|
||||
QVERIFY(v.gameArgs.contains("--demo"));
|
||||
const qsizetype width = v.gameArgs.indexOf("--width");
|
||||
QVERIFY(width >= 0);
|
||||
QCOMPARE(v.gameArgs.at(width + 1), QStringLiteral("${resolution_width}"));
|
||||
QVERIFY(v.gameArgs.contains("--quickPlayMultiplayer"));
|
||||
}
|
||||
|
||||
void legacyMinecraftArguments()
|
||||
{
|
||||
writeVersion(m_game, "1.7.10", Fixtures::read(QStringLiteral("versions/legacy-1.7.10.json")), true);
|
||||
|
||||
const MinecraftVersion v = VersionLoader::load(m_game, "1.7.10", {}, nullptr);
|
||||
QVERIFY(v.isValid());
|
||||
QCOMPARE(v.gameArgs,
|
||||
QStringList({"--username", "${auth_player_name}", "--session", "${auth_session}",
|
||||
"--gameDir", "${game_directory}"}));
|
||||
QCOMPARE(v.assetIndexId, QStringLiteral("1.7.10")); // нет assetIndex — берётся assets
|
||||
QCOMPARE(v.type, QStringLiteral("release")); // нет type — release
|
||||
QCOMPARE(v.javaMajor, 8); // нет javaVersion — 8
|
||||
QVERIFY(!v.supportsQuickPlay);
|
||||
|
||||
// Нет -cp в jvm — лаунчер добавляет стандартный набор.
|
||||
QVERIFY(v.jvmArgs.contains("-cp"));
|
||||
QVERIFY(v.jvmArgs.contains("${classpath}"));
|
||||
QVERIFY(v.jvmArgs.contains("-Djava.library.path=${natives_directory}"));
|
||||
QVERIFY(v.jvmArgs.contains("-Dminecraft.launcher.brand=${launcher_name}"));
|
||||
|
||||
bool sawNative = false;
|
||||
for (const MinecraftLibrary &lib : v.libraries) {
|
||||
if (lib.name == "net.minecraft:launchwrapper:1.12") {
|
||||
// Без downloads: путь из координат, url = репозиторий + путь.
|
||||
QCOMPARE(lib.path, QStringLiteral("net/minecraft/launchwrapper/1.12/launchwrapper-1.12.jar"));
|
||||
QCOMPARE(lib.url, QStringLiteral("https://libraries.example/maven/net/minecraft/launchwrapper/1.12/launchwrapper-1.12.jar"));
|
||||
}
|
||||
if (lib.native) {
|
||||
sawNative = true;
|
||||
QCOMPARE(lib.extractExclude, QStringList{"META-INF/"});
|
||||
#if defined(Q_OS_WIN)
|
||||
QVERIFY(lib.path == "lw/natives-windows-64.jar" || lib.path == "lw/natives-windows-32.jar");
|
||||
#elif defined(Q_OS_MACOS)
|
||||
QCOMPARE(lib.path, QStringLiteral("lw/natives-osx.jar"));
|
||||
#else
|
||||
QCOMPARE(lib.path, QStringLiteral("lw/natives-linux.jar"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
QVERIFY(sawNative);
|
||||
}
|
||||
|
||||
void inheritanceMergesChain()
|
||||
{
|
||||
writeVersion(m_game, "1.20.1", modernVanilla(), true);
|
||||
writeVersion(m_game, "fabric-loader-0.15.0-1.20.1", Fixtures::read(QStringLiteral("versions/fabric-loader-0.15.0-1.20.1.json")), false);
|
||||
|
||||
QString error;
|
||||
const MinecraftVersion v = VersionLoader::load(m_game, "fabric-loader-0.15.0-1.20.1", {}, &error);
|
||||
QVERIFY2(error.isEmpty(), qPrintable(error));
|
||||
QCOMPARE(v.id, QStringLiteral("fabric-loader-0.15.0-1.20.1"));
|
||||
QCOMPARE(v.mainClass, QStringLiteral("net.fabricmc.loader.impl.launch.knot.KnotClient"));
|
||||
QCOMPARE(v.javaMajor, 17); // от родителя
|
||||
QCOMPARE(v.assetIndexId, QStringLiteral("5")); // от родителя
|
||||
QCOMPARE(v.clientJarPath, m_game + "/versions/1.20.1/1.20.1.jar"); // своего jar нет
|
||||
QVERIFY(v.jvmArgs.contains("-DFabricMcEmu= net.minecraft.client.main.Main "));
|
||||
QVERIFY(v.jvmArgs.contains("-Dthis.os=yes")); // jvm родителя
|
||||
QVERIFY(v.gameArgs.contains("--username"));
|
||||
|
||||
// Дочерние библиотеки первыми и вытесняют те же group:artifact родителя.
|
||||
QCOMPARE(v.libraries.first().name, QStringLiteral("net.fabricmc:fabric-loader:0.15.0"));
|
||||
QStringList names;
|
||||
for (const MinecraftLibrary &lib : v.libraries)
|
||||
names << lib.name;
|
||||
QVERIFY(names.contains("com.mojang:brigadier:9.9.9"));
|
||||
QVERIFY(!names.contains("com.mojang:brigadier:1.1.8"));
|
||||
QCOMPARE(v.libraries.first().url,
|
||||
QStringLiteral("https://maven.fabricmc.net/net/fabricmc/fabric-loader/0.15.0/fabric-loader-0.15.0.jar"));
|
||||
}
|
||||
|
||||
void childJarWinsOverParent()
|
||||
{
|
||||
writeVersion(m_game, "1.20.1", modernVanilla(), true);
|
||||
writeVersion(m_game, "custom", "{\"id\":\"custom\",\"inheritsFrom\":\"1.20.1\"}", true);
|
||||
const MinecraftVersion v = VersionLoader::load(m_game, "custom", {}, nullptr);
|
||||
QCOMPARE(v.clientJarPath, m_game + "/versions/custom/custom.jar");
|
||||
QCOMPARE(v.mainClass, QStringLiteral("net.minecraft.client.main.Main"));
|
||||
}
|
||||
|
||||
void loadErrors()
|
||||
{
|
||||
QString error;
|
||||
MinecraftVersion v = VersionLoader::load(m_game, "missing", {}, &error);
|
||||
QVERIFY(!v.isValid());
|
||||
QVERIFY(error.contains(QDir::toNativeSeparators(m_game + "/versions/missing/missing.json")));
|
||||
|
||||
writeVersion(m_game, "broken", "{ nope", false);
|
||||
error.clear();
|
||||
v = VersionLoader::load(m_game, "broken", {}, &error);
|
||||
QVERIFY(!v.isValid());
|
||||
QVERIFY(!error.isEmpty());
|
||||
|
||||
writeVersion(m_game, "a", "{\"id\":\"a\",\"inheritsFrom\":\"b\",\"mainClass\":\"M\"}", false);
|
||||
writeVersion(m_game, "b", "{\"id\":\"b\",\"inheritsFrom\":\"a\"}", false);
|
||||
error.clear();
|
||||
v = VersionLoader::load(m_game, "a", {}, &error);
|
||||
QVERIFY(!v.isValid());
|
||||
QCOMPARE(error, Loc::text("version.error.inheritsCycle").arg("a"));
|
||||
|
||||
// Цепочка длиннее 10 уровней.
|
||||
for (int i = 0; i < 12; ++i)
|
||||
writeVersion(m_game, QStringLiteral("v%1").arg(i),
|
||||
QStringLiteral("{\"inheritsFrom\":\"v%1\",\"mainClass\":\"M\"}").arg(i + 1).toUtf8(), false);
|
||||
writeVersion(m_game, "v12", "{\"mainClass\":\"M\"}", false);
|
||||
error.clear();
|
||||
v = VersionLoader::load(m_game, "v0", {}, &error);
|
||||
QVERIFY(!v.isValid());
|
||||
QCOMPARE(error, Loc::text("common.error.inheritsChainTooLong").arg("v0"));
|
||||
|
||||
writeVersion(m_game, "nomain", "{\"id\":\"nomain\"}", false);
|
||||
error.clear();
|
||||
v = VersionLoader::load(m_game, "nomain", {}, &error);
|
||||
QVERIFY(!v.isValid());
|
||||
QCOMPARE(error, Loc::text("version.error.noMainClass").arg("nomain"));
|
||||
}
|
||||
|
||||
// --- инвентарь --------------------------------------------------------------
|
||||
|
||||
void installedDependentsSizeRemove()
|
||||
{
|
||||
writeVersion(m_game, "1.20.1", modernVanilla(), true);
|
||||
writeVersion(m_game, "forge-1", "{\"inheritsFrom\":\"1.20.1\",\"mainClass\":\"F\"}", false);
|
||||
writeVersion(m_game, "fabric-1", "{\"inheritsFrom\":\"1.20.1\",\"mainClass\":\"F\"}", false);
|
||||
writeVersion(m_game, "other", "{\"inheritsFrom\":\"1.19\",\"mainClass\":\"F\"}", false);
|
||||
QDir().mkpath(m_game + "/versions/emptydir"); // без json — не версия
|
||||
|
||||
QCOMPARE(VersionLoader::installedVersions(m_game),
|
||||
QStringList({"1.20.1", "fabric-1", "forge-1", "other"}));
|
||||
QStringList dependents = VersionLoader::dependentsOf(m_game, "1.20.1");
|
||||
dependents.sort();
|
||||
QCOMPARE(dependents, QStringList({"fabric-1", "forge-1"}));
|
||||
QVERIFY(VersionLoader::dependentsOf(m_game, QString()).isEmpty());
|
||||
|
||||
const qint64 expected = QFileInfo(m_game + "/versions/1.20.1/1.20.1.json").size()
|
||||
+ QFileInfo(m_game + "/versions/1.20.1/1.20.1.jar").size();
|
||||
QCOMPARE(VersionLoader::installedSize(m_game, "1.20.1"), expected);
|
||||
QCOMPARE(VersionLoader::installedSize(m_game, "../.."), qint64(0));
|
||||
|
||||
QString error;
|
||||
QVERIFY(VersionLoader::remove(m_game, "forge-1", &error));
|
||||
QVERIFY(!QDir(m_game + "/versions/forge-1").exists());
|
||||
QVERIFY(VersionLoader::remove(m_game, "forge-1", &error)); // уже нет — не ошибка
|
||||
QCOMPARE(VersionLoader::installedVersions(m_game), QStringList({"1.20.1", "fabric-1", "other"}));
|
||||
}
|
||||
|
||||
void removeRejectsUnsafeIds_data()
|
||||
{
|
||||
QTest::addColumn<QString>("id");
|
||||
QTest::newRow("empty") << "";
|
||||
QTest::newRow("dotdot") << "..";
|
||||
QTest::newRow("hidden") << ".git";
|
||||
QTest::newRow("slash") << "a/b";
|
||||
QTest::newRow("backslash") << "a\\b";
|
||||
}
|
||||
|
||||
void removeRejectsUnsafeIds()
|
||||
{
|
||||
QFETCH(QString, id);
|
||||
Fixtures::write(m_game + "/keep.txt", "keep");
|
||||
QString error;
|
||||
QVERIFY(!VersionLoader::remove(m_game, id, &error));
|
||||
QCOMPARE(error, Loc::text("version.error.invalidId"));
|
||||
QVERIFY(QFile::exists(m_game + "/keep.txt"));
|
||||
error.clear();
|
||||
QVERIFY(!VersionLoader::remove(QString(), "1.20.1", &error));
|
||||
}
|
||||
};
|
||||
|
||||
BILAUNCHER_TEST_MAIN(TestMinecraftVersion)
|
||||
#include "tst_minecraftversion.moc"
|
||||
Reference in new issue
Block a user