File indexing completed on 2024-05-12 05:46:50

0001 /* This file is part of the KDE libraries
0002    Copyright 2010 Canonical Ltd
0003    Author: Aurélien Gâteau <aurelien.gateau@canonical.com>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License (LGPL) as published by the Free Software Foundation;
0008    either version 2 of the License, or (at your option) any later
0009    version.
0010 
0011    This library is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this library; see the file COPYING.LIB.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019    Boston, MA 02110-1301, USA.
0020 */
0021 #include "test_kconf_update.h"
0022 
0023 // Qt
0024 #include <QFile>
0025 #include <QDir>
0026 #include <QSharedPointer>
0027 #include <QProcess>
0028 #include <qtemporaryfile.h>
0029 #include <qstandardpaths.h>
0030 #include "config-kconf.h"
0031 
0032 #include <qtest.h>
0033 
0034 QTEST_GUILESS_MAIN(TestKConfUpdate)
0035 
0036 void TestKConfUpdate::initTestCase()
0037 {
0038     QStandardPaths::setTestModeEnabled(true);
0039 
0040     // Ensure it all works with spaces in paths (as happens more commonly on OSX where it's ~/Library/Application Support/)
0041     qputenv("XDG_DATA_HOME", "/tmp/a b");
0042 }
0043 
0044 static void writeFile(const QString &path, const QString &content)
0045 {
0046     QFile file(path);
0047     if (!file.open(QIODevice::WriteOnly)) {
0048         qFatal("Could not write to '%s'", qPrintable(path));
0049     }
0050     file.write(content.toUtf8());
0051 }
0052 
0053 static QString readFile(const QString &path)
0054 {
0055     QFile file(path);
0056     if (!file.open(QIODevice::ReadOnly)) {
0057         qFatal("Could not read '%s'", qPrintable(path));
0058     }
0059     QString ret = QString::fromUtf8(file.readAll());
0060 #ifdef Q_OS_WIN
0061     // KConfig always writes files with the native line ending, the test comparison uses \n
0062     ret.replace("\r\n", "\n");
0063 #endif
0064     return ret;
0065 }
0066 
0067 static QTemporaryFile *writeUpdFile(const QString &content)
0068 {
0069     QTemporaryFile *file = new QTemporaryFile(QDir::tempPath() + QLatin1String("/test_kconf_update_XXXXXX.upd"));
0070     bool ok = file->open();
0071     Q_UNUSED(ok) // silence warnings
0072     Q_ASSERT(ok);
0073     file->write(content.toUtf8());
0074     file->flush();
0075     return file;
0076 }
0077 
0078 static void runKConfUpdate(const QString &updPath)
0079 {
0080     QVERIFY(QFile::exists(KCONF_UPDATE_EXECUTABLE));
0081     QCOMPARE(0, QProcess::execute(KCONF_UPDATE_EXECUTABLE, QStringList() << "--testmode" << "--debug" << updPath));
0082 }
0083 
0084 void TestKConfUpdate::test_data()
0085 {
0086     QTest::addColumn<QString>("updContent");
0087     QTest::addColumn<QString>("oldConfName");
0088     QTest::addColumn<QString>("oldConfContent");
0089     QTest::addColumn<QString>("newConfName");
0090     QTest::addColumn<QString>("expectedNewConfContent");
0091     QTest::addColumn<QString>("expectedOldConfContent");
0092     QTest::addColumn<bool>("useVersion5");
0093     QTest::addColumn<bool>("shouldUpdateWork");
0094 
0095     QTest::newRow("moveKeysSameFile")
0096             <<
0097             "File=testrc\n"
0098             "Group=group\n"
0099             "Key=old,new\n"
0100             "Options=overwrite\n"
0101             <<
0102             "testrc"
0103             <<
0104             "[group]\n"
0105             "old=value\n"
0106             <<
0107             "testrc"
0108             <<
0109             "[$Version]\n"
0110             "update_info=%1\n"
0111             "\n"
0112             "[group]\n"
0113             "new=value\n"
0114             <<
0115             ""
0116             << true
0117             << true
0118             ;
0119     QTest::newRow("moveKeysOtherFile")
0120             <<
0121             "File=oldrc,newrc\n"
0122             "Group=group1,group2\n"
0123             "Key=old,new\n"
0124             "Options=overwrite\n"
0125             <<
0126             "oldrc"
0127             <<
0128             "[group1]\n"
0129             "old=value\n"
0130             "[stay]\n"
0131             "foo=bar\n"
0132             <<
0133             "newrc"
0134             <<
0135             "[$Version]\n"
0136             "update_info=%1\n"
0137             "\n"
0138             "[group2]\n"
0139             "new=value\n"
0140             <<
0141             "[$Version]\n"
0142             "update_info=%1\n"
0143             "\n"
0144             "[stay]\n"
0145             "foo=bar\n"
0146             << true
0147             << true
0148             ;
0149     QTest::newRow("allKeys")
0150             <<
0151             "File=testrc\n"
0152             "Group=group1,group2\n"
0153             "AllKeys\n"
0154             <<
0155             "testrc"
0156             <<
0157             "[group1]\n"
0158             "key1=value1\n"
0159             "key2=value2\n"
0160             "\n"
0161             "[stay]\n"
0162             "foo=bar\n"
0163             <<
0164             "testrc"
0165             <<
0166             "[$Version]\n"
0167             "update_info=%1\n"
0168             "\n"
0169             "[group2]\n"
0170             "key1=value1\n"
0171             "key2=value2\n"
0172             "\n"
0173             "[stay]\n"
0174             "foo=bar\n"
0175             <<
0176             ""
0177             << true
0178             << true
0179             ;
0180     QTest::newRow("allKeysSubGroup")
0181             <<
0182             "File=testrc\n"
0183             "Group=[group][sub1],[group][sub2]\n"
0184             "AllKeys\n"
0185             <<
0186             "testrc"
0187             <<
0188             "[group][sub1]\n"
0189             "key1=value1\n"
0190             "key2=value2\n"
0191             "\n"
0192             "[group][sub1][subsub]\n"
0193             "key3=value3\n"
0194             "key4=value4\n"
0195             "\n"
0196             "[stay]\n"
0197             "foo=bar\n"
0198             <<
0199             "testrc"
0200             <<
0201             "[$Version]\n"
0202             "update_info=%1\n"
0203             "\n"
0204             "[group][sub2]\n"
0205             "key1=value1\n"
0206             "key2=value2\n"
0207             "\n"
0208             "[group][sub2][subsub]\n"
0209             "key3=value3\n"
0210             "key4=value4\n"
0211             "\n"
0212             "[stay]\n"
0213             "foo=bar\n"
0214             <<
0215             ""
0216             << true
0217             << true
0218             ;
0219     QTest::newRow("removeGroup")
0220             <<
0221             "File=testrc\n"
0222             "RemoveGroup=remove\n"
0223             <<
0224             "testrc"
0225             <<
0226             "[keep]\n"
0227             "key=value\n"
0228             ""
0229             "[remove]\n"
0230             "key=value\n"
0231             <<
0232             "testrc"
0233             <<
0234             "[$Version]\n"
0235             "update_info=%1\n"
0236             "\n"
0237             "[keep]\n"
0238             "key=value\n"
0239             <<
0240             ""
0241             << true
0242             << true
0243             ;
0244     QTest::newRow("moveKeysSameFileDontExist")
0245             <<
0246             "File=testrc\n"
0247             "Group=group,group2\n"
0248             "Key=key1\n"
0249             "Key=key2\n"
0250             <<
0251             "testrc"
0252             <<
0253             "[group]\n"
0254             "key1=value1\n"
0255             "key3=value3\n"
0256             <<
0257             "testrc"
0258             <<
0259             "[$Version]\n"
0260             "update_info=%1\n"
0261             "\n"
0262             "[group]\n"
0263             "key3=value3\n"
0264             "\n"
0265             "[group2]\n"
0266             "key1=value1\n"
0267             <<
0268             ""
0269             << true
0270             << true
0271             ;
0272     QTest::newRow("DontMigrateWhenFileDoesntHaveVersion")
0273             <<
0274             "File=testrc\n"
0275             "Group=group\n"
0276             "Key=old,new\n"
0277             "Options=overwrite\n"
0278             <<
0279             "testrc"
0280             <<
0281             "[group]\n"
0282             "old=value\n"
0283             <<
0284             "testrc"
0285             <<
0286             "[group]\n"
0287             "old=value\n"
0288             <<
0289             ""
0290             << false
0291             << false
0292             ;
0293 
0294     QTest::newRow("DontMigrateWhenUpdateCantDoItMissingFilename")
0295             <<
0296             "Group=group\n"
0297             "Key=old,new\n"
0298             "Options=overwrite\n"
0299             <<
0300             "testrc"
0301             <<
0302             "[group]\n"
0303             "old=value\n"
0304             <<
0305             "testrc"
0306             <<
0307             "[group]\n"
0308             "old=value\n"
0309             <<
0310             ""
0311             << true
0312             << false
0313             ;
0314 }
0315 
0316 void TestKConfUpdate::test()
0317 {
0318     QFETCH(QString, updContent);
0319     QFETCH(QString, oldConfName);
0320     QFETCH(QString, oldConfContent);
0321     QFETCH(QString, newConfName);
0322     QFETCH(QString, expectedNewConfContent);
0323     QFETCH(QString, expectedOldConfContent);
0324     QFETCH(bool, useVersion5);
0325     QFETCH(bool, shouldUpdateWork);
0326 
0327     // Prepend Version and the Id= field to the upd content
0328     const QString header = QStringLiteral("Id=%1\n").arg(QTest::currentDataTag());
0329     updContent = header + updContent;
0330     if (useVersion5)
0331         updContent.prepend("Version=5\n");
0332 
0333     const QString configDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
0334     QVERIFY(QDir().mkpath(configDir));
0335     QString oldConfPath = configDir + QLatin1Char('/') + oldConfName;
0336     QString newConfPath = configDir + QLatin1Char('/') + newConfName;
0337 
0338     QFile::remove(oldConfPath);
0339     QFile::remove(newConfPath);
0340 
0341     writeFile(oldConfPath, oldConfContent);
0342     QCOMPARE(readFile(oldConfPath), oldConfContent);
0343     QSharedPointer<QTemporaryFile> updFile(writeUpdFile(updContent));
0344     runKConfUpdate(updFile->fileName());
0345 
0346     QString updateInfo = QStringLiteral("%1:%2")
0347                          .arg(updFile->fileName().section('/', -1))
0348                          .arg(QTest::currentDataTag());
0349 
0350     QString newConfContentAfter = readFile(newConfPath);
0351     if (shouldUpdateWork) {
0352         expectedNewConfContent = expectedNewConfContent.arg(updateInfo);
0353     }
0354     QCOMPARE(newConfContentAfter, expectedNewConfContent);
0355 
0356     if (oldConfName != newConfName) {
0357         QString oldConfContentAfter = readFile(oldConfPath);
0358         if (shouldUpdateWork) {
0359             expectedOldConfContent = expectedOldConfContent.arg(updateInfo);
0360         }
0361         QCOMPARE(oldConfContentAfter, expectedOldConfContent);
0362     }
0363 }
0364 
0365 void TestKConfUpdate::testScript_data()
0366 {
0367 #ifdef Q_OS_WIN
0368     // add sh.exe and sed.exe to PATH on Windows
0369     // uncomment and adapt path to run all tests
0370     // qputenv("PATH", qgetenv("PATH") + ";C:\\kde\\msys\\bin");
0371 #endif
0372 
0373     QTest::addColumn<QString>("updContent");
0374     QTest::addColumn<QString>("updScript");
0375     QTest::addColumn<QString>("oldConfContent");
0376     QTest::addColumn<QString>("expectedNewConfContent");
0377 
0378     QTest::newRow("delete-key")
0379             <<
0380             "File=testrc\n"
0381             "Group=group\n"
0382             "Script=test.sh,sh\n"
0383             <<
0384             "echo '# DELETE deprecated'\n"
0385             <<
0386             "[group]\n"
0387             "deprecated=foo\n"
0388             "valid=bar\n"
0389             <<
0390             "[$Version]\n"
0391             "update_info=%1\n"
0392             "\n"
0393             "[group]\n"
0394             "valid=bar\n"
0395             ;
0396 
0397     QTest::newRow("delete-key2")
0398             <<
0399             "File=testrc\n"
0400             "Script=test.sh,sh\n"
0401             <<
0402             "echo '# DELETE [group]deprecated'\n"
0403             "echo '# DELETE [group][sub]deprecated2'\n"
0404             <<
0405             "[group]\n"
0406             "deprecated=foo\n"
0407             "valid=bar\n"
0408             "\n"
0409             "[group][sub]\n"
0410             "deprecated2=foo\n"
0411             "valid2=bar\n"
0412             <<
0413             "[$Version]\n"
0414             "update_info=%1\n"
0415             "\n"
0416             "[group]\n"
0417             "valid=bar\n"
0418             "\n"
0419             "[group][sub]\n"
0420             "valid2=bar\n"
0421             ;
0422 
0423     QTest::newRow("delete-group")
0424             <<
0425             "File=testrc\n"
0426             "Script=test.sh,sh\n"
0427             <<
0428             "echo '# DELETEGROUP [group1]'\n"
0429             "echo '# DELETEGROUP [group2][sub]'\n"
0430             <<
0431             "[group1]\n"
0432             "key=value\n"
0433             "\n"
0434             "[group2]\n"
0435             "valid=bar\n"
0436             "\n"
0437             "[group2][sub]\n"
0438             "key=value\n"
0439             <<
0440             "[$Version]\n"
0441             "update_info=%1\n"
0442             "\n"
0443             "[group2]\n"
0444             "valid=bar\n"
0445             ;
0446 
0447     QTest::newRow("delete-group2")
0448             <<
0449             "File=testrc\n"
0450             "Group=group\n"
0451             "Script=test.sh,sh\n"
0452             <<
0453             "echo '# DELETEGROUP'\n"
0454             <<
0455             "[group]\n"
0456             "key=value\n"
0457             "\n"
0458             "[group2]\n"
0459             "valid=bar\n"
0460             <<
0461             "[$Version]\n"
0462             "update_info=%1\n"
0463             "\n"
0464             "[group2]\n"
0465             "valid=bar\n"
0466             ;
0467 
0468     QTest::newRow("new-key")
0469             <<
0470             "File=testrc\n"
0471             "Script=test.sh,sh\n"
0472             <<
0473             "echo '[group]'\n"
0474             "echo 'new=value'\n"
0475             <<
0476             "[group]\n"
0477             "valid=bar\n"
0478             <<
0479             "[$Version]\n"
0480             "update_info=%1\n"
0481             "\n"
0482             "[group]\n"
0483             "new=value\n"
0484             "valid=bar\n"
0485             ;
0486 
0487     QTest::newRow("modify-key-no-overwrite")
0488             <<
0489             "File=testrc\n"
0490             "Script=test.sh,sh\n"
0491             <<
0492             "echo '[group]'\n"
0493             "echo 'existing=new'\n"
0494             <<
0495             "[group]\n"
0496             "existing=old\n"
0497             <<
0498             "[$Version]\n"
0499             "update_info=%1\n"
0500             "\n"
0501             "[group]\n"
0502             "existing=old\n"
0503             ;
0504 
0505     QTest::newRow("modify-key-overwrite")
0506             <<
0507             "File=testrc\n"
0508             "Options=overwrite\n"
0509             "Script=test.sh,sh\n"
0510             <<
0511             "echo '[group]'\n"
0512             "echo 'existing=new'\n"
0513             <<
0514             "[group]\n"
0515             "existing=old\n"
0516             <<
0517             "[$Version]\n"
0518             "update_info=%1\n"
0519             "\n"
0520             "[group]\n"
0521             "existing=new\n"
0522             ;
0523 
0524     QTest::newRow("new-key-in-subgroup")
0525             <<
0526             "File=testrc\n"
0527             "Script=test.sh,sh\n"
0528             <<
0529             "echo '[group][sub]'\n"
0530             "echo 'new=value2'\n"
0531             <<
0532             "[group][sub]\n"
0533             "existing=foo\n"
0534             <<
0535             "[$Version]\n"
0536             "update_info=%1\n"
0537             "\n"
0538             "[group][sub]\n"
0539             "existing=foo\n"
0540             "new=value2\n"
0541             ;
0542 
0543     QTest::newRow("new-key-in-subgroup2")
0544             <<
0545             "File=testrc\n"
0546             "Script=test.sh,sh\n"
0547             <<
0548             "echo '[group][sub]'\n"
0549             "echo 'new=value3'\n"
0550             <<
0551             "[group][sub]\n"
0552             "existing=foo\n"
0553             <<
0554             "[$Version]\n"
0555             "update_info=%1\n"
0556             "\n"
0557             "[group][sub]\n"
0558             "existing=foo\n"
0559             "new=value3\n"
0560             ;
0561 
0562     if (QStandardPaths::findExecutable(QStringLiteral("sed")).isEmpty()) {
0563         qWarning("sed executable not found, cannot run all tests!");
0564     } else {
0565         QTest::newRow("filter")
0566             <<
0567             "File=testrc\n"
0568             "Script=test.sh,sh\n"
0569             <<
0570             "echo '# DELETE [group]changed'\n"
0571             "sed s/value/VALUE/\n"
0572             <<
0573             "[group]\n"
0574             "changed=value\n"
0575             "unchanged=value\n"
0576             <<
0577             "[$Version]\n"
0578             "update_info=%1\n"
0579             "\n"
0580             "[group]\n"
0581             "changed=VALUE\n"
0582             "unchanged=value\n"
0583             ;
0584 
0585     QTest::newRow("filter-subgroup")
0586             <<
0587             "File=testrc\n"
0588             "Script=test.sh,sh\n"
0589             <<
0590             "echo '# DELETE [group][sub]changed'\n"
0591             "sed s/value/VALUE/\n"
0592             <<
0593             "[group]\n"
0594             "unchanged=value\n"
0595             "\n"
0596             "[group][sub]\n"
0597             "changed=value\n"
0598             "unchanged=value\n"
0599             <<
0600             "[$Version]\n"
0601             "update_info=%1\n"
0602             "\n"
0603             "[group]\n"
0604             "unchanged=value\n"
0605             "\n"
0606             "[group][sub]\n"
0607             "changed=VALUE\n"
0608             "unchanged=value\n"
0609             ;
0610     }
0611 }
0612 
0613 void TestKConfUpdate::testScript()
0614 {
0615     if (QStandardPaths::findExecutable(QStringLiteral("sh")).isEmpty()) {
0616         QSKIP("Could not find sh executable, cannot run test");
0617         return;
0618     }
0619 
0620     QFETCH(QString, updContent);
0621     QFETCH(QString, updScript);
0622     QFETCH(QString, oldConfContent);
0623     QFETCH(QString, expectedNewConfContent);
0624 
0625     // Prepend the Version and Id= field to the upd content
0626     updContent = QStringLiteral("Version=5\nId=%1\n").arg(QTest::currentDataTag()) + updContent;
0627 
0628     QSharedPointer<QTemporaryFile> updFile(writeUpdFile(updContent));
0629 
0630     const QString scriptDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kconf_update";
0631     QVERIFY(QDir().mkpath(scriptDir));
0632     QString scriptPath = scriptDir + "/test.sh";
0633     writeFile(scriptPath, updScript);
0634     QCOMPARE(readFile(scriptPath), updScript);
0635 
0636     QString confPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + "testrc";
0637     writeFile(confPath, oldConfContent);
0638     QCOMPARE(readFile(confPath), oldConfContent);
0639 
0640     runKConfUpdate(updFile->fileName());
0641 
0642     QString updateInfo = QStringLiteral("%1:%2")
0643                          .arg(updFile->fileName().section('/', -1))
0644                          .arg(QTest::currentDataTag());
0645     QString newConfContent = readFile(confPath);
0646     expectedNewConfContent = expectedNewConfContent.arg(updateInfo);
0647     QCOMPARE(newConfContent, expectedNewConfContent);
0648 }
0649 
0650