450 lines
20 KiB
C++
450 lines
20 KiB
C++
#include "testenv.h"
|
||||
|
|
#include "fixtures.h"
|
|||
|
|
|
|||
|
|
#include "gamelauncher.h"
|
|||
|
|
#include "javalocator.h"
|
|||
|
|
#include "localization.h"
|
|||
|
|
#include "minecraftversion.h"
|
|||
|
|
|
|||
|
|
#include <QDir>
|
|||
|
|
#include <QSignalSpy>
|
|||
|
|
|
|||
|
|
namespace {
|
|||
|
|
|
|||
|
|
#if defined(Q_OS_WIN)
|
|||
|
|
const QChar kSep = u';';
|
|||
|
|
const char *kNativeName = "lwjgl.dll";
|
|||
|
|
#elif defined(Q_OS_MACOS)
|
|||
|
|
const QChar kSep = u':';
|
|||
|
|
const char *kNativeName = "liblwjgl.dylib";
|
|||
|
|
#else
|
|||
|
|
const QChar kSep = u':';
|
|||
|
|
const char *kNativeName = "liblwjgl.so";
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
QString nat(const QString &path)
|
|||
|
|
{
|
|||
|
|
return QDir::toNativeSeparators(path);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MinecraftLibrary lib(const QString &name, const QString &path, bool native = false)
|
|||
|
|
{
|
|||
|
|
MinecraftLibrary library;
|
|||
|
|
library.name = name;
|
|||
|
|
library.path = path;
|
|||
|
|
library.native = native;
|
|||
|
|
return library;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} // namespace
|
|||
|
|
|
|||
|
|
class TestGameLauncher : public QObject
|
|||
|
|
{
|
|||
|
|
Q_OBJECT
|
|||
|
|
|
|||
|
|
QString m_game;
|
|||
|
|
|
|||
|
|
MinecraftVersion modernVersion() const
|
|||
|
|
{
|
|||
|
|
MinecraftVersion v;
|
|||
|
|
v.id = QStringLiteral("1.20.1");
|
|||
|
|
v.mainClass = QStringLiteral("net.minecraft.client.main.Main");
|
|||
|
|
v.type = QStringLiteral("release");
|
|||
|
|
v.assetIndexId = QStringLiteral("5");
|
|||
|
|
v.assetsKind = QStringLiteral("5");
|
|||
|
|
v.clientJarPath = m_game + QStringLiteral("/versions/1.20.1/1.20.1.jar");
|
|||
|
|
v.javaMajor = 17;
|
|||
|
|
v.jvmArgs = QStringList{"-Djava.library.path=${natives_directory}",
|
|||
|
|
"-Dminecraft.launcher.brand=${launcher_name}",
|
|||
|
|
"-Dminecraft.launcher.version=${launcher_version}",
|
|||
|
|
"-Dunknown=${not_a_variable}",
|
|||
|
|
"-cp", "${classpath}"};
|
|||
|
|
v.gameArgs = QStringList{"--username", "${auth_player_name}", "--version", "${version_name}",
|
|||
|
|
"--gameDir", "${game_directory}", "--assetsDir", "${assets_root}",
|
|||
|
|
"--assetIndex", "${assets_index_name}", "--uuid", "${auth_uuid}",
|
|||
|
|
"--accessToken", "${auth_access_token}", "--clientId", "${clientid}",
|
|||
|
|
"--xuid", "${auth_xuid}", "--userType", "${user_type}",
|
|||
|
|
"--versionType", "${version_type}", "--mystery", "${unknown_thing}"};
|
|||
|
|
v.libraries << lib("com.mojang:brigadier:1.1.8", "com/mojang/brigadier-1.1.8.jar")
|
|||
|
|
<< lib("org.lwjgl:lwjgl:3.3.1", "org/lwjgl/lwjgl-3.3.1.jar")
|
|||
|
|
<< lib("org.lwjgl:lwjgl:3.3.1:natives", "org/lwjgl/lwjgl-3.3.1-natives.jar", true);
|
|||
|
|
return v;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
LaunchOptions options() const
|
|||
|
|
{
|
|||
|
|
LaunchOptions o;
|
|||
|
|
o.gameDir = m_game;
|
|||
|
|
o.versionId = QStringLiteral("1.20.1");
|
|||
|
|
o.playerName = QStringLiteral("Steve");
|
|||
|
|
o.uuid = QStringLiteral("0123456789abcdef0123456789abcdef");
|
|||
|
|
o.accessToken = QStringLiteral("secret-token");
|
|||
|
|
o.userType = QStringLiteral("msa");
|
|||
|
|
o.minMemoryMb = 1024;
|
|||
|
|
o.maxMemoryMb = 6144;
|
|||
|
|
return o;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static QString valueAfter(const QStringList &args, const QString &flag)
|
|||
|
|
{
|
|||
|
|
const qsizetype i = args.indexOf(flag);
|
|||
|
|
return (i >= 0 && i + 1 < args.size()) ? args.at(i + 1) : QString();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void createGameFiles(const MinecraftVersion &v)
|
|||
|
|
{
|
|||
|
|
Fixtures::write(v.clientJarPath, "client");
|
|||
|
|
Fixtures::write(m_game + "/libraries/com/mojang/brigadier-1.1.8.jar", "lib");
|
|||
|
|
Fixtures::write(m_game + "/libraries/org/lwjgl/lwjgl-3.3.1.jar", "lib");
|
|||
|
|
Fixtures::makeZip(m_game + "/libraries/org/lwjgl/lwjgl-3.3.1-natives.jar",
|
|||
|
|
{{QStringLiteral("META-INF/MANIFEST.MF"), "x"},
|
|||
|
|
{QStringLiteral("linux/x64/org/lwjgl/") + kNativeName, "native-bytes"},
|
|||
|
|
{QStringLiteral("skip/") + kNativeName + ".sha1", "hash"}});
|
|||
|
|
Fixtures::write(m_game + "/assets/indexes/5.json", "{}");
|
|||
|
|
QDir().mkpath(m_game + "/assets/objects");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private slots:
|
|||
|
|
void init()
|
|||
|
|
{
|
|||
|
|
TestEnv::resetLauncherRoot();
|
|||
|
|
m_game = TestEnv::freshDir(QStringLiteral("game"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- buildArguments ---------------------------------------------------------
|
|||
|
|
|
|||
|
|
void substitutesVariables()
|
|||
|
|
{
|
|||
|
|
const MinecraftVersion v = modernVersion();
|
|||
|
|
const QString natives = m_game + "/versions/1.20.1/natives";
|
|||
|
|
const QStringList args = GameLauncher::buildArguments(options(), v, natives);
|
|||
|
|
|
|||
|
|
QCOMPARE(args.at(0), QStringLiteral("-Xms1024M"));
|
|||
|
|
QCOMPARE(args.at(1), QStringLiteral("-Xmx6144M"));
|
|||
|
|
QVERIFY(args.contains("-Djava.library.path=" + nat(natives)));
|
|||
|
|
QVERIFY(args.contains("-Dminecraft.launcher.brand=BiLauncher"));
|
|||
|
|
QVERIFY(args.contains(QStringLiteral("-Dminecraft.launcher.version=") + QStringLiteral(LAUNCHER_VERSION)));
|
|||
|
|
// Неизвестный плейсхолдер — аргумент выбрасывается целиком.
|
|||
|
|
QVERIFY(!args.join(' ').contains("not_a_variable"));
|
|||
|
|
QVERIFY(!args.contains("${unknown_thing}"));
|
|||
|
|
QVERIFY(args.contains("--mystery")); // сам флаг остаётся, как у официального лаунчера
|
|||
|
|
|
|||
|
|
// classpath: обычные библиотеки, без нативов, клиентский jar последним.
|
|||
|
|
const QStringList cp = valueAfter(args, "-cp").split(kSep);
|
|||
|
|
QCOMPARE(cp, QStringList({nat(m_game + "/libraries/com/mojang/brigadier-1.1.8.jar"),
|
|||
|
|
nat(m_game + "/libraries/org/lwjgl/lwjgl-3.3.1.jar"),
|
|||
|
|
nat(v.clientJarPath)}));
|
|||
|
|
|
|||
|
|
// mainClass — после jvm-аргументов, перед игровыми.
|
|||
|
|
const qsizetype main = args.indexOf(v.mainClass);
|
|||
|
|
QVERIFY(main > args.indexOf("-cp"));
|
|||
|
|
QVERIFY(main < args.indexOf("--username"));
|
|||
|
|
|
|||
|
|
QCOMPARE(valueAfter(args, "--username"), QStringLiteral("Steve"));
|
|||
|
|
QCOMPARE(valueAfter(args, "--version"), QStringLiteral("1.20.1"));
|
|||
|
|
QCOMPARE(valueAfter(args, "--gameDir"), nat(m_game));
|
|||
|
|
QCOMPARE(valueAfter(args, "--assetsDir"), nat(m_game + "/assets"));
|
|||
|
|
QCOMPARE(valueAfter(args, "--assetIndex"), QStringLiteral("5"));
|
|||
|
|
QCOMPARE(valueAfter(args, "--uuid"), QStringLiteral("0123456789abcdef0123456789abcdef"));
|
|||
|
|
QCOMPARE(valueAfter(args, "--accessToken"), QStringLiteral("secret-token"));
|
|||
|
|
QCOMPARE(valueAfter(args, "--clientId"), QStringLiteral("0")); // пустой → «0»
|
|||
|
|
QCOMPARE(valueAfter(args, "--xuid"), QStringLiteral("0"));
|
|||
|
|
QCOMPARE(valueAfter(args, "--userType"), QStringLiteral("msa"));
|
|||
|
|
QCOMPARE(valueAfter(args, "--versionType"), QStringLiteral("release"));
|
|||
|
|
QVERIFY(!args.contains("--width"));
|
|||
|
|
QVERIFY(!args.contains("--fullscreen"));
|
|||
|
|
QVERIFY(!args.contains("--server"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void legacyAssetsAndSession()
|
|||
|
|
{
|
|||
|
|
MinecraftVersion v = modernVersion();
|
|||
|
|
v.assetsKind = QStringLiteral("legacy");
|
|||
|
|
v.gameArgs = QStringList{"--session", "${auth_session}", "--assetsDir", "${game_assets}"};
|
|||
|
|
const QStringList args = GameLauncher::buildArguments(options(), v, m_game + "/n");
|
|||
|
|
QCOMPARE(valueAfter(args, "--session"),
|
|||
|
|
QStringLiteral("token:secret-token:0123456789abcdef0123456789abcdef"));
|
|||
|
|
QCOMPARE(valueAfter(args, "--assetsDir"), nat(m_game + "/assets/virtual/legacy"));
|
|||
|
|
|
|||
|
|
v.assetsKind = QStringLiteral("pre-1.6");
|
|||
|
|
QCOMPARE(valueAfter(GameLauncher::buildArguments(options(), v, m_game + "/n"), "--assetsDir"),
|
|||
|
|
nat(m_game + "/assets/virtual/legacy"));
|
|||
|
|
v.assetsKind = QStringLiteral("5");
|
|||
|
|
QCOMPARE(valueAfter(GameLauncher::buildArguments(options(), v, m_game + "/n"), "--assetsDir"),
|
|||
|
|
nat(m_game + "/assets"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void memoryAndExtraJvmArgs()
|
|||
|
|
{
|
|||
|
|
LaunchOptions o = options();
|
|||
|
|
o.minMemoryMb = 0;
|
|||
|
|
o.maxMemoryMb = 0;
|
|||
|
|
o.extraJvmArgs = QStringList{"-XX:+UseG1GC", "-Dfoo=bar"};
|
|||
|
|
const QStringList args = GameLauncher::buildArguments(o, modernVersion(), m_game + "/n");
|
|||
|
|
QVERIFY(!args.join(' ').contains("-Xms"));
|
|||
|
|
QVERIFY(!args.join(' ').contains("-Xmx"));
|
|||
|
|
QCOMPARE(args.at(0), QStringLiteral("-XX:+UseG1GC"));
|
|||
|
|
QCOMPARE(args.at(1), QStringLiteral("-Dfoo=bar"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void authlibInjectorGoesFirst()
|
|||
|
|
{
|
|||
|
|
LaunchOptions o = options();
|
|||
|
|
o.authlibInjectorPath = m_game + "/authlib-injector.jar";
|
|||
|
|
const QStringList args = GameLauncher::buildArguments(o, modernVersion(), m_game + "/n");
|
|||
|
|
QCOMPARE(args.at(0), "-javaagent:" + nat(o.authlibInjectorPath) + "=ely.by");
|
|||
|
|
QCOMPARE(args.at(1), QStringLiteral("-Dauthlibinjector.side=client"));
|
|||
|
|
QCOMPARE(args.at(2), QStringLiteral("-Xms1024M"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void windowSizeAndFullscreen()
|
|||
|
|
{
|
|||
|
|
LaunchOptions o = options();
|
|||
|
|
o.windowWidth = 1280;
|
|||
|
|
o.windowHeight = 720;
|
|||
|
|
o.fullscreen = true;
|
|||
|
|
MinecraftVersion v = modernVersion();
|
|||
|
|
QStringList args = GameLauncher::buildArguments(o, v, m_game + "/n");
|
|||
|
|
QCOMPARE(valueAfter(args, "--width"), QStringLiteral("1280"));
|
|||
|
|
QCOMPARE(valueAfter(args, "--height"), QStringLiteral("720"));
|
|||
|
|
QCOMPARE(args.count("--fullscreen"), 1);
|
|||
|
|
|
|||
|
|
// У версии свои аргументы разрешения — лаунчер свои не дублирует.
|
|||
|
|
v.hasCustomResolutionArgs = true;
|
|||
|
|
v.gameArgs << "--width" << "${resolution_width}" << "--height" << "${resolution_height}";
|
|||
|
|
args = GameLauncher::buildArguments(o, v, m_game + "/n");
|
|||
|
|
QCOMPARE(args.count("--width"), 1);
|
|||
|
|
QCOMPARE(valueAfter(args, "--width"), QStringLiteral("1280"));
|
|||
|
|
|
|||
|
|
// Только ширина — не передаём ничего.
|
|||
|
|
o.windowHeight = 0;
|
|||
|
|
o.fullscreen = false;
|
|||
|
|
args = GameLauncher::buildArguments(o, modernVersion(), m_game + "/n");
|
|||
|
|
QVERIFY(!args.contains("--width"));
|
|||
|
|
QVERIFY(!args.contains("--fullscreen"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void serverAutoJoin_data()
|
|||
|
|
{
|
|||
|
|
QTest::addColumn<bool>("quickPlay");
|
|||
|
|
QTest::addColumn<QString>("address");
|
|||
|
|
QTest::addColumn<QStringList>("expectedTail");
|
|||
|
|
|
|||
|
|
QTest::newRow("quickplay") << true << "play.example.com:25570"
|
|||
|
|
<< QStringList{"--quickPlayMultiplayer", "play.example.com:25570"};
|
|||
|
|
QTest::newRow("legacy with port") << false << "play.example.com:25570"
|
|||
|
|
<< QStringList{"--server", "play.example.com", "--port", "25570"};
|
|||
|
|
QTest::newRow("legacy default port") << false << "play.example.com"
|
|||
|
|
<< QStringList{"--server", "play.example.com", "--port", "25565"};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void serverAutoJoin()
|
|||
|
|
{
|
|||
|
|
QFETCH(bool, quickPlay);
|
|||
|
|
QFETCH(QString, address);
|
|||
|
|
QFETCH(QStringList, expectedTail);
|
|||
|
|
LaunchOptions o = options();
|
|||
|
|
o.serverAddress = address;
|
|||
|
|
MinecraftVersion v = modernVersion();
|
|||
|
|
v.supportsQuickPlay = quickPlay;
|
|||
|
|
const QStringList args = GameLauncher::buildArguments(o, v, m_game + "/n");
|
|||
|
|
QCOMPARE(args.mid(args.size() - expectedTail.size()), expectedTail);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void forgeIgnoreListGetsClientJar()
|
|||
|
|
{
|
|||
|
|
MinecraftVersion v = modernVersion();
|
|||
|
|
v.jvmArgs.prepend("-DignoreList=bootstraplauncher,securejarhandler,${version_name}.jar");
|
|||
|
|
QStringList args = GameLauncher::buildArguments(options(), v, m_game + "/n");
|
|||
|
|
// ${version_name} = 1.20.1 совпадает с именем jar — дописывать нечего.
|
|||
|
|
QCOMPARE(args.filter("-DignoreList=").constFirst(),
|
|||
|
|
QStringLiteral("-DignoreList=bootstraplauncher,securejarhandler,1.20.1.jar"));
|
|||
|
|
|
|||
|
|
// Профиль Forge со своим id, а jar родительский — имя jar дописывается.
|
|||
|
|
v.id = QStringLiteral("1.20.1-forge-47.4.0");
|
|||
|
|
args = GameLauncher::buildArguments(options(), v, m_game + "/n");
|
|||
|
|
QCOMPARE(args.filter("-DignoreList=").constFirst(),
|
|||
|
|
QStringLiteral("-DignoreList=bootstraplauncher,securejarhandler,1.20.1-forge-47.4.0.jar,1.20.1.jar"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void loggingArgumentOnlyWhenConfigExists()
|
|||
|
|
{
|
|||
|
|
MinecraftVersion v = modernVersion();
|
|||
|
|
v.loggingArgument = QStringLiteral("-Dlog4j.configurationFile=${path}");
|
|||
|
|
v.loggingConfigPath = m_game + "/assets/log_configs/client-1.12.xml";
|
|||
|
|
QVERIFY(GameLauncher::buildArguments(options(), v, m_game + "/n").filter("log4j").isEmpty());
|
|||
|
|
|
|||
|
|
Fixtures::write(v.loggingConfigPath, "<xml/>");
|
|||
|
|
const QStringList args = GameLauncher::buildArguments(options(), v, m_game + "/n");
|
|||
|
|
QCOMPARE(args.filter("log4j"), QStringList{"-Dlog4j.configurationFile=" + nat(v.loggingConfigPath)});
|
|||
|
|
QVERIFY(args.indexOf(args.filter("log4j").constFirst()) < args.indexOf(v.mainClass));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- missingFiles / extractNatives -------------------------------------------
|
|||
|
|
|
|||
|
|
void missingFiles()
|
|||
|
|
{
|
|||
|
|
const MinecraftVersion v = modernVersion();
|
|||
|
|
LaunchOptions o = options();
|
|||
|
|
|
|||
|
|
o.gameDir = m_game + "/does-not-exist";
|
|||
|
|
QCOMPARE(GameLauncher::missingFiles(o, v), QStringList{nat(o.gameDir)});
|
|||
|
|
|
|||
|
|
o = options();
|
|||
|
|
QStringList missing = GameLauncher::missingFiles(o, v);
|
|||
|
|
QVERIFY(missing.contains(nat(v.clientJarPath)));
|
|||
|
|
QVERIFY(missing.contains(nat(m_game + "/libraries/com/mojang/brigadier-1.1.8.jar")));
|
|||
|
|
QVERIFY(missing.contains(nat(m_game + "/assets/indexes/5.json")));
|
|||
|
|
QCOMPARE(GameLauncher::missingFiles(o, v, 2).size(), 2);
|
|||
|
|
|
|||
|
|
createGameFiles(v);
|
|||
|
|
QVERIFY(GameLauncher::missingFiles(o, v).isEmpty());
|
|||
|
|
|
|||
|
|
QDir(m_game + "/assets/objects").removeRecursively();
|
|||
|
|
QCOMPARE(GameLauncher::missingFiles(o, v), QStringList{nat(m_game + "/assets/objects")});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void extractNatives()
|
|||
|
|
{
|
|||
|
|
MinecraftVersion v = modernVersion();
|
|||
|
|
createGameFiles(v);
|
|||
|
|
const QString natives = m_game + "/versions/1.20.1/natives";
|
|||
|
|
|
|||
|
|
QString error;
|
|||
|
|
QVERIFY2(GameLauncher::extractNatives(options(), v, natives, &error), qPrintable(error));
|
|||
|
|
// Только сама библиотека: без META-INF и без .sha1, в плоскую папку.
|
|||
|
|
QCOMPARE(Fixtures::listTree(natives), QStringList{QString::fromLatin1(kNativeName)});
|
|||
|
|
QCOMPARE(Fixtures::readFile(natives + u'/' + kNativeName), QByteArray("native-bytes"));
|
|||
|
|
|
|||
|
|
// Повторный запуск поверх готовой папки — успех.
|
|||
|
|
QVERIFY(GameLauncher::extractNatives(options(), v, natives, &error));
|
|||
|
|
|
|||
|
|
// extractExclude по префиксу пути внутри архива.
|
|||
|
|
QDir(natives).removeRecursively();
|
|||
|
|
v.libraries[2].extractExclude = QStringList{"linux/"};
|
|||
|
|
QVERIFY(GameLauncher::extractNatives(options(), v, natives, &error));
|
|||
|
|
QVERIFY(Fixtures::listTree(natives).isEmpty());
|
|||
|
|
|
|||
|
|
// Архива нет вовсе.
|
|||
|
|
const QString jar = m_game + "/libraries/org/lwjgl/lwjgl-3.3.1-natives.jar";
|
|||
|
|
QFile::remove(jar);
|
|||
|
|
error.clear();
|
|||
|
|
QVERIFY(!GameLauncher::extractNatives(options(), v, natives, &error));
|
|||
|
|
QCOMPARE(error, Loc::text("launch.error.nativeReadFailed").arg(nat(jar)));
|
|||
|
|
|
|||
|
|
// Битый архив: QZipReader::isReadable() проверяет только, что файл открылся,
|
|||
|
|
// поэтому испорченный jar «распаковывается» в ничто без ошибки, и игра
|
|||
|
|
// падает уже на загрузке LWJGL. Фиксируем текущее поведение.
|
|||
|
|
Fixtures::write(jar, "not a zip");
|
|||
|
|
error.clear();
|
|||
|
|
QEXPECT_FAIL("", "битый jar с нативами не распознаётся как ошибка (QZipReader::isReadable)", Continue);
|
|||
|
|
QVERIFY(!GameLauncher::extractNatives(options(), v, natives, &error));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- launch (с подставной java) ----------------------------------------------
|
|||
|
|
|
|||
|
|
void launchRunsJavaAndReportsExit()
|
|||
|
|
{
|
|||
|
|
MinecraftVersion v = modernVersion();
|
|||
|
|
createGameFiles(v);
|
|||
|
|
const QString argsFile = m_game + "/java-args.txt";
|
|||
|
|
const QString java = Fixtures::installFakeJava(m_game + "/jdk", "17.0.8",
|
|||
|
|
{"args_file=" + argsFile, "stdout=[main] Setting user: Steve",
|
|||
|
|
"stdout=Game crashed? no", "exit=0"});
|
|||
|
|
LaunchOptions o = options();
|
|||
|
|
o.javaPath = java;
|
|||
|
|
|
|||
|
|
GameLauncher launcher;
|
|||
|
|
QSignalSpy started(&launcher, &GameLauncher::gameStarted);
|
|||
|
|
QSignalSpy finished(&launcher, &GameLauncher::gameFinished);
|
|||
|
|
QSignalSpy output(&launcher, &GameLauncher::output);
|
|||
|
|
QSignalSpy progress(&launcher, &GameLauncher::progress);
|
|||
|
|
|
|||
|
|
QString error;
|
|||
|
|
QVERIFY2(launcher.launch(o, v, &error), qPrintable(error));
|
|||
|
|
QCOMPARE(started.count(), 1);
|
|||
|
|
// Токен скрыт в показанной строке запуска.
|
|||
|
|
const QString shown = started.constFirst().constFirst().toString();
|
|||
|
|
QVERIFY(!shown.contains("secret-token"));
|
|||
|
|
QVERIFY(shown.contains("--accessToken"));
|
|||
|
|
|
|||
|
|
QTRY_COMPARE_WITH_TIMEOUT(finished.count(), 1, 15000);
|
|||
|
|
QCOMPARE(finished.constFirst().at(0).toInt(), 0);
|
|||
|
|
QCOMPARE(finished.constFirst().at(1).toBool(), false);
|
|||
|
|
QVERIFY(!launcher.isRunning());
|
|||
|
|
QVERIFY(progress.count() >= 3);
|
|||
|
|
|
|||
|
|
QStringList lines;
|
|||
|
|
for (const QList<QVariant> &call : output)
|
|||
|
|
lines << call.constFirst().toString();
|
|||
|
|
QVERIFY(lines.contains("[main] Setting user: Steve"));
|
|||
|
|
|
|||
|
|
// Java получила те же аргументы, что строит buildArguments, и в рабочей папке игры.
|
|||
|
|
const QStringList received = QString::fromLocal8Bit(Fixtures::readFile(argsFile)).split(u'\n', Qt::SkipEmptyParts);
|
|||
|
|
QCOMPARE(received, GameLauncher::buildArguments(o, v, m_game + "/versions/1.20.1/natives"));
|
|||
|
|
QVERIFY(QFile::exists(m_game + "/versions/1.20.1/natives/" + kNativeName));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void launchReportsCrash()
|
|||
|
|
{
|
|||
|
|
MinecraftVersion v = modernVersion();
|
|||
|
|
createGameFiles(v);
|
|||
|
|
LaunchOptions o = options();
|
|||
|
|
o.javaPath = Fixtures::installFakeJava(m_game + "/jdk-crash", "17.0.8", {"crash=1"});
|
|||
|
|
|
|||
|
|
GameLauncher launcher;
|
|||
|
|
QSignalSpy finished(&launcher, &GameLauncher::gameFinished);
|
|||
|
|
QString error;
|
|||
|
|
QVERIFY2(launcher.launch(o, v, &error), qPrintable(error));
|
|||
|
|
QTRY_COMPARE_WITH_TIMEOUT(finished.count(), 1, 15000);
|
|||
|
|
QCOMPARE(finished.constFirst().at(1).toBool(), true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void launchRefusesWhileRunningAndTerminate()
|
|||
|
|
{
|
|||
|
|
MinecraftVersion v = modernVersion();
|
|||
|
|
createGameFiles(v);
|
|||
|
|
LaunchOptions o = options();
|
|||
|
|
o.javaPath = Fixtures::installFakeJava(m_game + "/jdk-slow", "17.0.8", {"sleep_ms=30000"});
|
|||
|
|
|
|||
|
|
GameLauncher launcher;
|
|||
|
|
QSignalSpy finished(&launcher, &GameLauncher::gameFinished);
|
|||
|
|
QString error;
|
|||
|
|
QVERIFY(launcher.launch(o, v, &error));
|
|||
|
|
QVERIFY(launcher.isRunning());
|
|||
|
|
|
|||
|
|
QVERIFY(!launcher.launch(o, v, &error));
|
|||
|
|
QCOMPARE(error, Loc::text("common.error.gameRunning"));
|
|||
|
|
|
|||
|
|
launcher.terminate();
|
|||
|
|
QTRY_COMPARE_WITH_TIMEOUT(finished.count(), 1, 15000);
|
|||
|
|
QVERIFY(!launcher.isRunning());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void launchFailsOnMissingFilesAndOldJava()
|
|||
|
|
{
|
|||
|
|
MinecraftVersion v = modernVersion();
|
|||
|
|
LaunchOptions o = options();
|
|||
|
|
GameLauncher launcher;
|
|||
|
|
QSignalSpy started(&launcher, &GameLauncher::gameStarted);
|
|||
|
|
|
|||
|
|
QString error;
|
|||
|
|
QVERIFY(!launcher.launch(o, v, &error));
|
|||
|
|
QVERIFY(error.startsWith(Loc::text("launch.error.filesMissing").section(u'%', 0, 0)));
|
|||
|
|
|
|||
|
|
createGameFiles(v);
|
|||
|
|
// На машине может стоять своя Java 17+: тогда лаунчер законно выберет её.
|
|||
|
|
for (const QString &candidate : JavaLocator::findAll(m_game)) {
|
|||
|
|
if (JavaLocator::majorVersion(candidate) >= 17)
|
|||
|
|
QSKIP("в системе есть Java 17+, проверка «слишком старой Java» невозможна");
|
|||
|
|
}
|
|||
|
|
o.javaPath = Fixtures::installFakeJava(m_game + "/jdk8", "1.8.0_392");
|
|||
|
|
error.clear();
|
|||
|
|
QVERIFY(!launcher.launch(o, v, &error));
|
|||
|
|
QCOMPARE(error, Loc::text("java.error.tooOld").arg(17).arg(8).arg(nat(o.javaPath)));
|
|||
|
|
QCOMPARE(started.count(), 0);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
BILAUNCHER_TEST_MAIN(TestGameLauncher)
|
|||
|
|
#include "tst_gamelauncher.moc"
|