File indexing completed on 2024-11-10 04:40:08
0001 /* 0002 SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "changerecorder.h" 0008 #include "agentmanager.h" 0009 #include "itemdeletejob.h" 0010 #include "itemfetchscope.h" 0011 #include "itemmodifyjob.h" 0012 #include "testattribute.h" 0013 0014 #include "qtest_akonadi.h" 0015 0016 #include <QObject> 0017 #include <QSettings> 0018 0019 using namespace Akonadi; 0020 0021 Q_DECLARE_METATYPE(QSet<QByteArray>) 0022 0023 class ChangeRecorderTest : public QObject 0024 { 0025 Q_OBJECT 0026 0027 private Q_SLOTS: 0028 void initTestCase() 0029 { 0030 qRegisterMetaType<Akonadi::Item>(); 0031 qRegisterMetaType<QSet<QByteArray>>(); 0032 AkonadiTest::checkTestIsIsolated(); 0033 AkonadiTest::setAllResourcesOffline(); 0034 0035 settings = new QSettings(QStringLiteral("kde.org"), QStringLiteral("akonadi-changerecordertest"), this); 0036 } 0037 0038 // After each test 0039 void cleanup() 0040 { 0041 // See ChangeRecorderPrivate::notificationsFileName() 0042 QFile::remove(settings->fileName() + QStringLiteral("_changes.dat")); 0043 } 0044 0045 void testChangeRecorder_data() 0046 { 0047 QTest::addColumn<QStringList>("actions"); 0048 0049 QTest::newRow("nothingToReplay") << (QStringList() << QStringLiteral("rn")); 0050 QTest::newRow("nothingOneNothing") << (QStringList() << QStringLiteral("rn") << QStringLiteral("c2") << QStringLiteral("r2") << QStringLiteral("rn")); 0051 QTest::newRow("multipleItems") << (QStringList() << QStringLiteral("c1") << QStringLiteral("c2") << QStringLiteral("c3") << QStringLiteral("r1") 0052 << QStringLiteral("c4") << QStringLiteral("r2") << QStringLiteral("r3") << QStringLiteral("r4") 0053 << QStringLiteral("rn")); 0054 QTest::newRow("reload") << (QStringList() << QStringLiteral("c1") << QStringLiteral("c1") << QStringLiteral("c3") << QStringLiteral("reload") 0055 << QStringLiteral("r1") << QStringLiteral("r1") << QStringLiteral("r3") << QStringLiteral("rn")); 0056 QTest::newRow("more") << (QStringList() << QStringLiteral("c1") << QStringLiteral("c2") << QStringLiteral("c3") << QStringLiteral("reload") 0057 << QStringLiteral("r1") << QStringLiteral("reload") << QStringLiteral("c4") << QStringLiteral("reload") 0058 << QStringLiteral("r2") << QStringLiteral("reload") << QStringLiteral("r3") << QStringLiteral("r4") 0059 << QStringLiteral("rn")); 0060 // FIXME: Due to the event compression in the server we simply expect a removal signal 0061 // QTest::newRow("modifyThenDelete") << (QStringList() << "c1" << "d1" << "r1" << "rn"); 0062 } 0063 0064 void testChangeRecorder() 0065 { 0066 QFETCH(QStringList, actions); 0067 QString lastAction; 0068 0069 auto rec = createChangeRecorder(); 0070 QVERIFY(rec->isEmpty()); 0071 for (const QString &action : std::as_const(actions)) { 0072 qDebug() << action; 0073 if (action == QLatin1StringView("rn")) { 0074 replayNextAndExpectNothing(rec.get()); 0075 } else if (action == QLatin1StringView("reload")) { 0076 // Check saving and loading from disk 0077 rec = createChangeRecorder(); 0078 } else if (action.at(0) == QLatin1Char('c')) { 0079 // c1 = "trigger change on item 1" 0080 const int id = QStringView(action).mid(1).toInt(); 0081 Q_ASSERT(id); 0082 triggerChange(id); 0083 if (action != lastAction) { 0084 // enter event loop and wait for change notifications from the server 0085 QVERIFY(AkonadiTest::akWaitForSignal(rec.get(), &ChangeRecorder::changesAdded, 1000)); 0086 } 0087 } else if (action.at(0) == QLatin1Char('d')) { 0088 // d1 = "delete item 1" 0089 const int id = QStringView(action).mid(1).toInt(); 0090 Q_ASSERT(id); 0091 triggerDelete(id); 0092 QTest::qWait(500); 0093 } else if (action.at(0) == QLatin1Char('r')) { 0094 // r1 = "replayNext and expect to get itemChanged(1)" 0095 const int id = QStringView(action).mid(1).toInt(); 0096 Q_ASSERT(id); 0097 replayNextAndProcess(rec.get(), id); 0098 } else { 0099 QVERIFY2(false, qPrintable(QStringLiteral("Unsupported: ") + action)); 0100 } 0101 lastAction = action; 0102 } 0103 QVERIFY(rec->isEmpty()); 0104 } 0105 0106 private: 0107 void triggerChange(Akonadi::Item::Id uid) 0108 { 0109 static int s_num = 0; 0110 Item item(uid); 0111 auto attr = item.attribute<TestAttribute>(Item::AddIfMissing); 0112 attr->data = QByteArray::number(++s_num); 0113 auto job = new ItemModifyJob(item); 0114 job->disableRevisionCheck(); 0115 AKVERIFYEXEC(job); 0116 } 0117 0118 void triggerDelete(Akonadi::Item::Id uid) 0119 { 0120 Item item(uid); 0121 auto job = new ItemDeleteJob(item); 0122 AKVERIFYEXEC(job); 0123 } 0124 0125 void replayNextAndProcess(ChangeRecorder *rec, Akonadi::Item::Id expectedUid) 0126 { 0127 QSignalSpy nothingSpy(rec, &ChangeRecorder::nothingToReplay); 0128 QVERIFY(nothingSpy.isValid()); 0129 QSignalSpy itemChangedSpy(rec, &Monitor::itemChanged); 0130 QVERIFY(itemChangedSpy.isValid()); 0131 0132 rec->replayNext(); 0133 if (itemChangedSpy.isEmpty()) { 0134 QVERIFY(AkonadiTest::akWaitForSignal(rec, &Monitor::itemChanged, 1000)); 0135 } 0136 QCOMPARE(itemChangedSpy.count(), 1); 0137 QCOMPARE(itemChangedSpy.at(0).at(0).value<Akonadi::Item>().id(), expectedUid); 0138 0139 rec->changeProcessed(); 0140 0141 QCOMPARE(nothingSpy.count(), 0); 0142 } 0143 0144 void replayNextAndExpectNothing(ChangeRecorder *rec) 0145 { 0146 QSignalSpy nothingSpy(rec, &ChangeRecorder::nothingToReplay); 0147 QVERIFY(nothingSpy.isValid()); 0148 QSignalSpy itemChangedSpy(rec, &Monitor::itemChanged); 0149 QVERIFY(itemChangedSpy.isValid()); 0150 0151 rec->replayNext(); // emits nothingToReplay immediately 0152 0153 QCOMPARE(itemChangedSpy.count(), 0); 0154 QCOMPARE(nothingSpy.count(), 1); 0155 } 0156 0157 std::unique_ptr<ChangeRecorder> createChangeRecorder() const 0158 { 0159 auto rec = std::make_unique<ChangeRecorder>(); 0160 rec->setConfig(settings); 0161 rec->setAllMonitored(); 0162 rec->itemFetchScope().fetchFullPayload(); 0163 rec->itemFetchScope().fetchAllAttributes(); 0164 rec->itemFetchScope().setCacheOnly(true); 0165 0166 // Ensure we listen to a signal, otherwise MonitorPrivate::isLazilyIgnored will ignore notifications 0167 auto spy = new QSignalSpy(rec.get(), &Monitor::itemChanged); 0168 spy->setParent(rec.get()); 0169 0170 QSignalSpy readySpy(rec.get(), &Monitor::monitorReady); 0171 if (!readySpy.wait()) { 0172 QTest::qFail("Failed to wait for Monitor", __FILE__, __LINE__); 0173 return nullptr; 0174 } 0175 0176 return rec; 0177 } 0178 0179 QSettings *settings = nullptr; 0180 }; 0181 0182 QTEST_AKONADIMAIN(ChangeRecorderTest) 0183 0184 #include "changerecordertest.moc"