File indexing completed on 2024-04-28 15:19:33

0001 /*  This file is part of the KDE libraries
0002     SPDX-FileCopyrightText: 2010 Canonical Ltd
0003     SPDX-FileContributor: Aurélien Gâteau <aurelien.gateau@canonical.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 #include "test_kconf_update.h"
0008 
0009 #include <memory>
0010 
0011 // Qt
0012 #include "config-kconf.h"
0013 #include <QDir>
0014 #include <QFile>
0015 #include <QProcess>
0016 #include <qstandardpaths.h>
0017 #include <qtemporaryfile.h>
0018 
0019 #include <qtest.h>
0020 
0021 QTEST_GUILESS_MAIN(TestKConfUpdate)
0022 
0023 void TestKConfUpdate::initTestCase()
0024 {
0025     QStandardPaths::setTestModeEnabled(true);
0026 
0027     // Ensure it all works with spaces in paths (as happens more commonly on OSX where it's ~/Library/Application Support/)
0028     qputenv("XDG_DATA_HOME", "/tmp/a b");
0029 }
0030 
0031 static void writeFile(const QString &path, const QString &content)
0032 {
0033     QFile file(path);
0034     if (!file.open(QIODevice::WriteOnly)) {
0035         qFatal("Could not write to '%s'", qPrintable(path));
0036     }
0037     file.write(content.toUtf8());
0038 }
0039 
0040 static QString readFile(const QString &path)
0041 {
0042     QFile file(path);
0043     if (!file.open(QIODevice::ReadOnly)) {
0044         qFatal("Could not read '%s'", qPrintable(path));
0045     }
0046     QString ret = QString::fromUtf8(file.readAll());
0047 #ifdef Q_OS_WIN
0048     // KConfig always writes files with the native line ending, the test comparison uses \n
0049     ret.replace(QLatin1String{"\r\n"}, QLatin1String{"\n"});
0050 #endif
0051     return ret;
0052 }
0053 
0054 static std::unique_ptr<QTemporaryFile> writeUpdFile(const QString &content)
0055 {
0056     std::unique_ptr<QTemporaryFile> file{new QTemporaryFile(QDir::tempPath() + QLatin1String("/test_kconf_update_XXXXXX.upd"))};
0057     bool ok = file->open();
0058     Q_UNUSED(ok) // silence warnings
0059     Q_ASSERT(ok);
0060     file->write(content.toUtf8());
0061     file->flush();
0062     return file;
0063 }
0064 
0065 static void runKConfUpdate(const QString &updPath)
0066 {
0067     QVERIFY(QFile::exists(QStringLiteral(KCONF_UPDATE_EXECUTABLE)));
0068     QCOMPARE(0, QProcess::execute(QStringLiteral(KCONF_UPDATE_EXECUTABLE), QStringList{QStringLiteral("--testmode"), QStringLiteral("--debug"), updPath}));
0069 }
0070 
0071 void TestKConfUpdate::test_data()
0072 {
0073     QTest::addColumn<QString>("updContent");
0074     QTest::addColumn<QString>("oldConfName");
0075     QTest::addColumn<QString>("oldConfContent");
0076     QTest::addColumn<QString>("newConfName");
0077     QTest::addColumn<QString>("expectedNewConfContent");
0078     QTest::addColumn<QString>("expectedOldConfContent");
0079     QTest::addColumn<bool>("useVersion5");
0080     QTest::addColumn<bool>("shouldUpdateWork");
0081 
0082     QTest::newRow("moveKeysSameFile") << "File=testrc\n"
0083                                          "Group=group\n"
0084                                          "Key=old,new\n"
0085                                          "Options=overwrite\n"
0086                                       << "testrc"
0087                                       << "[group]\n"
0088                                          "old=value\n"
0089                                       << "testrc"
0090                                       << "[$Version]\n"
0091                                          "update_info=%1\n"
0092                                          "\n"
0093                                          "[group]\n"
0094                                          "new=value\n"
0095                                       << "" << true << true;
0096     QTest::newRow("moveKeysOtherFile") << "File=oldrc,newrc\n"
0097                                           "Group=group1,group2\n"
0098                                           "Key=old,new\n"
0099                                           "Options=overwrite\n"
0100                                        << "oldrc"
0101                                        << "[group1]\n"
0102                                           "old=value\n"
0103                                           "[stay]\n"
0104                                           "foo=bar\n"
0105                                        << "newrc"
0106                                        << "[$Version]\n"
0107                                           "update_info=%1\n"
0108                                           "\n"
0109                                           "[group2]\n"
0110                                           "new=value\n"
0111                                        << "[$Version]\n"
0112                                           "update_info=%1\n"
0113                                           "\n"
0114                                           "[stay]\n"
0115                                           "foo=bar\n"
0116                                        << true << true;
0117     QTest::newRow("allKeys") << "File=testrc\n"
0118                                 "Group=group1,group2\n"
0119                                 "AllKeys\n"
0120                              << "testrc"
0121                              << "[group1]\n"
0122                                 "key1=value1\n"
0123                                 "key2=value2\n"
0124                                 "\n"
0125                                 "[stay]\n"
0126                                 "foo=bar\n"
0127                              << "testrc"
0128                              << "[$Version]\n"
0129                                 "update_info=%1\n"
0130                                 "\n"
0131                                 "[group2]\n"
0132                                 "key1=value1\n"
0133                                 "key2=value2\n"
0134                                 "\n"
0135                                 "[stay]\n"
0136                                 "foo=bar\n"
0137                              << "" << true << true;
0138     QTest::newRow("allKeysSubGroup") << "File=testrc\n"
0139                                         "Group=[group][sub1],[group][sub2]\n"
0140                                         "AllKeys\n"
0141                                      << "testrc"
0142                                      << "[group][sub1]\n"
0143                                         "key1=value1\n"
0144                                         "key2=value2\n"
0145                                         "\n"
0146                                         "[group][sub1][subsub]\n"
0147                                         "key3=value3\n"
0148                                         "key4=value4\n"
0149                                         "\n"
0150                                         "[stay]\n"
0151                                         "foo=bar\n"
0152                                      << "testrc"
0153                                      << "[$Version]\n"
0154                                         "update_info=%1\n"
0155                                         "\n"
0156                                         "[group][sub2]\n"
0157                                         "key1=value1\n"
0158                                         "key2=value2\n"
0159                                         "\n"
0160                                         "[group][sub2][subsub]\n"
0161                                         "key3=value3\n"
0162                                         "key4=value4\n"
0163                                         "\n"
0164                                         "[stay]\n"
0165                                         "foo=bar\n"
0166                                      << "" << true << true;
0167     QTest::newRow("removeGroup") << "File=testrc\n"
0168                                     "RemoveGroup=remove\n"
0169                                  << "testrc"
0170                                  << "[keep]\n"
0171                                     "key=value\n"
0172                                     ""
0173                                     "[remove]\n"
0174                                     "key=value\n"
0175                                  << "testrc"
0176                                  << "[$Version]\n"
0177                                     "update_info=%1\n"
0178                                     "\n"
0179                                     "[keep]\n"
0180                                     "key=value\n"
0181                                  << "" << true << true;
0182     QTest::newRow("moveKeysSameFileDontExist") << "File=testrc\n"
0183                                                   "Group=group,group2\n"
0184                                                   "Key=key1\n"
0185                                                   "Key=key2\n"
0186                                                << "testrc"
0187                                                << "[group]\n"
0188                                                   "key1=value1\n"
0189                                                   "key3=value3\n"
0190                                                << "testrc"
0191                                                << "[$Version]\n"
0192                                                   "update_info=%1\n"
0193                                                   "\n"
0194                                                   "[group]\n"
0195                                                   "key3=value3\n"
0196                                                   "\n"
0197                                                   "[group2]\n"
0198                                                   "key1=value1\n"
0199                                                << "" << true << true;
0200     QTest::newRow("DontMigrateWhenFileDoesntHaveVersion") << "File=testrc\n"
0201                                                              "Group=group\n"
0202                                                              "Key=old,new\n"
0203                                                              "Options=overwrite\n"
0204                                                           << "testrc"
0205                                                           << "[group]\n"
0206                                                              "old=value\n"
0207                                                           << "testrc"
0208                                                           << "[group]\n"
0209                                                              "old=value\n"
0210                                                           << "" << false << false;
0211 
0212     QTest::newRow("DontMigrateWhenUpdateCantDoItMissingFilename") << "Group=group\n"
0213                                                                      "Key=old,new\n"
0214                                                                      "Options=overwrite\n"
0215                                                                   << "testrc"
0216                                                                   << "[group]\n"
0217                                                                      "old=value\n"
0218                                                                   << "testrc"
0219                                                                   << "[group]\n"
0220                                                                      "old=value\n"
0221                                                                   << "" << true << false;
0222 }
0223 
0224 void TestKConfUpdate::test()
0225 {
0226     QFETCH(QString, updContent);
0227     QFETCH(QString, oldConfName);
0228     QFETCH(QString, oldConfContent);
0229     QFETCH(QString, newConfName);
0230     QFETCH(QString, expectedNewConfContent);
0231     QFETCH(QString, expectedOldConfContent);
0232     QFETCH(bool, useVersion5);
0233     QFETCH(bool, shouldUpdateWork);
0234 
0235     // Prepend Version and the Id= field to the upd content
0236     const QString header = QLatin1String("Id=%1\n").arg(QLatin1String(QTest::currentDataTag()));
0237     updContent = header + updContent;
0238     if (useVersion5) {
0239         updContent.prepend(QLatin1String{"Version=5\n"});
0240     }
0241 
0242     const QString configDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
0243     QVERIFY(QDir().mkpath(configDir));
0244     QString oldConfPath = configDir + QLatin1Char('/') + oldConfName;
0245     QString newConfPath = configDir + QLatin1Char('/') + newConfName;
0246 
0247     QFile::remove(oldConfPath);
0248     QFile::remove(newConfPath);
0249 
0250     writeFile(oldConfPath, oldConfContent);
0251     QCOMPARE(readFile(oldConfPath), oldConfContent);
0252     std::unique_ptr<QTemporaryFile> updFile(writeUpdFile(updContent));
0253     runKConfUpdate(updFile->fileName());
0254 
0255     QString updateInfo = QLatin1String("%1:%2").arg(updFile->fileName().section(QLatin1Char('/'), -1), QLatin1String{QTest::currentDataTag()});
0256 
0257     QString newConfContentAfter = readFile(newConfPath);
0258     if (shouldUpdateWork) {
0259         expectedNewConfContent = expectedNewConfContent.arg(updateInfo);
0260     }
0261     QCOMPARE(newConfContentAfter, expectedNewConfContent);
0262 
0263     if (oldConfName != newConfName) {
0264         QString oldConfContentAfter = readFile(oldConfPath);
0265         if (shouldUpdateWork) {
0266             expectedOldConfContent = expectedOldConfContent.arg(updateInfo);
0267         }
0268         QCOMPARE(oldConfContentAfter, expectedOldConfContent);
0269     }
0270 }
0271 
0272 void TestKConfUpdate::testScript_data()
0273 {
0274 #ifdef Q_OS_WIN
0275     // add sh.exe and sed.exe to PATH on Windows
0276     // uncomment and adapt path to run all tests
0277     // qputenv("PATH", qgetenv("PATH") + ";C:\\kde\\msys\\bin");
0278 #endif
0279 
0280     QTest::addColumn<QString>("updContent");
0281     QTest::addColumn<QString>("updScript");
0282     QTest::addColumn<QString>("oldConfContent");
0283     QTest::addColumn<QString>("expectedNewConfContent");
0284 
0285     QTest::newRow("delete-key") << "File=testrc\n"
0286                                    "Group=group\n"
0287                                    "Script=test.sh,sh\n"
0288                                 << "echo '# DELETE deprecated'\n"
0289                                 << "[group]\n"
0290                                    "deprecated=foo\n"
0291                                    "valid=bar\n"
0292                                 << "[$Version]\n"
0293                                    "update_info=%1\n"
0294                                    "\n"
0295                                    "[group]\n"
0296                                    "valid=bar\n";
0297 
0298     QTest::newRow("delete-key2") << "File=testrc\n"
0299                                     "Script=test.sh,sh\n"
0300                                  << "echo '# DELETE [group]deprecated'\n"
0301                                     "echo '# DELETE [group][sub]deprecated2'\n"
0302                                  << "[group]\n"
0303                                     "deprecated=foo\n"
0304                                     "valid=bar\n"
0305                                     "\n"
0306                                     "[group][sub]\n"
0307                                     "deprecated2=foo\n"
0308                                     "valid2=bar\n"
0309                                  << "[$Version]\n"
0310                                     "update_info=%1\n"
0311                                     "\n"
0312                                     "[group]\n"
0313                                     "valid=bar\n"
0314                                     "\n"
0315                                     "[group][sub]\n"
0316                                     "valid2=bar\n";
0317 
0318     QTest::newRow("delete-group") << "File=testrc\n"
0319                                      "Script=test.sh,sh\n"
0320                                   << "echo '# DELETEGROUP [group1]'\n"
0321                                      "echo '# DELETEGROUP [group2][sub]'\n"
0322                                   << "[group1]\n"
0323                                      "key=value\n"
0324                                      "\n"
0325                                      "[group2]\n"
0326                                      "valid=bar\n"
0327                                      "\n"
0328                                      "[group2][sub]\n"
0329                                      "key=value\n"
0330                                   << "[$Version]\n"
0331                                      "update_info=%1\n"
0332                                      "\n"
0333                                      "[group2]\n"
0334                                      "valid=bar\n";
0335 
0336     QTest::newRow("delete-group2") << "File=testrc\n"
0337                                       "Group=group\n"
0338                                       "Script=test.sh,sh\n"
0339                                    << "echo '# DELETEGROUP'\n"
0340                                    << "[group]\n"
0341                                       "key=value\n"
0342                                       "\n"
0343                                       "[group2]\n"
0344                                       "valid=bar\n"
0345                                    << "[$Version]\n"
0346                                       "update_info=%1\n"
0347                                       "\n"
0348                                       "[group2]\n"
0349                                       "valid=bar\n";
0350 
0351     QTest::newRow("new-key") << "File=testrc\n"
0352                                 "Script=test.sh,sh\n"
0353                              << "echo '[group]'\n"
0354                                 "echo 'new=value'\n"
0355                              << "[group]\n"
0356                                 "valid=bar\n"
0357                              << "[$Version]\n"
0358                                 "update_info=%1\n"
0359                                 "\n"
0360                                 "[group]\n"
0361                                 "new=value\n"
0362                                 "valid=bar\n";
0363 
0364     QTest::newRow("modify-key-no-overwrite") << "File=testrc\n"
0365                                                 "Script=test.sh,sh\n"
0366                                              << "echo '[group]'\n"
0367                                                 "echo 'existing=new'\n"
0368                                              << "[group]\n"
0369                                                 "existing=old\n"
0370                                              << "[$Version]\n"
0371                                                 "update_info=%1\n"
0372                                                 "\n"
0373                                                 "[group]\n"
0374                                                 "existing=old\n";
0375 
0376     QTest::newRow("modify-key-overwrite") << "File=testrc\n"
0377                                              "Options=overwrite\n"
0378                                              "Script=test.sh,sh\n"
0379                                           << "echo '[group]'\n"
0380                                              "echo 'existing=new'\n"
0381                                           << "[group]\n"
0382                                              "existing=old\n"
0383                                           << "[$Version]\n"
0384                                              "update_info=%1\n"
0385                                              "\n"
0386                                              "[group]\n"
0387                                              "existing=new\n";
0388 
0389     QTest::newRow("new-key-in-subgroup") << "File=testrc\n"
0390                                             "Script=test.sh,sh\n"
0391                                          << "echo '[group][sub]'\n"
0392                                             "echo 'new=value2'\n"
0393                                          << "[group][sub]\n"
0394                                             "existing=foo\n"
0395                                          << "[$Version]\n"
0396                                             "update_info=%1\n"
0397                                             "\n"
0398                                             "[group][sub]\n"
0399                                             "existing=foo\n"
0400                                             "new=value2\n";
0401 
0402     QTest::newRow("new-key-in-subgroup2") << "File=testrc\n"
0403                                              "Script=test.sh,sh\n"
0404                                           << "echo '[group][sub]'\n"
0405                                              "echo 'new=value3'\n"
0406                                           << "[group][sub]\n"
0407                                              "existing=foo\n"
0408                                           << "[$Version]\n"
0409                                              "update_info=%1\n"
0410                                              "\n"
0411                                              "[group][sub]\n"
0412                                              "existing=foo\n"
0413                                              "new=value3\n";
0414 
0415     if (QStandardPaths::findExecutable(QStringLiteral("sed")).isEmpty()) {
0416         qWarning("sed executable not found, cannot run all tests!");
0417     } else {
0418         QTest::newRow("filter") << "File=testrc\n"
0419                                    "Script=test.sh,sh\n"
0420                                 << "echo '# DELETE [group]changed'\n"
0421                                    "sed s/value/VALUE/\n"
0422                                 << "[group]\n"
0423                                    "changed=value\n"
0424                                    "unchanged=value\n"
0425                                 << "[$Version]\n"
0426                                    "update_info=%1\n"
0427                                    "\n"
0428                                    "[group]\n"
0429                                    "changed=VALUE\n"
0430                                    "unchanged=value\n";
0431 
0432         QTest::newRow("filter-subgroup") << "File=testrc\n"
0433                                             "Script=test.sh,sh\n"
0434                                          << "echo '# DELETE [group][sub]changed'\n"
0435                                             "sed s/value/VALUE/\n"
0436                                          << "[group]\n"
0437                                             "unchanged=value\n"
0438                                             "\n"
0439                                             "[group][sub]\n"
0440                                             "changed=value\n"
0441                                             "unchanged=value\n"
0442                                          << "[$Version]\n"
0443                                             "update_info=%1\n"
0444                                             "\n"
0445                                             "[group]\n"
0446                                             "unchanged=value\n"
0447                                             "\n"
0448                                             "[group][sub]\n"
0449                                             "changed=VALUE\n"
0450                                             "unchanged=value\n";
0451     }
0452 }
0453 
0454 void TestKConfUpdate::testScript()
0455 {
0456     if (QStandardPaths::findExecutable(QStringLiteral("sh")).isEmpty()) {
0457         QSKIP("Could not find sh executable, cannot run test");
0458         return;
0459     }
0460 
0461     QFETCH(QString, updContent);
0462     QFETCH(QString, updScript);
0463     QFETCH(QString, oldConfContent);
0464     QFETCH(QString, expectedNewConfContent);
0465 
0466     // Prepend the Version and Id= field to the upd content
0467     updContent.prepend(QLatin1String("Version=5\nId=%1\n").arg(QLatin1String{QTest::currentDataTag()}));
0468 
0469     std::unique_ptr<QTemporaryFile> updFile(writeUpdFile(updContent));
0470 
0471     const QString scriptDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String{"/kconf_update"};
0472     QVERIFY(QDir().mkpath(scriptDir));
0473     const QString scriptPath = scriptDir + QLatin1String{"/test.sh"};
0474     writeFile(scriptPath, updScript);
0475     QCOMPARE(readFile(scriptPath), updScript);
0476 
0477     const QString confPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String{"/testrc"};
0478     writeFile(confPath, oldConfContent);
0479     QCOMPARE(readFile(confPath), oldConfContent);
0480 
0481     runKConfUpdate(updFile->fileName());
0482 
0483     const QString updateInfo = QLatin1String("%1:%2").arg(updFile->fileName().section(QLatin1Char{'/'}, -1), QLatin1String{QTest::currentDataTag()});
0484     QString newConfContent = readFile(confPath);
0485     expectedNewConfContent = expectedNewConfContent.arg(updateInfo);
0486     QCOMPARE(newConfContent, expectedNewConfContent);
0487 }
0488 
0489 #include "moc_test_kconf_update.cpp"