File indexing completed on 2024-11-10 04:40:14

0001 /*
0002     SPDX-FileCopyrightText: 2011 Stephen Kelly <steveire@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "monitor.h"
0008 
0009 #include "akonaditestfake_export.h"
0010 #include "fakeserverdata.h"
0011 #include "fakesession.h"
0012 #include "inspectablechangerecorder.h"
0013 #include "inspectablemonitor.h"
0014 #include <QSignalSpy>
0015 #include <QTest>
0016 
0017 using namespace Akonadi;
0018 
0019 class MonitorNotificationTest : public QObject
0020 {
0021     Q_OBJECT
0022 public:
0023     explicit MonitorNotificationTest(QObject *parent = nullptr)
0024         : QObject(parent)
0025         , m_sessionName("MonitorNotificationTest fake session")
0026     {
0027         m_fakeSession = new FakeSession(m_sessionName, FakeSession::EndJobsImmediately);
0028         m_fakeSession->setAsDefaultSession();
0029     }
0030 
0031     ~MonitorNotificationTest() override
0032     {
0033         delete m_fakeSession;
0034     }
0035 
0036 private Q_SLOTS:
0037     void testSingleMessage();
0038     void testFillPipeline();
0039     void testMonitor();
0040 
0041     void testSingleMessage_data();
0042     void testFillPipeline_data();
0043     void testMonitor_data();
0044 
0045 private:
0046     template<typename MonitorImpl>
0047     void testSingleMessage_impl(MonitorImpl *monitor, FakeCollectionCache *collectionCache, FakeItemCache *itemCache);
0048     template<typename MonitorImpl>
0049     void testFillPipeline_impl(MonitorImpl *monitor, FakeCollectionCache *collectionCache, FakeItemCache *itemCache);
0050     template<typename MonitorImpl>
0051     void testMonitor_impl(MonitorImpl *monitor, FakeCollectionCache *collectionCache, FakeItemCache *itemCache);
0052 
0053 private:
0054     FakeSession *m_fakeSession = nullptr;
0055     QByteArray m_sessionName;
0056 };
0057 
0058 void MonitorNotificationTest::testSingleMessage_data()
0059 {
0060     QTest::addColumn<bool>("useChangeRecorder");
0061 
0062     QTest::newRow("useChangeRecorder") << true;
0063     QTest::newRow("useMonitor") << false;
0064 }
0065 
0066 void MonitorNotificationTest::testSingleMessage()
0067 {
0068     QFETCH(bool, useChangeRecorder);
0069 
0070     auto collectionCache = new FakeCollectionCache(m_fakeSession);
0071     FakeItemCache itemCache(m_fakeSession);
0072     auto depsFactory = new FakeMonitorDependenciesFactory(&itemCache, collectionCache);
0073 
0074     if (!useChangeRecorder) {
0075         testSingleMessage_impl(new InspectableMonitor(depsFactory, this), collectionCache, &itemCache);
0076     } else {
0077         auto changeRecorder = new InspectableChangeRecorder(depsFactory, this);
0078         changeRecorder->setChangeRecordingEnabled(false);
0079         testSingleMessage_impl(changeRecorder, collectionCache, &itemCache);
0080     }
0081 }
0082 
0083 template<typename MonitorImpl>
0084 void MonitorNotificationTest::testSingleMessage_impl(MonitorImpl *monitor, FakeCollectionCache *collectionCache, FakeItemCache *itemCache)
0085 {
0086     Q_UNUSED(itemCache)
0087 
0088     // Workaround for the QTimer::singleShot() in fake monitors to happen
0089     QTest::qWait(10);
0090 
0091     monitor->setSession(m_fakeSession);
0092     monitor->fetchCollection(true);
0093 
0094     Protocol::ChangeNotificationList list;
0095 
0096     Collection parent(1);
0097     Collection added(2);
0098 
0099     auto msg = Protocol::CollectionChangeNotificationPtr::create();
0100     msg->setParentCollection(parent.id());
0101     msg->setOperation(Protocol::CollectionChangeNotification::Add);
0102     msg->setCollection(Protocol::FetchCollectionsResponse(added.id()));
0103     // With notification payloads most requests by-pass the pipeline as the
0104     // notification already contains everything. To force pipelineing we set
0105     // the internal metadata (normally set by ChangeRecorder)
0106     msg->addMetadata("FETCH_COLLECTION");
0107 
0108     QHash<Collection::Id, Collection> data;
0109     data.insert(parent.id(), parent);
0110     data.insert(added.id(), added);
0111 
0112     // Pending notifications remains empty because we don't fill the pipeline with one message.
0113 
0114     QVERIFY(monitor->pipeline().isEmpty());
0115     QVERIFY(monitor->pendingNotifications().isEmpty());
0116 
0117     monitor->notificationConnection()->emitNotify(msg);
0118 
0119     QTRY_COMPARE(monitor->pipeline().size(), 1);
0120     QVERIFY(monitor->pendingNotifications().isEmpty());
0121 
0122     collectionCache->setData(data);
0123     collectionCache->emitDataAvailable();
0124 
0125     QVERIFY(monitor->pipeline().isEmpty());
0126     QVERIFY(monitor->pendingNotifications().isEmpty());
0127 }
0128 
0129 void MonitorNotificationTest::testFillPipeline_data()
0130 {
0131     QTest::addColumn<bool>("useChangeRecorder");
0132 
0133     QTest::newRow("useChangeRecorder") << true;
0134     QTest::newRow("useMonitor") << false;
0135 }
0136 
0137 void MonitorNotificationTest::testFillPipeline()
0138 {
0139     QFETCH(bool, useChangeRecorder);
0140 
0141     auto collectionCache = new FakeCollectionCache(m_fakeSession);
0142     FakeItemCache itemCache(m_fakeSession);
0143     auto depsFactory = new FakeMonitorDependenciesFactory(&itemCache, collectionCache);
0144 
0145     if (!useChangeRecorder) {
0146         testFillPipeline_impl(new InspectableMonitor(depsFactory, this), collectionCache, &itemCache);
0147     } else {
0148         auto changeRecorder = new InspectableChangeRecorder(depsFactory, this);
0149         changeRecorder->setChangeRecordingEnabled(false);
0150         testFillPipeline_impl(changeRecorder, collectionCache, &itemCache);
0151     }
0152 }
0153 
0154 template<typename MonitorImpl>
0155 void MonitorNotificationTest::testFillPipeline_impl(MonitorImpl *monitor, FakeCollectionCache *collectionCache, FakeItemCache *itemCache)
0156 {
0157     Q_UNUSED(itemCache)
0158 
0159     monitor->setSession(m_fakeSession);
0160     monitor->fetchCollection(true);
0161 
0162     Protocol::ChangeNotificationList list;
0163     QHash<Collection::Id, Collection> data;
0164 
0165     int i = 1;
0166     while (i < 40) {
0167         Collection parent(i++);
0168         Collection added(i++);
0169 
0170         auto msg = Protocol::CollectionChangeNotificationPtr::create();
0171         msg->setParentCollection(parent.id());
0172         msg->setOperation(Protocol::CollectionChangeNotification::Add);
0173         msg->setCollection(Protocol::FetchCollectionsResponse(added.id()));
0174         msg->addMetadata("FETCH_COLLECTION");
0175 
0176         data.insert(parent.id(), parent);
0177         data.insert(added.id(), added);
0178 
0179         list << msg;
0180     }
0181 
0182     QVERIFY(monitor->pipeline().isEmpty());
0183     QVERIFY(monitor->pendingNotifications().isEmpty());
0184 
0185     for (const Protocol::ChangeNotificationPtr &ntf : std::as_const(list)) {
0186         monitor->notificationConnection()->emitNotify(ntf);
0187     }
0188 
0189     QTRY_COMPARE(monitor->pipeline().size(), 5);
0190     QCOMPARE(monitor->pendingNotifications().size(), 15);
0191 
0192     collectionCache->setData(data);
0193     collectionCache->emitDataAvailable();
0194 
0195     QVERIFY(monitor->pipeline().isEmpty());
0196     QVERIFY(monitor->pendingNotifications().isEmpty());
0197 }
0198 
0199 void MonitorNotificationTest::testMonitor_data()
0200 {
0201     QTest::addColumn<bool>("useChangeRecorder");
0202 
0203     QTest::newRow("useChangeRecorder") << true;
0204     QTest::newRow("useMonitor") << false;
0205 }
0206 
0207 void MonitorNotificationTest::testMonitor()
0208 {
0209     QFETCH(bool, useChangeRecorder);
0210 
0211     auto collectionCache = new FakeCollectionCache(m_fakeSession);
0212     FakeItemCache itemCache(m_fakeSession);
0213     auto depsFactory = new FakeMonitorDependenciesFactory(&itemCache, collectionCache);
0214 
0215     if (!useChangeRecorder) {
0216         testMonitor_impl(new InspectableMonitor(depsFactory, this), collectionCache, &itemCache);
0217     } else {
0218         auto changeRecorder = new InspectableChangeRecorder(depsFactory, this);
0219         changeRecorder->setChangeRecordingEnabled(false);
0220         testMonitor_impl(changeRecorder, collectionCache, &itemCache);
0221     }
0222 }
0223 
0224 template<typename MonitorImpl>
0225 void MonitorNotificationTest::testMonitor_impl(MonitorImpl *monitor, FakeCollectionCache *collectionCache, FakeItemCache *itemCache)
0226 {
0227     Q_UNUSED(itemCache)
0228 
0229     monitor->setSession(m_fakeSession);
0230     monitor->fetchCollection(true);
0231 
0232     Protocol::ChangeNotificationList list;
0233 
0234     Collection col2(2);
0235     col2.setParentCollection(Collection::root());
0236 
0237     collectionCache->insert(col2);
0238 
0239     int i = 4;
0240 
0241     while (i < 8) {
0242         Collection added(i++);
0243 
0244         auto msg = Protocol::CollectionChangeNotificationPtr::create();
0245         msg->setParentCollection(i % 2 ? 2 : added.id() - 1);
0246         msg->setOperation(Protocol::CollectionChangeNotification::Add);
0247         msg->setCollection(Protocol::FetchCollectionsResponse(added.id()));
0248         msg->addMetadata("FETCH_COLLECTION");
0249 
0250         list << msg;
0251     }
0252 
0253     QVERIFY(monitor->pipeline().isEmpty());
0254     QVERIFY(monitor->pendingNotifications().isEmpty());
0255 
0256     Collection col4(4);
0257     col4.setParentCollection(col2);
0258     Collection col6(6);
0259     col6.setParentCollection(col2);
0260 
0261     collectionCache->insert(col4);
0262     collectionCache->insert(col6);
0263 
0264     qRegisterMetaType<Akonadi::Collection>();
0265     QSignalSpy collectionAddedSpy(monitor, SIGNAL(collectionAdded(Akonadi::Collection, Akonadi::Collection)));
0266 
0267     collectionCache->emitDataAvailable();
0268 
0269     QTRY_VERIFY(monitor->pipeline().isEmpty());
0270     QVERIFY(monitor->pendingNotifications().isEmpty());
0271 
0272     for (const Protocol::ChangeNotificationPtr &ntf : std::as_const(list)) {
0273         monitor->notificationConnection()->emitNotify(ntf);
0274     }
0275 
0276     // Collection 6 is not notified, because Collection 5 has held up the pipeline
0277     QTRY_COMPARE(collectionAddedSpy.size(), 1);
0278     QCOMPARE((int)collectionAddedSpy.takeFirst().first().value<Akonadi::Collection>().id(), 4);
0279     QCOMPARE(monitor->pipeline().size(), 3);
0280     QCOMPARE(monitor->pendingNotifications().size(), 0);
0281 
0282     Collection col7(7);
0283     col7.setParentCollection(col6);
0284 
0285     collectionCache->insert(col7);
0286     collectionCache->emitDataAvailable();
0287 
0288     // Collection 5 is still holding the pipeline
0289     QCOMPARE(collectionAddedSpy.size(), 0);
0290     QCOMPARE(monitor->pipeline().size(), 3);
0291     QCOMPARE(monitor->pendingNotifications().size(), 0);
0292 
0293     Collection col5(5);
0294     col5.setParentCollection(col4);
0295 
0296     collectionCache->insert(col5);
0297     collectionCache->emitDataAvailable();
0298 
0299     // Collection 5 is in cache, pipeline is flushed
0300     QCOMPARE(collectionAddedSpy.size(), 3);
0301     QCOMPARE(monitor->pipeline().size(), 0);
0302     QCOMPARE(monitor->pendingNotifications().size(), 0);
0303 }
0304 
0305 QTEST_MAIN(MonitorNotificationTest)
0306 #include "monitornotificationtest.moc"