/* This file is part of the KDE libraries SPDX-FileCopyrightText: 2010 Canonical Ltd SPDX-FileContributor: Aurélien Gâteau SPDX-License-Identifier: LGPL-2.0-or-later */ #include "test_kconf_update.h" #include // Qt #include "config-kconf.h" #include #include #include #include #include #include QTEST_GUILESS_MAIN(TestKConfUpdate) void TestKConfUpdate::initTestCase() { QStandardPaths::setTestModeEnabled(true); // Ensure it all works with spaces in paths (as happens more commonly on OSX where it's ~/Library/Application Support/) qputenv("XDG_DATA_HOME", "/tmp/a b"); } static void writeFile(const QString &path, const QString &content) { QFile file(path); if (!file.open(QIODevice::WriteOnly)) { qFatal("Could not write to '%s'", qPrintable(path)); } file.write(content.toUtf8()); } static QString readFile(const QString &path) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { qFatal("Could not read '%s'", qPrintable(path)); } QString ret = QString::fromUtf8(file.readAll()); #ifdef Q_OS_WIN // KConfig always writes files with the native line ending, the test comparison uses \n ret.replace(QLatin1String{"\r\n"}, QLatin1String{"\n"}); #endif return ret; } static std::unique_ptr writeUpdFile(const QString &content) { std::unique_ptr file{new QTemporaryFile(QDir::tempPath() + QLatin1String("/test_kconf_update_XXXXXX.upd"))}; bool ok = file->open(); Q_UNUSED(ok) // silence warnings Q_ASSERT(ok); file->write(content.toUtf8()); file->flush(); return file; } static void runKConfUpdate(const QString &updPath) { QVERIFY(QFile::exists(QStringLiteral(KCONF_UPDATE_EXECUTABLE))); QCOMPARE(0, QProcess::execute(QStringLiteral(KCONF_UPDATE_EXECUTABLE), QStringList{QStringLiteral("--testmode"), QStringLiteral("--debug"), updPath})); } void TestKConfUpdate::test_data() { QTest::addColumn("updContent"); QTest::addColumn("oldConfName"); QTest::addColumn("oldConfContent"); QTest::addColumn("newConfName"); QTest::addColumn("expectedNewConfContent"); QTest::addColumn("expectedOldConfContent"); QTest::addColumn("useVersion5"); QTest::addColumn("shouldUpdateWork"); QTest::newRow("moveKeysSameFile") << "File=testrc\n" "Group=group\n" "Key=old,new\n" "Options=overwrite\n" << "testrc" << "[group]\n" "old=value\n" << "testrc" << "[$Version]\n" "update_info=%1\n" "\n" "[group]\n" "new=value\n" << "" << true << true; QTest::newRow("moveKeysOtherFile") << "File=oldrc,newrc\n" "Group=group1,group2\n" "Key=old,new\n" "Options=overwrite\n" << "oldrc" << "[group1]\n" "old=value\n" "[stay]\n" "foo=bar\n" << "newrc" << "[$Version]\n" "update_info=%1\n" "\n" "[group2]\n" "new=value\n" << "[$Version]\n" "update_info=%1\n" "\n" "[stay]\n" "foo=bar\n" << true << true; QTest::newRow("allKeys") << "File=testrc\n" "Group=group1,group2\n" "AllKeys\n" << "testrc" << "[group1]\n" "key1=value1\n" "key2=value2\n" "\n" "[stay]\n" "foo=bar\n" << "testrc" << "[$Version]\n" "update_info=%1\n" "\n" "[group2]\n" "key1=value1\n" "key2=value2\n" "\n" "[stay]\n" "foo=bar\n" << "" << true << true; QTest::newRow("allKeysSubGroup") << "File=testrc\n" "Group=[group][sub1],[group][sub2]\n" "AllKeys\n" << "testrc" << "[group][sub1]\n" "key1=value1\n" "key2=value2\n" "\n" "[group][sub1][subsub]\n" "key3=value3\n" "key4=value4\n" "\n" "[stay]\n" "foo=bar\n" << "testrc" << "[$Version]\n" "update_info=%1\n" "\n" "[group][sub2]\n" "key1=value1\n" "key2=value2\n" "\n" "[group][sub2][subsub]\n" "key3=value3\n" "key4=value4\n" "\n" "[stay]\n" "foo=bar\n" << "" << true << true; QTest::newRow("removeGroup") << "File=testrc\n" "RemoveGroup=remove\n" << "testrc" << "[keep]\n" "key=value\n" "" "[remove]\n" "key=value\n" << "testrc" << "[$Version]\n" "update_info=%1\n" "\n" "[keep]\n" "key=value\n" << "" << true << true; QTest::newRow("moveKeysSameFileDontExist") << "File=testrc\n" "Group=group,group2\n" "Key=key1\n" "Key=key2\n" << "testrc" << "[group]\n" "key1=value1\n" "key3=value3\n" << "testrc" << "[$Version]\n" "update_info=%1\n" "\n" "[group]\n" "key3=value3\n" "\n" "[group2]\n" "key1=value1\n" << "" << true << true; QTest::newRow("DontMigrateWhenFileDoesntHaveVersion") << "File=testrc\n" "Group=group\n" "Key=old,new\n" "Options=overwrite\n" << "testrc" << "[group]\n" "old=value\n" << "testrc" << "[group]\n" "old=value\n" << "" << false << false; QTest::newRow("DontMigrateWhenUpdateCantDoItMissingFilename") << "Group=group\n" "Key=old,new\n" "Options=overwrite\n" << "testrc" << "[group]\n" "old=value\n" << "testrc" << "[group]\n" "old=value\n" << "" << true << false; } void TestKConfUpdate::test() { QFETCH(QString, updContent); QFETCH(QString, oldConfName); QFETCH(QString, oldConfContent); QFETCH(QString, newConfName); QFETCH(QString, expectedNewConfContent); QFETCH(QString, expectedOldConfContent); QFETCH(bool, useVersion5); QFETCH(bool, shouldUpdateWork); // Prepend Version and the Id= field to the upd content const QString header = QLatin1String("Id=%1\n").arg(QLatin1String(QTest::currentDataTag())); updContent = header + updContent; if (useVersion5) { updContent.prepend(QLatin1String{"Version=5\n"}); } const QString configDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QVERIFY(QDir().mkpath(configDir)); QString oldConfPath = configDir + QLatin1Char('/') + oldConfName; QString newConfPath = configDir + QLatin1Char('/') + newConfName; QFile::remove(oldConfPath); QFile::remove(newConfPath); writeFile(oldConfPath, oldConfContent); QCOMPARE(readFile(oldConfPath), oldConfContent); std::unique_ptr updFile(writeUpdFile(updContent)); runKConfUpdate(updFile->fileName()); QString updateInfo = QLatin1String("%1:%2").arg(updFile->fileName().section(QLatin1Char('/'), -1), QLatin1String{QTest::currentDataTag()}); QString newConfContentAfter = readFile(newConfPath); if (shouldUpdateWork) { expectedNewConfContent = expectedNewConfContent.arg(updateInfo); } QCOMPARE(newConfContentAfter, expectedNewConfContent); if (oldConfName != newConfName) { QString oldConfContentAfter = readFile(oldConfPath); if (shouldUpdateWork) { expectedOldConfContent = expectedOldConfContent.arg(updateInfo); } QCOMPARE(oldConfContentAfter, expectedOldConfContent); } } void TestKConfUpdate::testScript_data() { #ifdef Q_OS_WIN // add sh.exe and sed.exe to PATH on Windows // uncomment and adapt path to run all tests // qputenv("PATH", qgetenv("PATH") + ";C:\\kde\\msys\\bin"); #endif QTest::addColumn("updContent"); QTest::addColumn("updScript"); QTest::addColumn("oldConfContent"); QTest::addColumn("expectedNewConfContent"); QTest::newRow("delete-key") << "File=testrc\n" "Group=group\n" "Script=test.sh,sh\n" << "echo '# DELETE deprecated'\n" << "[group]\n" "deprecated=foo\n" "valid=bar\n" << "[$Version]\n" "update_info=%1\n" "\n" "[group]\n" "valid=bar\n"; QTest::newRow("delete-key2") << "File=testrc\n" "Script=test.sh,sh\n" << "echo '# DELETE [group]deprecated'\n" "echo '# DELETE [group][sub]deprecated2'\n" << "[group]\n" "deprecated=foo\n" "valid=bar\n" "\n" "[group][sub]\n" "deprecated2=foo\n" "valid2=bar\n" << "[$Version]\n" "update_info=%1\n" "\n" "[group]\n" "valid=bar\n" "\n" "[group][sub]\n" "valid2=bar\n"; QTest::newRow("delete-group") << "File=testrc\n" "Script=test.sh,sh\n" << "echo '# DELETEGROUP [group1]'\n" "echo '# DELETEGROUP [group2][sub]'\n" << "[group1]\n" "key=value\n" "\n" "[group2]\n" "valid=bar\n" "\n" "[group2][sub]\n" "key=value\n" << "[$Version]\n" "update_info=%1\n" "\n" "[group2]\n" "valid=bar\n"; QTest::newRow("delete-group2") << "File=testrc\n" "Group=group\n" "Script=test.sh,sh\n" << "echo '# DELETEGROUP'\n" << "[group]\n" "key=value\n" "\n" "[group2]\n" "valid=bar\n" << "[$Version]\n" "update_info=%1\n" "\n" "[group2]\n" "valid=bar\n"; QTest::newRow("new-key") << "File=testrc\n" "Script=test.sh,sh\n" << "echo '[group]'\n" "echo 'new=value'\n" << "[group]\n" "valid=bar\n" << "[$Version]\n" "update_info=%1\n" "\n" "[group]\n" "new=value\n" "valid=bar\n"; QTest::newRow("modify-key-no-overwrite") << "File=testrc\n" "Script=test.sh,sh\n" << "echo '[group]'\n" "echo 'existing=new'\n" << "[group]\n" "existing=old\n" << "[$Version]\n" "update_info=%1\n" "\n" "[group]\n" "existing=old\n"; QTest::newRow("modify-key-overwrite") << "File=testrc\n" "Options=overwrite\n" "Script=test.sh,sh\n" << "echo '[group]'\n" "echo 'existing=new'\n" << "[group]\n" "existing=old\n" << "[$Version]\n" "update_info=%1\n" "\n" "[group]\n" "existing=new\n"; QTest::newRow("new-key-in-subgroup") << "File=testrc\n" "Script=test.sh,sh\n" << "echo '[group][sub]'\n" "echo 'new=value2'\n" << "[group][sub]\n" "existing=foo\n" << "[$Version]\n" "update_info=%1\n" "\n" "[group][sub]\n" "existing=foo\n" "new=value2\n"; QTest::newRow("new-key-in-subgroup2") << "File=testrc\n" "Script=test.sh,sh\n" << "echo '[group][sub]'\n" "echo 'new=value3'\n" << "[group][sub]\n" "existing=foo\n" << "[$Version]\n" "update_info=%1\n" "\n" "[group][sub]\n" "existing=foo\n" "new=value3\n"; if (QStandardPaths::findExecutable(QStringLiteral("sed")).isEmpty()) { qWarning("sed executable not found, cannot run all tests!"); } else { QTest::newRow("filter") << "File=testrc\n" "Script=test.sh,sh\n" << "echo '# DELETE [group]changed'\n" "sed s/value/VALUE/\n" << "[group]\n" "changed=value\n" "unchanged=value\n" << "[$Version]\n" "update_info=%1\n" "\n" "[group]\n" "changed=VALUE\n" "unchanged=value\n"; QTest::newRow("filter-subgroup") << "File=testrc\n" "Script=test.sh,sh\n" << "echo '# DELETE [group][sub]changed'\n" "sed s/value/VALUE/\n" << "[group]\n" "unchanged=value\n" "\n" "[group][sub]\n" "changed=value\n" "unchanged=value\n" << "[$Version]\n" "update_info=%1\n" "\n" "[group]\n" "unchanged=value\n" "\n" "[group][sub]\n" "changed=VALUE\n" "unchanged=value\n"; } } void TestKConfUpdate::testScript() { if (QStandardPaths::findExecutable(QStringLiteral("sh")).isEmpty()) { QSKIP("Could not find sh executable, cannot run test"); return; } QFETCH(QString, updContent); QFETCH(QString, updScript); QFETCH(QString, oldConfContent); QFETCH(QString, expectedNewConfContent); // Prepend the Version and Id= field to the upd content updContent.prepend(QLatin1String("Version=5\nId=%1\n").arg(QLatin1String{QTest::currentDataTag()})); std::unique_ptr updFile(writeUpdFile(updContent)); const QString scriptDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kconf_update"}; QVERIFY(QDir().mkpath(scriptDir)); const QString scriptPath = scriptDir + QLatin1String{"/test.sh"}; writeFile(scriptPath, updScript); QCOMPARE(readFile(scriptPath), updScript); const QString confPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String{"/testrc"}; writeFile(confPath, oldConfContent); QCOMPARE(readFile(confPath), oldConfContent); runKConfUpdate(updFile->fileName()); const QString updateInfo = QLatin1String("%1:%2").arg(updFile->fileName().section(QLatin1Char{'/'}, -1), QLatin1String{QTest::currentDataTag()}); QString newConfContent = readFile(confPath); expectedNewConfContent = expectedNewConfContent.arg(updateInfo); QCOMPARE(newConfContent, expectedNewConfContent); }