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

0001 /*
0002     This file is part of the KDE Baloo project.
0003     SPDX-FileCopyrightText: 2011 Sebastian Trueg <trueg@kde.org>
0004     SPDX-FileCopyrightText: 2013-2014 Vishesh Handa <vhanda@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "pendingfilequeue.h"
0010 
0011 #include <QTest>
0012 #include <QSignalSpy>
0013 #include <qtimer.h>
0014 
0015 namespace Baloo {
0016 
0017 class PendingFileQueueTest : public QObject
0018 {
0019     Q_OBJECT
0020 
0021 public:
0022     PendingFileQueueTest();
0023 
0024 private Q_SLOTS:
0025     void testTimers();
0026     void testTimeout();
0027     void testRequeue();
0028     void testDeleteCreate();
0029     void testCreateDelete();
0030     void testCreateDelete2();
0031 };
0032 
0033 PendingFileQueueTest::PendingFileQueueTest()
0034 {
0035 }
0036 
0037 class TimerEventEater : public QObject
0038 {
0039     Q_OBJECT
0040 
0041 public:
0042     TimerEventEater(QObject* parent = nullptr) : QObject(parent) {}
0043 
0044 protected:
0045     bool eventFilter(QObject *object, QEvent *event) override {
0046         Q_UNUSED(object);
0047         Q_UNUSED(event);
0048         return true;
0049     }
0050 };
0051 
0052 void PendingFileQueueTest::testTimers()
0053 {
0054     QString myUrl(QStringLiteral("/tmp"));
0055 
0056     PendingFileQueue queue;
0057     queue.setMinimumTimeout(1);
0058     queue.setTrackingTime(1);
0059 
0060     QSignalSpy spy(&queue, SIGNAL(indexModifiedFile(QString)));
0061     QVERIFY(spy.isValid());
0062 
0063     PendingFile file(myUrl);
0064     file.setModified();
0065     queue.enqueue(file);
0066 
0067     // The signal should be emitted immediately
0068     QVERIFY(spy.wait(50));
0069     QCOMPARE(spy.count(), 1);
0070     QCOMPARE(spy.takeFirst().constFirst().toString(), myUrl);
0071     QCOMPARE(queue.m_recentlyEmitted.count(), 1);
0072 
0073     // Enqueue the url again. This time it should wait for should wait for the
0074     // minimumTimeout
0075     queue.enqueue(file);
0076 
0077     QTest::qWait(100);
0078     QCOMPARE(queue.m_pendingFiles.count(), 1);
0079     QCOMPARE(queue.m_recentlyEmitted.count(), 1);
0080     QVERIFY(spy.isEmpty());
0081 
0082     QVERIFY(spy.wait(1500));
0083     QCOMPARE(spy.count(), 1);
0084     QCOMPARE(spy.takeFirst().constFirst().toString(), myUrl);
0085     QCOMPARE(queue.m_pendingFiles.count(), 0);
0086     QCOMPARE(queue.m_recentlyEmitted.count(), 1);
0087 
0088     // Wait for Tracking Time
0089     QTest::qWait(1500);
0090     QCOMPARE(queue.m_recentlyEmitted.count(), 0);
0091 }
0092 
0093 void PendingFileQueueTest::testTimeout()
0094 {
0095     QString file1Url(QStringLiteral("file1_url"));
0096     QString file2Url(QStringLiteral("file2_url"));
0097 
0098     QTime currentTime = QTime::currentTime();
0099 
0100     PendingFileQueue queue;
0101     queue.setMinimumTimeout(2);
0102 
0103     auto eventEater = new TimerEventEater(this);
0104     queue.m_cacheTimer.installEventFilter(eventEater);
0105     queue.m_clearRecentlyEmittedTimer.installEventFilter(eventEater);
0106     queue.m_pendingFilesTimer.installEventFilter(eventEater);
0107 
0108     QSignalSpy spy(&queue, SIGNAL(indexModifiedFile(QString)));
0109     QVERIFY(spy.isValid());
0110 
0111     PendingFile file1(file1Url);
0112     PendingFile file2(file2Url);
0113     file1.setModified();
0114     file2.setModified();
0115 
0116     // Enqueue, and process the event queue
0117     queue.enqueue(file1);
0118     QTimer::singleShot(0, [&queue, currentTime] { queue.processCache(currentTime); });
0119 
0120     // The signal should be emitted immediately
0121     QVERIFY(spy.wait());
0122     QCOMPARE(spy.count(), 1);
0123     QCOMPARE(spy.takeFirst().constFirst().toString(), file1Url);
0124 
0125     // Enqueue file1 again, and also file2. This time, only file2
0126     // should be signaled immediately
0127     queue.enqueue(file1);
0128     queue.enqueue(file2);
0129     QTimer::singleShot(0, [&queue, currentTime] { queue.processCache(currentTime); });
0130 
0131     QVERIFY(spy.wait(50));
0132     QCOMPARE(spy.count(), 1);
0133     QCOMPARE(spy.takeFirst().constFirst().toString(), file2Url);
0134 
0135     // Advance time 1.5 seconds, and let the pending queue be processed.
0136     // Nothing should be signaled, as the timeout is 2 seconds
0137     currentTime = currentTime.addMSecs(1500);
0138     QTimer::singleShot(0, [&queue, currentTime] { queue.processPendingFiles(currentTime); });
0139     QVERIFY(!spy.wait(50));
0140 
0141     currentTime = currentTime.addMSecs(1000);
0142     QTimer::singleShot(0, [&queue, currentTime] { queue.processPendingFiles(currentTime); });
0143     QVERIFY(spy.wait(0));
0144     QCOMPARE(spy.count(), 1);
0145     QCOMPARE(spy.takeFirst().constFirst().toString(), file1Url);
0146 }
0147 
0148 void PendingFileQueueTest::testRequeue()
0149 {
0150     QString myUrl(QStringLiteral("/tmp"));
0151 
0152     QTime currentTime = QTime::currentTime();
0153 
0154     PendingFileQueue queue;
0155     queue.setMinimumTimeout(2);
0156     queue.setMaximumTimeout(5);
0157 
0158     auto eventEater = new TimerEventEater(this);
0159     queue.m_cacheTimer.installEventFilter(eventEater);
0160     queue.m_clearRecentlyEmittedTimer.installEventFilter(eventEater);
0161     queue.m_pendingFilesTimer.installEventFilter(eventEater);
0162 
0163     QSignalSpy spy(&queue, SIGNAL(indexModifiedFile(QString)));
0164     QVERIFY(spy.isValid());
0165 
0166     PendingFile file(myUrl);
0167     file.setModified();
0168     queue.enqueue(file);
0169     QTimer::singleShot(0, [&queue, currentTime] { queue.processCache(currentTime); });
0170 
0171     // The signal should be emitted immediately
0172     QVERIFY(spy.wait());
0173     QCOMPARE(spy.count(), 1);
0174     QCOMPARE(spy.takeFirst().constFirst().toString(), myUrl);
0175 
0176     // Send many events. The first one should enqueue it with the minimumTimeout, and each
0177     // successive one should double the timeout up to maxTimeout
0178     for (int i = 0; i < 3; i++) {
0179         queue.enqueue(file);
0180         currentTime = currentTime.addMSecs(20);
0181         QTimer::singleShot(0, [&queue, currentTime] { queue.processCache(currentTime); });
0182         spy.wait(0);
0183     }
0184 
0185     // Signal should be emitted after 5 seconds (min(2 * 2 * 2, maxTimeout))
0186     int elapsed10thSeconds = 0;
0187     while (true) {
0188         currentTime = currentTime.addMSecs(100);
0189         QTimer::singleShot(0, [&queue, currentTime] { queue.processPendingFiles(currentTime); });
0190         if (spy.wait(0)) {
0191             break;
0192         }
0193         QVERIFY2(elapsed10thSeconds <= 50, "Signal emitted late");
0194         elapsed10thSeconds++;
0195     }
0196     QVERIFY2(elapsed10thSeconds > 40, "Signal emitted early");
0197 
0198     QCOMPARE(spy.count(), 1);
0199     QCOMPARE(spy.takeFirst().constFirst().toString(), myUrl);
0200 }
0201 
0202 void PendingFileQueueTest::testDeleteCreate()
0203 {
0204     QString myUrl(QStringLiteral("/dir/testfile"));
0205 
0206     QTime currentTime = QTime::currentTime();
0207 
0208     PendingFileQueue queue;
0209 
0210     auto eventEater = new TimerEventEater(this);
0211     queue.m_cacheTimer.installEventFilter(eventEater);
0212     queue.m_clearRecentlyEmittedTimer.installEventFilter(eventEater);
0213     queue.m_pendingFilesTimer.installEventFilter(eventEater);
0214 
0215     QSignalSpy spyModified(&queue, SIGNAL(indexModifiedFile(QString)));
0216     QSignalSpy spyRemoved(&queue, SIGNAL(removeFileIndex(QString)));
0217     QVERIFY(spyModified.isValid());
0218     QVERIFY(spyRemoved.isValid());
0219 
0220     PendingFile file1(myUrl);
0221     PendingFile file2(myUrl);
0222     file1.setDeleted();
0223     file2.setModified();
0224 
0225     queue.enqueue(file1);
0226     queue.enqueue(file2);
0227     QTimer::singleShot(0, [&queue, currentTime] { queue.processCache(currentTime); });
0228 
0229     // The signals should be emitted immediately
0230     QVERIFY(spyModified.wait());
0231 
0232     QCOMPARE(spyRemoved.count(), 1);
0233     QCOMPARE(spyRemoved.takeFirst().constFirst().toString(), myUrl);
0234 
0235     QCOMPARE(spyModified.count(), 1);
0236     QCOMPARE(spyModified.takeFirst().constFirst().toString(), myUrl);
0237 }
0238 
0239 void PendingFileQueueTest::testCreateDelete()
0240 {
0241     QString myUrl(QStringLiteral("/dir/testfile"));
0242 
0243     QTime currentTime = QTime::currentTime();
0244 
0245     PendingFileQueue queue;
0246 
0247     auto eventEater = new TimerEventEater(this);
0248     queue.m_cacheTimer.installEventFilter(eventEater);
0249     queue.m_clearRecentlyEmittedTimer.installEventFilter(eventEater);
0250     queue.m_pendingFilesTimer.installEventFilter(eventEater);
0251 
0252     QSignalSpy spyModified(&queue, SIGNAL(indexModifiedFile(QString)));
0253     QSignalSpy spyRemoved(&queue, SIGNAL(removeFileIndex(QString)));
0254     QVERIFY(spyModified.isValid());
0255     QVERIFY(spyRemoved.isValid());
0256 
0257     // Actually same file, just different events
0258     PendingFile file_modified(myUrl);
0259     PendingFile file_delete(myUrl);
0260     file_modified.setModified();
0261     file_delete.setDeleted();
0262 
0263     QTimer::singleShot(0, [&] {
0264         queue.enqueue(file_modified);
0265         queue.enqueue(file_delete);
0266     });
0267     QTimer::singleShot(0, [&queue, currentTime] { queue.processCache(currentTime); });
0268 
0269     // The Removed signal should be emitted immediately
0270     QVERIFY(spyRemoved.wait());
0271 
0272     // The Modified signal should be suppressed
0273     QCOMPARE(spyModified.count(), 0);
0274 
0275     QCOMPARE(spyRemoved.count(), 1);
0276     QCOMPARE(spyRemoved.takeFirst().constFirst().toString(), myUrl);
0277 }
0278 
0279 void PendingFileQueueTest::testCreateDelete2()
0280 {
0281     QString myUrl(QStringLiteral("/dir/testfile"));
0282 
0283     QTime currentTime = QTime::currentTime();
0284 
0285     PendingFileQueue queue;
0286 
0287     auto eventEater = new TimerEventEater(this);
0288     queue.m_cacheTimer.installEventFilter(eventEater);
0289     queue.m_clearRecentlyEmittedTimer.installEventFilter(eventEater);
0290     queue.m_pendingFilesTimer.installEventFilter(eventEater);
0291 
0292     QSignalSpy spyModified(&queue, SIGNAL(indexModifiedFile(QString)));
0293     QSignalSpy spyRemoved(&queue, SIGNAL(removeFileIndex(QString)));
0294     QVERIFY(spyModified.isValid());
0295     QVERIFY(spyRemoved.isValid());
0296 
0297     PendingFile file_modified(myUrl);
0298     PendingFile file_delete(myUrl);
0299     file_modified.setModified();
0300     file_delete.setDeleted();
0301 
0302     // Prime the recent files list
0303     queue.enqueue(file_modified);
0304     QTimer::singleShot(0, [&queue, currentTime] { queue.processCache(currentTime); });
0305     QVERIFY(spyModified.wait());
0306     QCOMPARE(spyModified.count(), 1);
0307     QCOMPARE(spyModified.takeFirst().constFirst().toString(), myUrl);
0308     QCOMPARE(queue.m_recentlyEmitted.count(), 1);
0309 
0310     // Modify the file again
0311     QTimer::singleShot(0, [&] { queue.enqueue(file_modified); });
0312     QTimer::singleShot(0, [&queue, currentTime] { queue.processCache(currentTime); });
0313     // Process the timer, the file should be pending now
0314     QTest::qWait(10);
0315     QCOMPARE(queue.m_pendingFiles.count(), 1);
0316 
0317     // Let 5 seconds pass (minimum pending timeout)
0318     currentTime = currentTime.addMSecs(5000);
0319     QTimer::singleShot(0, [&] { queue.enqueue(file_delete); });
0320     // The "process" timer fires 10ms after the enqueue, plenty of time for the pending timer to fire
0321     QTimer::singleShot(0, [&queue, currentTime] { queue.processPendingFiles(currentTime); });
0322     currentTime = currentTime.addMSecs(10);
0323     QTimer::singleShot(0, [&queue, currentTime] { queue.processCache(currentTime); });
0324 
0325     // The Removed signal should be emitted immediately
0326     QVERIFY(spyRemoved.wait());
0327     QCOMPARE(spyRemoved.count(), 1);
0328     QCOMPARE(spyRemoved.takeFirst().constFirst().toString(), myUrl);
0329 
0330     // The Modified signal should be suppressed, the file no longer be pending
0331     QCOMPARE(spyModified.count(), 0);
0332     QCOMPARE(queue.m_pendingFiles.count(), 0);
0333 }
0334 
0335 } // namespace
0336 
0337 QTEST_GUILESS_MAIN(Baloo::PendingFileQueueTest)
0338 
0339 #include "pendingfilequeuetest.moc"