File indexing completed on 2024-04-28 03:51:41

0001 /*
0002     SPDX-FileCopyrightText: 2010 Sebastian Trueg <trueg at kde.org>
0003     SPDX-FileCopyrightText: 2014 Vishesh Handa <vhanda@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "kinotify.h"
0009 
0010 #include <QTemporaryDir>
0011 #include <KRandom>
0012 
0013 #include <QTextStream>
0014 #include <QFile>
0015 #include <QSignalSpy>
0016 #include <QDir>
0017 #include <QTest>
0018 
0019 #include <stdio.h>
0020 
0021 class KInotifyTest : public QObject
0022 {
0023     Q_OBJECT
0024 
0025 private Q_SLOTS:
0026     void testDeleteFile();
0027     void testDeleteFolder();
0028     void testCreateFolder();
0029     void testRenameFile();
0030     void testRenameDeleteFile();
0031     void testMoveFile();
0032     void testRenameFolder();
0033     void testMoveFolder();
0034     void testMoveFromUnwatchedFolder();
0035     void testMoveToUnwatchedFolder();
0036     void testMoveRootFolder();
0037     void testFileClosedAfterWrite();
0038 
0039     void init();
0040 
0041 private:
0042     std::unique_ptr<QTemporaryDir> m_tmpDir;
0043     QDir m_dir;
0044     std::unique_ptr<KInotify> m_kn;
0045 };
0046 
0047 namespace
0048 {
0049 void touchFile(const QString& path)
0050 {
0051     QFile file(path);
0052     file.open(QIODevice::WriteOnly);
0053     QTextStream s(&file);
0054     s << KRandom::randomString(10);
0055 }
0056 
0057 void mkdir(const QString& path)
0058 {
0059     QDir().mkpath(path);
0060     QVERIFY(QDir(path).exists());
0061 }
0062 }
0063 
0064 void KInotifyTest::init()
0065 {
0066     m_tmpDir = std::make_unique<QTemporaryDir>();
0067 
0068     const QString subDir(QStringLiteral("%1/subdir/").arg(m_tmpDir->path()));
0069     mkdir(subDir);
0070     m_dir = QDir(subDir);
0071 
0072     m_kn = std::make_unique<KInotify>(nullptr);
0073     QVERIFY(m_kn->addWatch(m_dir.path(), KInotify::EventAll));
0074 
0075     QSignalSpy kiSpy(m_kn.get(), &KInotify::installedWatches);
0076     QVERIFY(kiSpy.count() || kiSpy.wait());
0077 }
0078 
0079 void KInotifyTest::testDeleteFile()
0080 {
0081     // create some test files
0082     const QString f1(QStringLiteral("%1/randomJunk1").arg(m_dir.path()));
0083     touchFile(f1);
0084 
0085     // listen to the desired signal
0086     QSignalSpy spy(m_kn.get(), &KInotify::deleted);
0087 
0088     // test removing a file
0089     QFile::remove(f1);
0090     QVERIFY(spy.wait());
0091     QCOMPARE(spy.count(), 1);
0092     QCOMPARE(spy.takeFirst().at(0).toString(), f1);
0093 }
0094 
0095 void KInotifyTest::testDeleteFolder()
0096 {
0097     const QString d1(QStringLiteral("%1/").arg(m_dir.path()));
0098 
0099     // listen to the desired signal
0100     QSignalSpy spy(m_kn.get(), &KInotify::deleted);
0101 
0102     // test removing a folder
0103     QVERIFY(QDir().rmdir(d1));
0104     QVERIFY(spy.wait());
0105     QCOMPARE(spy.count(), 1);
0106     QCOMPARE(spy.first().at(0).toString(), d1);
0107     QCOMPARE(spy.first().at(1).toBool(), true);
0108     // make sure we do not watch the removed folder anymore
0109     QVERIFY(!m_kn->watchingPath(d1));
0110 }
0111 
0112 void KInotifyTest::testCreateFolder()
0113 {
0114     // listen to the desired signal
0115     QSignalSpy createdSpy(m_kn.get(), &KInotify::created);
0116 
0117     // create the subdir
0118     const QString d1(QStringLiteral("%1/randomJunk1/").arg(m_dir.path()));
0119     mkdir(d1);
0120     QVERIFY(createdSpy.wait());
0121     QCOMPARE(createdSpy.count(), 1);
0122     QCOMPARE(createdSpy.takeFirst().at(0).toString(), d1);
0123     QVERIFY(m_kn->watchingPath(d1));
0124 
0125     // lets go one level deeper
0126     const QString d2 = QStringLiteral("%1subdir1/").arg(d1);
0127     mkdir(d2);
0128     QVERIFY(createdSpy.wait());
0129     QCOMPARE(createdSpy.count(), 1);
0130     QCOMPARE(createdSpy.takeFirst().at(0).toString(), d2);
0131     QVERIFY(m_kn->watchingPath(d2));
0132 
0133     // although we are in the folder test lets try creating a file
0134     const QString f1 = QStringLiteral("%1somefile1").arg(d2);
0135     touchFile(f1);
0136     QVERIFY(createdSpy.wait());
0137     QCOMPARE(createdSpy.count(), 1);
0138     QCOMPARE(createdSpy.takeFirst().at(0).toString(), f1);
0139 }
0140 
0141 void KInotifyTest::testRenameFile()
0142 {
0143     // create some test files
0144     const QString f1(QStringLiteral("%1/randomJunk1").arg(m_dir.path()));
0145     touchFile(f1);
0146 
0147     // listen to the desired signal
0148     QSignalSpy spy(m_kn.get(), &KInotify::moved);
0149 
0150     // actually move the file
0151     const QString f2(QStringLiteral("%1/randomJunk2").arg(m_dir.path()));
0152     rename(f1.toLocal8Bit().constData(), f2.toLocal8Bit().constData());
0153 
0154     // check the desired signal
0155     QVERIFY(spy.wait());
0156     QCOMPARE(spy.count(), 1);
0157     QList<QVariant> args = spy.takeFirst();
0158     QCOMPARE(args.at(0).toString(), f1);
0159     QCOMPARE(args.at(1).toString(), f2);
0160 
0161     // test a subsequent rename
0162     const QString f3(QStringLiteral("%1/randomJunk3").arg(m_dir.path()));
0163     rename(f2.toLocal8Bit().constData(), f3.toLocal8Bit().constData());
0164 
0165     // check the desired signal
0166     QVERIFY(spy.wait());
0167     QCOMPARE(spy.count(), 1);
0168     args = spy.takeFirst();
0169     QCOMPARE(args.at(0).toString(), f2);
0170     QCOMPARE(args.at(1).toString(), f3);
0171 }
0172 
0173 void KInotifyTest::testRenameDeleteFile()
0174 {
0175     // create some test files
0176     const QString f1(QStringLiteral("%1/randomJunk1").arg(m_dir.path()));
0177     touchFile(f1);
0178 
0179     // listen to the desired signal
0180     QSignalSpy spy(m_kn.get(), &KInotify::moved);
0181 
0182     // actually move the file
0183     const QString f2(QStringLiteral("%1/randomJunk2").arg(m_dir.path()));
0184     rename(f1.toLocal8Bit().constData(), f2.toLocal8Bit().constData());
0185 
0186     // check the desired signal
0187     QVERIFY(spy.wait());
0188     QCOMPARE(spy.count(), 1);
0189     QList<QVariant> args = spy.takeFirst();
0190     QCOMPARE(args.at(0).toString(), f1);
0191     QCOMPARE(args.at(1).toString(), f2);
0192 
0193     // test a subsequent delete
0194     QSignalSpy spy1(m_kn.get(), &KInotify::deleted);
0195     QFile::remove(f2);
0196 
0197     // check the desired signal
0198     QVERIFY(spy1.wait());
0199     QCOMPARE(spy1.count(), 1);
0200     QCOMPARE(spy1.takeFirst().at(0).toString(), f2);
0201 
0202     QVERIFY(spy.isEmpty());
0203 }
0204 
0205 void KInotifyTest::testMoveFile()
0206 {
0207     // create some test files
0208     QTemporaryDir destDir;
0209     const QString src(QStringLiteral("%1/randomJunk1").arg(m_dir.path()));
0210     const QString dest(QStringLiteral("%1/randomJunk2").arg(destDir.path()));
0211     touchFile(src);
0212 
0213     // Add another path without common parent to the watcher
0214     QSignalSpy knSpy(m_kn.get(), &KInotify::installedWatches);
0215     m_kn->addWatch(destDir.path(), KInotify::EventAll);
0216     QVERIFY(knSpy.count() || knSpy.wait());
0217 
0218     // listen to the desired signal
0219     QSignalSpy spy(m_kn.get(), &KInotify::moved);
0220 
0221     // actually move the file
0222     rename(src.toLocal8Bit().constData(), dest.toLocal8Bit().constData());
0223 
0224     // check the desired signal
0225     QVERIFY(spy.wait());
0226     QCOMPARE(spy.count(), 1);
0227     QList<QVariant> args = spy.takeFirst();
0228     QCOMPARE(args.at(0).toString(), src);
0229     QCOMPARE(args.at(1).toString(), dest);
0230 
0231     // test a subsequent move (back to the original folder)
0232     const QString dest2(QStringLiteral("%1/randomJunk3").arg(m_dir.path()));
0233     rename(dest.toLocal8Bit().constData(), dest2.toLocal8Bit().constData());
0234 
0235     // check the desired signal
0236     QVERIFY(spy.wait());
0237     QCOMPARE(spy.count(), 1);
0238     args = spy.takeFirst();
0239     QCOMPARE(args.at(0).toString(), dest);
0240     QCOMPARE(args.at(1).toString(), dest2);
0241 }
0242 
0243 void KInotifyTest::testRenameFolder()
0244 {
0245     // create some test files
0246     const QString d1(QStringLiteral("%1/randomJunk1/").arg(m_dir.path()));
0247     mkdir(d1);
0248 
0249     {
0250     QSignalSpy spy(m_kn.get(), &KInotify::created);
0251     QVERIFY(spy.wait());
0252     }
0253 
0254     // listen to the desired signal
0255     QSignalSpy spy(m_kn.get(), &KInotify::moved);
0256 
0257     // actually rename the folder
0258     const QString d2(QStringLiteral("%1/randomJunk2/").arg(m_dir.path()));
0259     rename(d1.toLocal8Bit().constData(), d2.toLocal8Bit().constData());
0260 
0261     // check the desired signal
0262     QVERIFY(spy.wait());
0263     QCOMPARE(spy.count(), 1);
0264     QList<QVariant> args = spy.takeFirst();
0265     QCOMPARE(args.at(0).toString(), d1);
0266     QCOMPARE(args.at(1).toString(), d2);
0267 
0268     // check the path cache
0269     QVERIFY(!m_kn->watchingPath(d1));
0270     QVERIFY(m_kn->watchingPath(d2));
0271 
0272     // test a subsequent rename
0273     const QString d3(QStringLiteral("%1/randomJunk3/").arg(m_dir.path()));
0274     rename(d2.toLocal8Bit().constData(), d3.toLocal8Bit().constData());
0275 
0276     // check the desired signal
0277     QVERIFY(spy.wait());
0278     QCOMPARE(spy.count(), 1);
0279     args = spy.takeFirst();
0280     QCOMPARE(args.at(0).toString(), d2);
0281     QCOMPARE(args.at(1).toString(), d3);
0282 
0283     // check the path cache
0284     QVERIFY(!m_kn->watchingPath(d1));
0285     QVERIFY(!m_kn->watchingPath(d2));
0286     QVERIFY(m_kn->watchingPath(d3));
0287 
0288     // KInotify claims it has updated its data structures, lets see if that is true
0289     // by creating a file in the new folder
0290     // listen to the desired signal
0291     const QString f4(QStringLiteral("%1somefile").arg(d3));
0292 
0293     QSignalSpy createdSpy(m_kn.get(), &KInotify::created);
0294 
0295     // test creating a file
0296     touchFile(f4);
0297 
0298     QVERIFY(createdSpy.wait());
0299     QCOMPARE(createdSpy.count(), 1);
0300     QCOMPARE(createdSpy.takeFirst().at(0).toString(), f4);
0301 }
0302 
0303 void KInotifyTest::testMoveFolder()
0304 {
0305     // create some test files
0306     QTemporaryDir destDir;
0307     const QString src(QStringLiteral("%1/randomJunk1/").arg(m_dir.path()));
0308     const QString dest(QStringLiteral("%1/randomJunk2/").arg(destDir.path()));
0309     mkdir(src);
0310 
0311     // Add another path without common parent to the watcher
0312     QSignalSpy knSpy(m_kn.get(), &KInotify::installedWatches);
0313     m_kn->addWatch(destDir.path(), KInotify::EventAll);
0314     QVERIFY(knSpy.count() || knSpy.wait());
0315 
0316     // listen to the desired signal
0317     QSignalSpy spy(m_kn.get(), &KInotify::moved);
0318 
0319     // actually move the file
0320     rename(src.toLocal8Bit().constData(), dest.toLocal8Bit().constData());
0321 
0322     // check the desired signal
0323     QVERIFY(spy.wait());
0324     QCOMPARE(spy.count(), 1);
0325     QList<QVariant> args = spy.takeFirst();
0326     QCOMPARE(args.at(0).toString(), src);
0327     QCOMPARE(args.at(1).toString(), dest);
0328 
0329     // check the path cache
0330     QVERIFY(!m_kn->watchingPath(src));
0331     QVERIFY(m_kn->watchingPath(dest));
0332 
0333     // test a subsequent move
0334     const QString dest2(QStringLiteral("%1/randomJunk3/").arg(m_dir.path()));
0335     rename(dest.toLocal8Bit().constData(), dest2.toLocal8Bit().constData());
0336 
0337     // check the desired signal
0338     QVERIFY(spy.wait());
0339     QCOMPARE(spy.count(), 1);
0340     args = spy.takeFirst();
0341     QCOMPARE(args.at(0).toString(), dest);
0342     QCOMPARE(args.at(1).toString(), dest2);
0343 
0344     // check the path cache
0345     QVERIFY(!m_kn->watchingPath(src));
0346     QVERIFY(!m_kn->watchingPath(dest));
0347     QVERIFY(m_kn->watchingPath(dest2));
0348 
0349     // KInotify claims it has updated its data structures, lets see if that is true
0350     // by creating a file in the new folder
0351     // listen to the desired signal
0352     const QString f4(QStringLiteral("%1somefile").arg(dest2));
0353 
0354     QSignalSpy createdSpy(m_kn.get(), &KInotify::created);
0355 
0356     // test creating a file
0357     touchFile(f4);
0358 
0359     QVERIFY(createdSpy.wait());
0360     QCOMPARE(createdSpy.count(), 1);
0361     QCOMPARE(createdSpy.takeFirst().at(0).toString(), f4);
0362 }
0363 
0364 void KInotifyTest::testMoveFromUnwatchedFolder()
0365 {
0366     // create unwatched source folder
0367     QTemporaryDir srcDir;
0368     const QString src{srcDir.path()};
0369     const QString dest{m_dir.path()};
0370 
0371     QSignalSpy spy(m_kn.get(), &KInotify::created);
0372 
0373     // Create stuff inside src
0374     mkdir(QStringLiteral("%1/sub").arg(src));
0375     touchFile(QStringLiteral("%1/sub/file1").arg(src));
0376     mkdir(QStringLiteral("%1/sub/sub1").arg(src));
0377     touchFile(QStringLiteral("%1/sub/sub1/file2").arg(src));
0378 
0379     // Now move
0380     QFile::rename(QStringLiteral("%1/sub").arg(src),
0381             QStringLiteral("%1/sub").arg(dest));
0382 
0383     QVERIFY(spy.wait());
0384     QCOMPARE(spy.count(), 4);
0385 
0386     // Checking if watches are installed
0387     QSignalSpy spy1(m_kn.get(), &KInotify::deleted);
0388     QDir dstdir(QStringLiteral("%1/sub").arg(dest));
0389     dstdir.removeRecursively();
0390 
0391     QVERIFY(spy1.wait());
0392     QCOMPARE(spy1.count(), 4);
0393 }
0394 
0395 void KInotifyTest::testMoveToUnwatchedFolder()
0396 {
0397     // create unwatched destination folder
0398     QTemporaryDir destDir;
0399     const QString src{m_dir.path()};
0400     const QString dest{destDir.path()};
0401 
0402     QSignalSpy spy(m_kn.get(), &KInotify::created);
0403 
0404     // Create stuff inside src
0405     mkdir(QStringLiteral("%1/sub").arg(src));
0406     touchFile(QStringLiteral("%1/sub/file1").arg(src));
0407     touchFile(QStringLiteral("%1/file2").arg(src));
0408 
0409     QVERIFY(spy.wait());
0410     QCOMPARE(spy.count(), 3);
0411 
0412     // Move file
0413     QFile::rename(QStringLiteral("%1/file2").arg(src),
0414             QStringLiteral("%1/file2").arg(dest));
0415 
0416     QSignalSpy spy1(m_kn.get(), &KInotify::deleted);
0417     QVERIFY(spy1.wait());
0418     QCOMPARE(spy1.count(), 1);
0419 
0420     // Move dir
0421     QFile::rename(QStringLiteral("%1/sub").arg(src),
0422             QStringLiteral("%1/sub").arg(dest));
0423 
0424     QVERIFY(spy1.wait());
0425     QCOMPARE(spy1.count(), 2);
0426 }
0427 
0428 void KInotifyTest::testMoveRootFolder()
0429 {
0430     // listen for the moved signal
0431     QSignalSpy spy(m_kn.get(), &KInotify::moved);
0432 
0433     // Rename the watched root directory
0434     const QString src(QStringLiteral("%1/").arg(m_dir.path()));
0435     const QString dest(QStringLiteral("%1/newname/").arg(m_tmpDir->path()));
0436     QFile::rename(src, dest);
0437 
0438     // check the desired signal
0439     QEXPECT_FAIL("", "KInotify cannot handle moving of top-level folders.", Abort);
0440     QVERIFY(spy.wait(500));
0441     QCOMPARE(spy.count(), 1);
0442     QList<QVariant> args = spy.takeFirst();
0443     QCOMPARE(args.at(0).toString(), src);
0444     QCOMPARE(args.at(1).toString(), dest);
0445 
0446     // check the path cache
0447     QVERIFY(!m_kn->watchingPath(src));
0448     QVERIFY(m_kn->watchingPath(dest));
0449 }
0450 
0451 void KInotifyTest::testFileClosedAfterWrite()
0452 {
0453     QSignalSpy spy(m_kn.get(), &KInotify::closedWrite);
0454     touchFile(m_dir.path() + QLatin1String("/file"));
0455 
0456     QVERIFY(spy.wait());
0457     QCOMPARE(spy.count(), 1);
0458     QCOMPARE(spy.at(0).first().toString(), QString(m_dir.path() + QLatin1String("/file")));
0459 }
0460 
0461 QTEST_GUILESS_MAIN(KInotifyTest)
0462 
0463 #include "kinotifytest.moc"