Files
BiLauncher/tests/unit/tst_javaruntime.cpp
T

233 lines
10 KiB
C++
Raw Normal View History

2026-10-12 13:14:08 +03:00
#include "testenv.h"
#include "fixtures.h"
#include "javaruntime.h"
#include "launcherpaths.h"
#include "localization.h"
#include <QDir>
#include <QFile>
class TestJavaRuntime : public QObject
{
Q_OBJECT
private:
static QString fakeBinary(const QString &root, const QString &relativeBinDir)
{
const QString path = root + u'/' + relativeBinDir + u'/' + Fixtures::javaExecutableName();
Fixtures::write(path, "#!/bin/sh\n");
return path;
}
static InstalledJavaRuntime makeRuntime(const QString &id, int major, const QString &version)
{
InstalledJavaRuntime runtime;
runtime.id = id;
runtime.kind = QStringLiteral("jdk");
runtime.major = major;
runtime.version = version;
runtime.size = 12345;
runtime.javaPath = fakeBinary(JavaRuntimeStore::dirFor(id), QStringLiteral("jdk/bin"));
return runtime;
}
private slots:
void init() { TestEnv::resetLauncherRoot(); }
void kindKeysRoundTrip()
{
for (JavaRuntimeKind kind : {JavaRuntimeKind::Mojang, JavaRuntimeKind::Jdk, JavaRuntimeKind::Jre}) {
const auto back = javaKindFromKey(javaKindKey(kind));
QVERIFY(back.has_value());
QCOMPARE(*back, kind);
QVERIFY(!javaKindTitle(kind).isEmpty());
}
QCOMPARE(javaKindKey(JavaRuntimeKind::Mojang), QStringLiteral("java"));
QVERIFY(!javaKindFromKey(QStringLiteral("graalvm")).has_value());
QVERIFY(!javaKindFromKey(QString()).has_value());
}
void entryValidity()
{
JavaRuntimeEntry entry;
QVERIFY(!entry.isValid());
entry.id = QStringLiteral("temurin-jdk-21");
entry.major = 21;
QVERIFY(!entry.isValid());
entry.url = QUrl(QStringLiteral("https://example.org/jdk.zip"));
QVERIFY(entry.isValid());
}
void sanitizeId_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("expected");
QTest::newRow("plain") << "temurin-jdk-21.0.2_13" << "temurin-jdk-21.0.2_13";
QTest::newRow("plus") << "jdk-21.0.2+13" << "jdk-21.0.2_13";
QTest::newRow("slashes") << "../../etc/passwd" << "_.._etc_passwd";
QTest::newRow("backslash") << "a\\b" << "a_b";
QTest::newRow("leadingDots") << "...hidden" << "hidden";
QTest::newRow("onlyDots") << ".." << "";
QTest::newRow("empty") << "" << "";
}
void sanitizeId()
{
QFETCH(QString, input);
QFETCH(QString, expected);
QCOMPARE(JavaRuntimeStore::sanitizeId(input), expected);
}
void dirForStaysInsideJavaDir()
{
QCOMPARE(JavaRuntimeStore::dirFor(QStringLiteral("jdk-17")),
LauncherPaths::javaDir() + QStringLiteral("/jdk-17"));
QVERIFY(JavaRuntimeStore::dirFor(QStringLiteral("..")).isEmpty());
QVERIFY(JavaRuntimeStore::dirFor(QStringLiteral("../x")).startsWith(LauncherPaths::javaDir() + u'/'));
}
void locateBinaryPrefersShortestBinPath()
{
const QString root = TestEnv::freshDir(QStringLiteral("locate"));
QVERIFY(JavaRuntimeStore::locateBinary(root).isEmpty());
QVERIFY(JavaRuntimeStore::locateBinary(root + QStringLiteral("/missing")).isEmpty());
QVERIFY(JavaRuntimeStore::locateBinary(QString()).isEmpty());
// java вне папки bin не считается
Fixtures::write(root + u'/' + Fixtures::javaExecutableName(), "x");
QVERIFY(JavaRuntimeStore::locateBinary(root).isEmpty());
const QString deep = fakeBinary(root, QStringLiteral("jdk-21/lib/legal/jre/bin"));
QCOMPARE(JavaRuntimeStore::locateBinary(root), deep);
const QString macLayout = fakeBinary(root, QStringLiteral("jdk-21/Contents/Home/bin"));
QCOMPARE(JavaRuntimeStore::locateBinary(root), macLayout);
const QString flat = fakeBinary(root, QStringLiteral("jdk-21/bin"));
QCOMPARE(JavaRuntimeStore::locateBinary(root), flat);
}
void writeMetaFindInstalledRemove()
{
QString error;
const InstalledJavaRuntime older = makeRuntime(QStringLiteral("temurin-jdk-17.0.9"), 17, QStringLiteral("17.0.9"));
const InstalledJavaRuntime newer = makeRuntime(QStringLiteral("temurin-jdk-21.0.2"), 21, QStringLiteral("21.0.2"));
const InstalledJavaRuntime newerPatch = makeRuntime(QStringLiteral("temurin-jdk-21.0.5"), 21, QStringLiteral("21.0.5"));
QVERIFY2(JavaRuntimeStore::writeMeta(older, &error), qPrintable(error));
QVERIFY2(JavaRuntimeStore::writeMeta(newer, &error), qPrintable(error));
QVERIFY2(JavaRuntimeStore::writeMeta(newerPatch, &error), qPrintable(error));
// Путь хранится относительным: папку можно переносить.
const QJsonObject meta = Fixtures::readJsonFile(JavaRuntimeStore::dirFor(newer.id) + "/runtime.json");
QCOMPARE(meta.value("javaPath").toString(), "jdk/bin/" + Fixtures::javaExecutableName());
const auto found = JavaRuntimeStore::find(newer.id);
QVERIFY(found.has_value());
QCOMPARE(found->major, 21);
QCOMPARE(found->version, QStringLiteral("21.0.2"));
QCOMPARE(found->kind, QStringLiteral("jdk"));
QCOMPARE(found->size, qint64(12345));
QCOMPARE(QFileInfo(found->javaPath).canonicalFilePath(), QFileInfo(newer.javaPath).canonicalFilePath());
// Сортировка: свежая major первой, внутри — по версии по убыванию.
const QList<InstalledJavaRuntime> all = JavaRuntimeStore::installed();
QCOMPARE(all.size(), 3);
QCOMPARE(all.at(0).id, newerPatch.id);
QCOMPARE(all.at(1).id, newer.id);
QCOMPARE(all.at(2).id, older.id);
QVERIFY(JavaRuntimeStore::remove(older.id, &error));
QVERIFY(!QDir(JavaRuntimeStore::dirFor(older.id)).exists());
QVERIFY(!JavaRuntimeStore::find(older.id).has_value());
// Удаление несуществующего — не ошибка.
QVERIFY(JavaRuntimeStore::remove(older.id, &error));
QCOMPARE(JavaRuntimeStore::installed().size(), 2);
}
void findRejectsBrokenMeta()
{
const InstalledJavaRuntime runtime = makeRuntime(QStringLiteral("jdk-x"), 21, QStringLiteral("21"));
QVERIFY(JavaRuntimeStore::writeMeta(runtime, nullptr));
QVERIFY(JavaRuntimeStore::find(runtime.id).has_value());
// Бинарник пропал — запись больше не считается установленной.
QFile::remove(runtime.javaPath);
QVERIFY(!JavaRuntimeStore::find(runtime.id).has_value());
// id в файле не совпадает с папкой.
const InstalledJavaRuntime other = makeRuntime(QStringLiteral("jdk-y"), 17, QStringLiteral("17"));
QVERIFY(JavaRuntimeStore::writeMeta(other, nullptr));
QJsonObject meta = Fixtures::readJsonFile(JavaRuntimeStore::dirFor(other.id) + "/runtime.json");
meta["id"] = "jdk-z";
Fixtures::writeJson(JavaRuntimeStore::dirFor(other.id) + "/runtime.json", meta);
QVERIFY(!JavaRuntimeStore::find(other.id).has_value());
// Мусор вместо json.
Fixtures::write(JavaRuntimeStore::dirFor(other.id) + "/runtime.json", "{not json");
QVERIFY(!JavaRuntimeStore::find(other.id).has_value());
QVERIFY(JavaRuntimeStore::installed().isEmpty());
}
void invalidIdsAreRejected()
{
QString error;
InstalledJavaRuntime runtime;
runtime.id = QStringLiteral("..");
QVERIFY(!JavaRuntimeStore::writeMeta(runtime, &error));
QCOMPARE(error, Loc::text("java.error.invalidRuntimeId"));
error.clear();
QVERIFY(!JavaRuntimeStore::remove(QStringLiteral(".."), &error));
QCOMPARE(error, Loc::text("java.error.invalidRuntimeId"));
QVERIFY(!JavaRuntimeStore::find(QString()).has_value());
}
void minimumJavaFor_data()
{
QTest::addColumn<QString>("version");
QTest::addColumn<int>("java");
QTest::newRow("empty") << "" << 0;
QTest::newRow("spaces") << " " << 0;
QTest::newRow("1.7.10") << "1.7.10" << 8;
QTest::newRow("1.12.2") << "1.12.2" << 8;
QTest::newRow("1.16.5") << "1.16.5" << 8;
QTest::newRow("1.17") << "1.17" << 16;
QTest::newRow("1.17.1") << "1.17.1" << 16;
QTest::newRow("1.18") << "1.18" << 17;
QTest::newRow("1.19.4") << "1.19.4" << 17;
QTest::newRow("1.20.1") << "1.20.1" << 17;
QTest::newRow("1.20.4") << "1.20.4" << 17;
QTest::newRow("1.20.5") << "1.20.5" << 21;
QTest::newRow("1.20.6") << "1.20.6" << 21;
QTest::newRow("1.21") << "1.21" << 21;
QTest::newRow("1.21.8") << "1.21.8" << 21;
QTest::newRow("forge-suffix") << "1.20.1-forge-47.4.0" << 17;
QTest::newRow("newEpoch") << "26.1" << 21;
QTest::newRow("snapshot20") << "20w45a" << 8;
QTest::newRow("snapshot21") << "21w19a" << 16;
QTest::newRow("snapshot22") << "22w45a" << 17;
QTest::newRow("snapshot24") << "24w14a" << 21;
QTest::newRow("snapshot25") << "25w33a" << 21;
QTest::newRow("beta") << "b1.7.3" << 8;
QTest::newRow("trimmed") << " 1.18.2 " << 17;
}
void minimumJavaFor()
{
QFETCH(QString, version);
QFETCH(int, java);
QCOMPARE(JavaRequirement::minimumFor(version), java);
}
void coverage()
{
QCOMPARE(JavaRequirement::coverage(25), Loc::text("java.range.modern"));
QCOMPARE(JavaRequirement::coverage(21), Loc::text("java.range.modern"));
QCOMPARE(JavaRequirement::coverage(17), Loc::text("java.range.mid"));
QCOMPARE(JavaRequirement::coverage(16), Loc::text("java.range.seventeen"));
QCOMPARE(JavaRequirement::coverage(11), Loc::text("java.range.unsuitable"));
QCOMPARE(JavaRequirement::coverage(8), Loc::text("java.range.legacy"));
QVERIFY(JavaRequirement::coverage(0).isEmpty());
}
};
BILAUNCHER_TEST_MAIN(TestJavaRuntime)
#include "tst_javaruntime.moc"