#include "testenv.h" #include "fixtures.h" #include "javaruntime.h" #include "launcherpaths.h" #include "localization.h" #include #include 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("input"); QTest::addColumn("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 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("version"); QTest::addColumn("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"