File indexing completed on 2022-11-29 18:25:16

0001 /*
0002   This file is part of the KDE libraries
0003   Copyright 2006 Allen Winter <winter@kde.org>
0004   Copyright 2006 Jaison Lee <lee.jaison@gmail.com>
0005 
0006   This library is free software; you can redistribute it and/or
0007   modify it under the terms of the GNU Library General Public
0008   License version 2 as published by the Free Software Foundation.
0009 
0010   This library is distributed in the hope that it will be useful,
0011   but WITHOUT ANY WARRANTY; without even the implied warranty of
0012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013   Library General Public License for more details.
0014 
0015   You should have received a copy of the GNU Library General Public License
0016   along with this library; see the file COPYING.LIB.  If not, write to
0017   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018   Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "ksavefiletest.h"
0022 
0023 #include "kbackup.h" // TODO split out into a separate test
0024 
0025 #include <QDebug>
0026 #include <QTextStream>
0027 
0028 #include <qstandardpaths.h>
0029 #include <qtemporaryfile.h>
0030 
0031 #include <kdebug.h>
0032 #include <klocalizedstring.h>
0033 #include <klocale.h>
0034 #include <ktemporaryfile.h>
0035 #include <ktempdir.h>
0036 #include <ksavefile.h>
0037 
0038 #include <qtest_kde.h>
0039 
0040 QTEST_KDEMAIN_CORE(KSaveFileTest)
0041 
0042 void KSaveFileTest::initTestCase()
0043 {
0044     tmp = tmpDir.path() + '/';
0045 }
0046 
0047 void KSaveFileTest::test_ksavefile()
0048 {
0049     QString targetFile;
0050 
0051     {
0052         //This will be the file we eventually write to. Yes, I know you
0053         //should never remove the temporaryfile and then expect the filename
0054         //to continue to be unique, but this is a test for crying out loud. :)
0055         QTemporaryFile file(QDir::tempPath() + QLatin1String("/ksavefiletest_XXXXXX"));
0056         QVERIFY(file.open());
0057         targetFile = file.fileName();
0058     }
0059 
0060     {
0061         //Test basic functionality
0062         KSaveFile saveFile;
0063         saveFile.setFileName(targetFile);
0064         QVERIFY(saveFile.open());
0065         QVERIFY(!QFile::exists(targetFile));
0066 
0067         QTextStream ts(&saveFile);
0068         ts << "This is test data one.\n";
0069         ts.flush();
0070         QCOMPARE(saveFile.error(), QFile::NoError);
0071         QVERIFY(!QFile::exists(targetFile));
0072 
0073         QVERIFY(saveFile.finalize());
0074         QVERIFY(QFile::exists(targetFile));
0075 
0076         QFile::remove(targetFile);
0077         QVERIFY(!QFile::exists(targetFile));
0078     }
0079 
0080     {
0081         //Make sure destructor does what it is supposed to do.
0082         {
0083             KSaveFile saveFile;
0084             saveFile.setFileName(targetFile);
0085             QVERIFY(saveFile.open());
0086             QVERIFY(!QFile::exists(targetFile));
0087         }
0088 
0089         QVERIFY(QFile::exists(targetFile));
0090         QFile::remove(targetFile);
0091         QVERIFY(!QFile::exists(targetFile));
0092     }
0093 
0094     {
0095         //Test some error conditions
0096         KSaveFile saveFile;
0097         QVERIFY(!saveFile.open());   //no filename
0098         saveFile.setFileName(targetFile);
0099         QVERIFY(saveFile.open());
0100         QVERIFY(!QFile::exists(targetFile));
0101         QVERIFY(!saveFile.open());   //already open
0102 
0103         QVERIFY(saveFile.finalize());
0104         QVERIFY(QFile::exists(targetFile));
0105         QVERIFY(!saveFile.finalize());   //already finalized
0106 
0107         QFile::remove(targetFile);
0108         QVERIFY(!QFile::exists(targetFile));
0109     }
0110 
0111     {
0112         //Do it again, aborting this time
0113         KSaveFile saveFile(targetFile);
0114         QVERIFY(saveFile.open());
0115         QVERIFY(!QFile::exists(targetFile));
0116 
0117         QTextStream ts(&saveFile);
0118         ts << "This is test data two.\n";
0119         ts.flush();
0120         QCOMPARE(saveFile.error(), QFile::NoError);
0121         QVERIFY(!QFile::exists(targetFile));
0122 
0123         saveFile.abort();
0124         QVERIFY(!QFile::exists(targetFile));
0125     }
0126 
0127     QFile file(targetFile);
0128     QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
0129     QVERIFY(file.setPermissions(file.permissions() | QFile::ExeUser));
0130     file.close();
0131 
0132     {
0133         //Test how it works when the file already exists
0134         //Also check for special permissions
0135         KSaveFile saveFile(targetFile);
0136         QVERIFY(saveFile.open());
0137 
0138         QVERIFY(QFile::exists(targetFile));
0139         QFileInfo fi(targetFile);
0140 
0141 #ifndef Q_OS_WIN
0142         // Windows: qt_ntfs_permission_lookup is not set by default in
0143         // qfsfileengine_win.cpp, could change in future Qt versions.
0144         QVERIFY(fi.permission(QFile::ExeUser));
0145 #endif
0146         QVERIFY(fi.size() == 0);
0147 
0148         QTextStream ts(&saveFile);
0149         ts << "This is test data three.\n";
0150         ts.flush();
0151 
0152         fi.refresh();
0153         QVERIFY(fi.size() == 0);
0154         QVERIFY(saveFile.finalize());
0155 
0156         fi.refresh();
0157         QVERIFY(fi.size() != 0);
0158 #ifndef Q_OS_WIN
0159         QVERIFY(fi.permission(QFile::ExeUser));
0160 #endif
0161 
0162         QFile::remove(targetFile);
0163     }
0164 
0165     {
0166         QFileInfo fi(targetFile);
0167         targetFile = fi.fileName();
0168         QDir::setCurrent(fi.path());
0169 
0170         //one more time, this time with relative filenames
0171         KSaveFile saveFile(targetFile);
0172         QVERIFY(saveFile.open());
0173         QVERIFY(!QFile::exists(targetFile));
0174 
0175         QTextStream ts(&saveFile);
0176         ts << "This is test data four.\n";
0177         ts.flush();
0178         QCOMPARE(saveFile.error(), QFile::NoError);
0179         QVERIFY(!QFile::exists(targetFile));
0180 
0181         QVERIFY(saveFile.finalize());
0182         QVERIFY(QFile::exists(targetFile));
0183         QFile::remove(targetFile);
0184     }
0185 }
0186 
0187 void KSaveFileTest::transactionalWriteNoPermissionsOnDir_data()
0188 {
0189     QTest::addColumn<bool>("directWriteFallback");
0190 
0191     QTest::newRow("default") << false;
0192     QTest::newRow("directWriteFallback") << true;
0193 }
0194 
0195 void KSaveFileTest::transactionalWriteNoPermissionsOnDir()
0196 {
0197 #ifdef Q_OS_UNIX
0198     QFETCH(bool, directWriteFallback);
0199     // Restore permissions so that the QTemporaryDir cleanup can happen
0200     class PermissionRestorer
0201     {
0202         QString m_path;
0203     public:
0204         PermissionRestorer(const QString &path)
0205             : m_path(path)
0206         {}
0207 
0208         ~PermissionRestorer()
0209         {
0210             restore();
0211         }
0212         void restore()
0213         {
0214             QFile file(m_path);
0215             file.setPermissions(QFile::Permissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner));
0216         }
0217     };
0218 
0219     KTempDir dir;
0220     QVERIFY(QFile(dir.name()).setPermissions(QFile::ReadOwner | QFile::ExeOwner));
0221     PermissionRestorer permissionRestorer(dir.name());
0222 
0223     const QString targetFile = dir.name() + QString::fromLatin1("/outfile");
0224     KSaveFile firstTry(targetFile);
0225     QVERIFY(!firstTry.open(QIODevice::WriteOnly));
0226     QCOMPARE((int)firstTry.error(), (int)QFile::PermissionsError); // actually better than QSaveFile (limited because of QTemporaryFileEngine)
0227     QVERIFY(!firstTry.finalize());
0228 
0229     // Now make an existing writable file
0230     permissionRestorer.restore();
0231     QFile f(targetFile);
0232     QVERIFY(f.open(QIODevice::WriteOnly));
0233     QCOMPARE(f.write("Hello"), Q_INT64_C(5));
0234     f.close();
0235 
0236     // Make the directory non-writable again
0237     QVERIFY(QFile(dir.name()).setPermissions(QFile::ReadOwner | QFile::ExeOwner));
0238 
0239     // And write to it again using KSaveFile; only works if directWriteFallback is enabled
0240     KSaveFile file(targetFile);
0241     file.setDirectWriteFallback(directWriteFallback);
0242     QCOMPARE(file.directWriteFallback(), directWriteFallback);
0243     if (directWriteFallback) {
0244         QVERIFY(file.open(QIODevice::WriteOnly));
0245         QCOMPARE((int)file.error(), (int)QFile::NoError);
0246         QCOMPARE(file.write("World"), Q_INT64_C(5));
0247         QVERIFY(file.finalize());
0248 
0249         QFile reader(targetFile);
0250         QVERIFY(reader.open(QIODevice::ReadOnly));
0251         QCOMPARE(QString::fromLatin1(reader.readAll()), QString::fromLatin1("World"));
0252         reader.close();
0253 
0254         QVERIFY(file.open(QIODevice::WriteOnly));
0255         QCOMPARE((int)file.error(), (int)QFile::NoError);
0256         QCOMPARE(file.write("W"), Q_INT64_C(1));
0257         QVERIFY(file.finalize());
0258 
0259         QVERIFY(reader.open(QIODevice::ReadOnly));
0260         QCOMPARE(QString::fromLatin1(reader.readAll()), QString::fromLatin1("W"));
0261     } else {
0262         QVERIFY(!file.open(QIODevice::WriteOnly));
0263         QCOMPARE((int)file.error(), (int)QFile::PermissionsError);
0264     }
0265 #endif
0266 }
0267 
0268 void KSaveFileTest::test_simpleBackupFile()
0269 {
0270     QTemporaryFile file;
0271     QVERIFY(file.open());
0272 
0273     QVERIFY(KBackup::simpleBackupFile(file.fileName()));
0274     QVERIFY(QFile::exists(file.fileName() + '~'));
0275     QFile::remove(file.fileName() + '~');
0276 
0277     QVERIFY(KBackup::simpleBackupFile(file.fileName(), tmp));
0278     QFileInfo fi(file.fileName());
0279     QVERIFY(QFile::exists(tmp + fi.fileName() + '~'));
0280     QFile::remove(tmp + fi.fileName() + '~');
0281 }
0282 
0283 void KSaveFileTest::test_numberedBackupFile()
0284 {
0285     // Test absolute pathname file
0286     {
0287         QTemporaryFile file;
0288         QVERIFY(file.open());
0289 
0290         for (int i = 1; i < 15; i++) {
0291             QVERIFY(KBackup::numberedBackupFile(file.fileName()));
0292         }
0293 
0294         QString fileNameTemplate = file.fileName() + ".%1~";
0295         for (int i = 1; i <= 10; i++) {
0296             QVERIFY(QFile::exists(fileNameTemplate.arg(i)));
0297             filesToRemove << fileNameTemplate.arg(i);
0298         }
0299 
0300         QVERIFY(!QFile::exists(fileNameTemplate.arg(11)));
0301         QVERIFY(!QFile::exists(fileNameTemplate.arg(12)));
0302         QVERIFY(!QFile::exists(fileNameTemplate.arg(13)));
0303         QVERIFY(!QFile::exists(fileNameTemplate.arg(14)));
0304     }
0305 
0306     // Test current directory
0307     {
0308         QTemporaryFile file;
0309         QVERIFY(file.open());
0310 
0311         QFileInfo fi(file.fileName());
0312         QVERIFY(QDir::setCurrent(fi.absolutePath()));
0313 
0314         for (int i = 1; i < 15; i++) {
0315             QVERIFY(KBackup::numberedBackupFile(fi.fileName()));
0316         }
0317 
0318         QString fileNameTemplate = fi.fileName() + ".%1~";
0319         for (int i = 1; i <= 10; i++) {
0320             QVERIFY(QFile::exists(fileNameTemplate.arg(i)));
0321             filesToRemove << fileNameTemplate.arg(i);
0322         }
0323 
0324         QVERIFY(!QFile::exists(fileNameTemplate.arg(11)));
0325         QVERIFY(!QFile::exists(fileNameTemplate.arg(12)));
0326         QVERIFY(!QFile::exists(fileNameTemplate.arg(13)));
0327         QVERIFY(!QFile::exists(fileNameTemplate.arg(14)));
0328     }
0329 
0330     // Test absolute pathname file w/new directory
0331     {
0332         QTemporaryFile file;
0333         QVERIFY(file.open());
0334 
0335         QFileInfo fi(file.fileName());
0336         QVERIFY(QDir::setCurrent(fi.absolutePath()));
0337 
0338         for (int i = 1; i < 15; i++) {
0339             QVERIFY(KBackup::numberedBackupFile(fi.fileName(), tmp));
0340         }
0341 
0342         QString fileNameTemplate = tmp + fi.fileName() + ".%1~";
0343         for (int i = 1; i <= 10; i++) {
0344             QVERIFY(QFile::exists(fileNameTemplate.arg(i)));
0345             filesToRemove << fileNameTemplate.arg(i);
0346         }
0347 
0348         QVERIFY(!QFile::exists(fileNameTemplate.arg(11)));
0349         QVERIFY(!QFile::exists(fileNameTemplate.arg(12)));
0350         QVERIFY(!QFile::exists(fileNameTemplate.arg(13)));
0351         QVERIFY(!QFile::exists(fileNameTemplate.arg(14)));
0352     }
0353 
0354     // Test current directory w/new directory
0355     {
0356         QTemporaryFile file;
0357         QVERIFY(file.open());
0358 
0359         QFileInfo fi(file.fileName());
0360         QVERIFY(QDir::setCurrent(fi.absolutePath()));
0361 
0362         for (int i = 1; i < 15; i++) {
0363             QVERIFY(KBackup::numberedBackupFile(fi.fileName(), tmp));
0364         }
0365 
0366         QString fileNameTemplate = tmp + fi.fileName() + ".%1~";
0367         for (int i = 1; i <= 10; i++) {
0368             QVERIFY(QFile::exists(fileNameTemplate.arg(i)));
0369             filesToRemove << fileNameTemplate.arg(i);
0370         }
0371 
0372         QVERIFY(!QFile::exists(fileNameTemplate.arg(11)));
0373         QVERIFY(!QFile::exists(fileNameTemplate.arg(12)));
0374         QVERIFY(!QFile::exists(fileNameTemplate.arg(13)));
0375         QVERIFY(!QFile::exists(fileNameTemplate.arg(14)));
0376     }
0377 
0378 }
0379 
0380 void KSaveFileTest::test_rcsBackupFile()
0381 {
0382     QString cipath = QStandardPaths::findExecutable("ci");
0383     if (cipath.isEmpty()) {
0384         QSKIP("ci not available");
0385     }
0386 
0387     {
0388         QTemporaryFile f;
0389         QVERIFY(f.open());
0390 
0391         QVERIFY(KBackup::rcsBackupFile(f.fileName()));
0392         QVERIFY(QFile::exists(f.fileName() + ",v"));
0393         QVERIFY(KBackup::rcsBackupFile(f.fileName()));
0394         QVERIFY(QFile::exists(f.fileName() + ",v"));
0395 
0396         filesToRemove << f.fileName() + ",v";
0397     }
0398 
0399     {
0400         QTemporaryFile f;
0401         QVERIFY(f.open());
0402 
0403         QVERIFY(KBackup::rcsBackupFile(f.fileName()));
0404         QVERIFY(QFile::exists(f.fileName() + ",v"));
0405 
0406         QTextStream out(&f);
0407         out << "Testing a change\n";
0408         out.flush();
0409 
0410         QVERIFY(KBackup::rcsBackupFile(f.fileName()));
0411 
0412         out << "Testing another change\n";
0413         out.flush();
0414 
0415         QVERIFY(KBackup::rcsBackupFile(f.fileName()));
0416         filesToRemove << f.fileName() + ",v";
0417     }
0418 
0419     {
0420         QTemporaryFile f;
0421         QVERIFY(f.open());
0422 
0423         QFileInfo fi(f.fileName());
0424         QVERIFY(QDir::setCurrent(fi.absolutePath()));
0425 
0426         QVERIFY(KBackup::rcsBackupFile(fi.fileName()));
0427         QVERIFY(QFile::exists(f.fileName() + ",v"));
0428         QVERIFY(KBackup::rcsBackupFile(fi.fileName()));
0429         QVERIFY(QFile::exists(f.fileName() + ",v"));
0430 
0431         filesToRemove << f.fileName() + ",v";
0432     }
0433 
0434     {
0435         QTemporaryFile f;
0436         QVERIFY(f.open());
0437 
0438         QFileInfo fi(f.fileName());
0439 
0440         QVERIFY(KBackup::rcsBackupFile(f.fileName(), tmp));
0441         QVERIFY(QFile::exists(tmp + fi.fileName() + ",v"));
0442         QVERIFY(KBackup::rcsBackupFile(f.fileName(), tmp));
0443         QVERIFY(QFile::exists(tmp + fi.fileName() + ",v"));
0444 
0445         filesToRemove << tmp + fi.fileName() + ",v";
0446     }
0447 
0448     {
0449         QTemporaryFile f;
0450         QVERIFY(f.open());
0451 
0452         QFileInfo fi(f.fileName());
0453         QVERIFY(QDir::setCurrent(fi.absolutePath()));
0454 
0455         QVERIFY(KBackup::rcsBackupFile(fi.fileName(), tmp));
0456         QVERIFY(QFile::exists(tmp + fi.fileName() + ",v"));
0457         QVERIFY(KBackup::rcsBackupFile(fi.fileName(), tmp));
0458         QVERIFY(QFile::exists(tmp + fi.fileName() + ",v"));
0459 
0460         filesToRemove << tmp + fi.fileName() + ",v";
0461     }
0462 }
0463 
0464 void KSaveFileTest::cleanupTestCase()
0465 {
0466     foreach (const QString &fileToRemove, filesToRemove) {
0467         QFile::remove(fileToRemove);
0468     }
0469 }