File indexing completed on 2024-11-10 04:40:13
0001 /* 0002 SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "itemsync.h" 0008 #include "agentinstance.h" 0009 #include "agentmanager.h" 0010 #include "collection.h" 0011 #include "control.h" 0012 #include "item.h" 0013 #include "itemcreatejob.h" 0014 #include "itemdeletejob.h" 0015 #include "itemfetchjob.h" 0016 #include "itemfetchscope.h" 0017 #include "monitor.h" 0018 #include "qtest_akonadi.h" 0019 #include "resourceselectjob_p.h" 0020 0021 #include <KRandom> 0022 0023 #include <QObject> 0024 #include <QSignalSpy> 0025 0026 #include <memory> 0027 0028 using namespace Akonadi; 0029 0030 Q_DECLARE_METATYPE(KJob *) 0031 Q_DECLARE_METATYPE(ItemSync::TransactionMode) 0032 using namespace std::chrono_literals; 0033 class ItemsyncTest : public QObject 0034 { 0035 Q_OBJECT 0036 private: 0037 Item::List fetchItems(const Collection &col) 0038 { 0039 qDebug() << "fetching items from collection" << col.remoteId() << col.name(); 0040 auto fetch = new ItemFetchJob(col, this); 0041 fetch->fetchScope().fetchFullPayload(); 0042 fetch->fetchScope().fetchAllAttributes(); 0043 fetch->fetchScope().setCacheOnly(true); // resources are switched off anyway 0044 if (!fetch->exec()) { 0045 []() { 0046 QFAIL("Failed to fetch items!"); 0047 }(); 0048 } 0049 return fetch->items(); 0050 } 0051 0052 static void createItems(const Collection &col, int itemCount) 0053 { 0054 for (int i = 0; i < itemCount; ++i) { 0055 Item item(QStringLiteral("application/octet-stream")); 0056 item.setRemoteId(QStringLiteral("rid") + QString::number(i)); 0057 item.setGid(QStringLiteral("gid") + QString::number(i)); 0058 item.setPayload<QByteArray>("payload1"); 0059 auto job = new ItemCreateJob(item, col); 0060 AKVERIFYEXEC(job); 0061 } 0062 } 0063 0064 static Item duplicateItem(const Item &item, const Collection &col) 0065 { 0066 Item duplicate = item; 0067 duplicate.setId(-1); 0068 auto job = new ItemCreateJob(duplicate, col); 0069 [job]() { 0070 AKVERIFYEXEC(job); 0071 }(); 0072 return job->item(); 0073 } 0074 0075 static Item modifyItem(Item item) 0076 { 0077 static int counter = 0; 0078 item.setFlag(QByteArray("\\READ") + QByteArray::number(counter)); 0079 counter++; 0080 return item; 0081 } 0082 0083 std::unique_ptr<Monitor> createCollectionMonitor(const Collection &col) 0084 { 0085 auto monitor = std::make_unique<Monitor>(); 0086 monitor->setCollectionMonitored(col); 0087 if (!AkonadiTest::akWaitForSignal(monitor.get(), &Monitor::monitorReady)) { 0088 QTest::qFail("Failed to wait for monitor", __FILE__, __LINE__); 0089 return nullptr; 0090 } 0091 0092 return monitor; 0093 } 0094 0095 private Q_SLOTS: 0096 void initTestCase() 0097 { 0098 AkonadiTest::checkTestIsIsolated(); 0099 Control::start(); 0100 AkonadiTest::setAllResourcesOffline(); 0101 qRegisterMetaType<KJob *>(); 0102 qRegisterMetaType<ItemSync::TransactionMode>(); 0103 } 0104 0105 void testFullSync() 0106 { 0107 const Collection col = Collection(AkonadiTest::collectionIdFromPath(QStringLiteral("res1/foo"))); 0108 QVERIFY(col.isValid()); 0109 Item::List origItems = fetchItems(col); 0110 0111 // Since the item sync affects the knut resource we ensure we actually managed to load all items 0112 // This needs to be adjusted should the testdataset change 0113 QCOMPARE(origItems.size(), 15); 0114 0115 Akonadi::Monitor monitor; 0116 monitor.setCollectionMonitored(col); 0117 QSignalSpy deletedSpy(&monitor, &Monitor::itemRemoved); 0118 QVERIFY(deletedSpy.isValid()); 0119 QSignalSpy addedSpy(&monitor, &Monitor::itemAdded); 0120 QVERIFY(addedSpy.isValid()); 0121 QSignalSpy changedSpy(&monitor, &Monitor::itemChanged); 0122 QVERIFY(changedSpy.isValid()); 0123 0124 auto syncer = new ItemSync(col); 0125 syncer->setTransactionMode(ItemSync::SingleTransaction); 0126 QSignalSpy transactionSpy(syncer, &ItemSync::transactionCommitted); 0127 QVERIFY(transactionSpy.isValid()); 0128 syncer->setFullSyncItems(origItems); 0129 AKVERIFYEXEC(syncer); 0130 QCOMPARE(transactionSpy.count(), 1); 0131 0132 Item::List resultItems = fetchItems(col); 0133 QCOMPARE(resultItems.count(), origItems.count()); 0134 QTest::qWait(100); 0135 QCOMPARE(deletedSpy.count(), 0); 0136 QCOMPARE(addedSpy.count(), 0); 0137 QCOMPARE(changedSpy.count(), 0); 0138 } 0139 0140 void testFullStreamingSync_data() 0141 { 0142 QTest::addColumn<ItemSync::TransactionMode>("transactionMode"); 0143 QTest::addColumn<bool>("goToEventLoopAfterAddingItems"); 0144 0145 QTest::newRow("single transaction, no eventloop") << ItemSync::SingleTransaction << false; 0146 QTest::newRow("multi transaction, no eventloop") << ItemSync::MultipleTransactions << false; 0147 QTest::newRow("single transaction, with eventloop") << ItemSync::SingleTransaction << true; 0148 QTest::newRow("multi transaction, with eventloop") << ItemSync::MultipleTransactions << true; 0149 } 0150 0151 void testFullStreamingSync() 0152 { 0153 QFETCH(ItemSync::TransactionMode, transactionMode); 0154 QFETCH(bool, goToEventLoopAfterAddingItems); 0155 0156 const Collection col = Collection(AkonadiTest::collectionIdFromPath(QStringLiteral("res1/foo"))); 0157 QVERIFY(col.isValid()); 0158 Item::List origItems = fetchItems(col); 0159 QCOMPARE(origItems.size(), 15); 0160 0161 auto monitor = createCollectionMonitor(col); 0162 QSignalSpy deletedSpy(monitor.get(), &Monitor::itemRemoved); 0163 QSignalSpy addedSpy(monitor.get(), &Monitor::itemAdded); 0164 QSignalSpy changedSpy(monitor.get(), &Monitor::itemChanged); 0165 0166 auto syncer = new ItemSync(col); 0167 QSignalSpy transactionSpy(syncer, &ItemSync::transactionCommitted); 0168 QVERIFY(transactionSpy.isValid()); 0169 syncer->setTransactionMode(transactionMode); 0170 syncer->setBatchSize(1); 0171 syncer->setAutoDelete(false); 0172 syncer->setStreamingEnabled(true); 0173 QSignalSpy spy(syncer, &KJob::result); 0174 QVERIFY(spy.isValid()); 0175 syncer->setTotalItems(origItems.count()); 0176 QTest::qWait(0); 0177 QCOMPARE(spy.count(), 0); 0178 0179 for (int i = 0; i < origItems.count(); ++i) { 0180 Item::List l; 0181 // Modify to trigger a changed signal 0182 l << modifyItem(origItems[i]); 0183 syncer->setFullSyncItems(l); 0184 if (goToEventLoopAfterAddingItems) { 0185 QTest::qWait(0); 0186 } 0187 if (i < origItems.count() - 1) { 0188 QCOMPARE(spy.count(), 0); 0189 } 0190 } 0191 syncer->deliveryDone(); 0192 QTRY_COMPARE(spy.count(), 1); 0193 KJob *job = spy.at(0).at(0).value<KJob *>(); 0194 QCOMPARE(job, syncer); 0195 QCOMPARE(job->error(), 0); 0196 if (transactionMode == ItemSync::SingleTransaction) { 0197 QCOMPARE(transactionSpy.count(), 1); 0198 } 0199 if (transactionMode == ItemSync::MultipleTransactions) { 0200 QCOMPARE(transactionSpy.count(), origItems.count()); 0201 } 0202 0203 Item::List resultItems = fetchItems(col); 0204 QCOMPARE(resultItems.count(), origItems.count()); 0205 0206 delete syncer; 0207 QTest::qWait(100); 0208 QTRY_COMPARE(deletedSpy.count(), 0); 0209 QTRY_COMPARE(addedSpy.count(), 0); 0210 QTRY_COMPARE(changedSpy.count(), origItems.count()); 0211 } 0212 0213 void testIncrementalSync() 0214 { 0215 { 0216 auto select = new ResourceSelectJob(QStringLiteral("akonadi_knut_resource_0")); 0217 AKVERIFYEXEC(select); 0218 } 0219 0220 const Collection col = Collection(AkonadiTest::collectionIdFromPath(QStringLiteral("res1/foo"))); 0221 QVERIFY(col.isValid()); 0222 Item::List origItems = fetchItems(col); 0223 QCOMPARE(origItems.size(), 15); 0224 0225 auto monitor = createCollectionMonitor(col); 0226 QSignalSpy deletedSpy(monitor.get(), &Monitor::itemRemoved); 0227 QSignalSpy addedSpy(monitor.get(), &Monitor::itemAdded); 0228 QSignalSpy changedSpy(monitor.get(), &Monitor::itemChanged); 0229 0230 { 0231 auto syncer = new ItemSync(col); 0232 QSignalSpy transactionSpy(syncer, &ItemSync::transactionCommitted); 0233 QVERIFY(transactionSpy.isValid()); 0234 syncer->setTransactionMode(ItemSync::SingleTransaction); 0235 syncer->setIncrementalSyncItems(origItems, Item::List()); 0236 AKVERIFYEXEC(syncer); 0237 QCOMPARE(transactionSpy.count(), 1); 0238 } 0239 0240 QTest::qWait(100); 0241 QTRY_COMPARE(deletedSpy.count(), 0); 0242 QCOMPARE(addedSpy.count(), 0); 0243 QTRY_COMPARE(changedSpy.count(), 0); 0244 deletedSpy.clear(); 0245 addedSpy.clear(); 0246 changedSpy.clear(); 0247 0248 Item::List resultItems = fetchItems(col); 0249 QCOMPARE(resultItems.count(), origItems.count()); 0250 0251 Item::List delItems; 0252 delItems << resultItems.takeFirst(); 0253 0254 Item itemWithOnlyRemoteId; 0255 itemWithOnlyRemoteId.setRemoteId(resultItems.front().remoteId()); 0256 delItems << itemWithOnlyRemoteId; 0257 resultItems.takeFirst(); 0258 0259 // This item will not be removed since it isn't existing locally 0260 Item itemWithRandomRemoteId; 0261 itemWithRandomRemoteId.setRemoteId(KRandom::randomString(100)); 0262 delItems << itemWithRandomRemoteId; 0263 0264 { 0265 auto syncer = new ItemSync(col); 0266 syncer->setTransactionMode(ItemSync::SingleTransaction); 0267 QSignalSpy transactionSpy(syncer, &ItemSync::transactionCommitted); 0268 QVERIFY(transactionSpy.isValid()); 0269 syncer->setIncrementalSyncItems(resultItems, delItems); 0270 AKVERIFYEXEC(syncer); 0271 QCOMPARE(transactionSpy.count(), 1); 0272 } 0273 0274 Item::List resultItems2 = fetchItems(col); 0275 QCOMPARE(resultItems2.count(), resultItems.count()); 0276 0277 QTest::qWait(100); 0278 QTRY_COMPARE(deletedSpy.count(), 2); 0279 QCOMPARE(addedSpy.count(), 0); 0280 QTRY_COMPARE(changedSpy.count(), 0); 0281 0282 { 0283 auto select = new ResourceSelectJob(QStringLiteral("")); 0284 AKVERIFYEXEC(select); 0285 } 0286 } 0287 0288 void testIncrementalStreamingSync() 0289 { 0290 const Collection col = Collection(AkonadiTest::collectionIdFromPath(QStringLiteral("res1/foo"))); 0291 QVERIFY(col.isValid()); 0292 Item::List origItems = fetchItems(col); 0293 0294 auto monitor = createCollectionMonitor(col); 0295 QSignalSpy deletedSpy(monitor.get(), &Monitor::itemRemoved); 0296 QSignalSpy addedSpy(monitor.get(), &Monitor::itemAdded); 0297 QSignalSpy changedSpy(monitor.get(), &Monitor::itemChanged); 0298 0299 auto syncer = new ItemSync(col); 0300 syncer->setTransactionMode(ItemSync::SingleTransaction); 0301 QSignalSpy transactionSpy(syncer, &ItemSync::transactionCommitted); 0302 QVERIFY(transactionSpy.isValid()); 0303 syncer->setAutoDelete(false); 0304 QSignalSpy spy(syncer, &KJob::result); 0305 QVERIFY(spy.isValid()); 0306 syncer->setStreamingEnabled(true); 0307 QTest::qWait(0); 0308 QCOMPARE(spy.count(), 0); 0309 0310 for (int i = 0; i < origItems.count(); ++i) { 0311 Item::List l; 0312 // Modify to trigger a changed signal 0313 l << modifyItem(origItems[i]); 0314 syncer->setIncrementalSyncItems(l, Item::List()); 0315 if (i < origItems.count() - 1) { 0316 QTest::qWait(0); // enter the event loop so itemsync actually can do something 0317 } 0318 QCOMPARE(spy.count(), 0); 0319 } 0320 syncer->deliveryDone(); 0321 QTRY_COMPARE(spy.count(), 1); 0322 KJob *job = spy.at(0).at(0).value<KJob *>(); 0323 QCOMPARE(job, syncer); 0324 QCOMPARE(job->error(), 0); 0325 QCOMPARE(transactionSpy.count(), 1); 0326 0327 Item::List resultItems = fetchItems(col); 0328 QCOMPARE(resultItems.count(), origItems.count()); 0329 0330 delete syncer; 0331 0332 QTest::qWait(100); 0333 QCOMPARE(deletedSpy.count(), 0); 0334 QCOMPARE(addedSpy.count(), 0); 0335 QTRY_COMPARE(changedSpy.count(), origItems.size()); 0336 } 0337 0338 void testEmptyIncrementalSync() 0339 { 0340 const Collection col = Collection(AkonadiTest::collectionIdFromPath(QStringLiteral("res1/foo"))); 0341 QVERIFY(col.isValid()); 0342 Item::List origItems = fetchItems(col); 0343 0344 auto monitor = createCollectionMonitor(col); 0345 QSignalSpy deletedSpy(monitor.get(), &Monitor::itemRemoved); 0346 QSignalSpy addedSpy(monitor.get(), &Monitor::itemAdded); 0347 QSignalSpy changedSpy(monitor.get(), &Monitor::itemChanged); 0348 0349 auto syncer = new ItemSync(col); 0350 syncer->setTransactionMode(ItemSync::SingleTransaction); 0351 QSignalSpy transactionSpy(syncer, &ItemSync::transactionCommitted); 0352 QVERIFY(transactionSpy.isValid()); 0353 syncer->setIncrementalSyncItems(Item::List(), Item::List()); 0354 AKVERIFYEXEC(syncer); 0355 // It would be better if we didn't have a transaction at all, but so far the transaction is still created 0356 QCOMPARE(transactionSpy.count(), 1); 0357 0358 Item::List resultItems = fetchItems(col); 0359 QCOMPARE(resultItems.count(), origItems.count()); 0360 0361 QTest::qWait(100); 0362 QCOMPARE(deletedSpy.count(), 0); 0363 QCOMPARE(addedSpy.count(), 0); 0364 QCOMPARE(changedSpy.count(), 0); 0365 } 0366 0367 void testIncrementalStreamingSyncBatchProcessing() 0368 { 0369 const Collection col = Collection(AkonadiTest::collectionIdFromPath(QStringLiteral("res1/foo"))); 0370 QVERIFY(col.isValid()); 0371 Item::List origItems = fetchItems(col); 0372 0373 auto monitor = createCollectionMonitor(col); 0374 QSignalSpy deletedSpy(monitor.get(), &Monitor::itemRemoved); 0375 QSignalSpy addedSpy(monitor.get(), &Monitor::itemAdded); 0376 QSignalSpy changedSpy(monitor.get(), &Monitor::itemChanged); 0377 0378 auto syncer = new ItemSync(col); 0379 QSignalSpy transactionSpy(syncer, &ItemSync::transactionCommitted); 0380 QVERIFY(transactionSpy.isValid()); 0381 QSignalSpy spy(syncer, &KJob::result); 0382 QVERIFY(spy.isValid()); 0383 syncer->setStreamingEnabled(true); 0384 syncer->setTransactionMode(ItemSync::MultipleTransactions); 0385 QTest::qWait(0); 0386 QCOMPARE(spy.count(), 0); 0387 0388 for (int i = 0; i < syncer->batchSize(); ++i) { 0389 Item::List l; 0390 // Modify to trigger a changed signal 0391 l << modifyItem(origItems[i]); 0392 syncer->setIncrementalSyncItems(l, Item::List()); 0393 if (i < (syncer->batchSize() - 1)) { 0394 QTest::qWait(0); // enter the event loop so itemsync actually can do something 0395 } 0396 QCOMPARE(spy.count(), 0); 0397 } 0398 QTest::qWait(100); 0399 // this should process one batch of batchSize() items 0400 QTRY_COMPARE(changedSpy.count(), syncer->batchSize()); 0401 QCOMPARE(transactionSpy.count(), 1); // one per batch 0402 0403 for (int i = syncer->batchSize(); i < origItems.count(); ++i) { 0404 Item::List l; 0405 // Modify to trigger a changed signal 0406 l << modifyItem(origItems[i]); 0407 syncer->setIncrementalSyncItems(l, Item::List()); 0408 if (i < origItems.count() - 1) { 0409 QTest::qWait(0); // enter the event loop so itemsync actually can do something 0410 } 0411 QCOMPARE(spy.count(), 0); 0412 } 0413 0414 syncer->deliveryDone(); 0415 QTRY_COMPARE(spy.count(), 1); 0416 QCOMPARE(transactionSpy.count(), 2); // one per batch 0417 QTest::qWait(100); 0418 0419 Item::List resultItems = fetchItems(col); 0420 QCOMPARE(resultItems.count(), origItems.count()); 0421 0422 QTest::qWait(100); 0423 QCOMPARE(deletedSpy.count(), 0); 0424 QCOMPARE(addedSpy.count(), 0); 0425 QTRY_COMPARE(changedSpy.count(), resultItems.count()); 0426 } 0427 0428 void testGidMerge() 0429 { 0430 Collection col(AkonadiTest::collectionIdFromPath(QStringLiteral("res3"))); 0431 { 0432 Item item(QStringLiteral("application/octet-stream")); 0433 item.setRemoteId(QStringLiteral("rid1")); 0434 item.setGid(QStringLiteral("gid1")); 0435 item.setPayload<QByteArray>("payload1"); 0436 auto job = new ItemCreateJob(item, col); 0437 AKVERIFYEXEC(job); 0438 } 0439 { 0440 Item item(QStringLiteral("application/octet-stream")); 0441 item.setRemoteId(QStringLiteral("rid2")); 0442 item.setGid(QStringLiteral("gid2")); 0443 item.setPayload<QByteArray>("payload1"); 0444 auto job = new ItemCreateJob(item, col); 0445 AKVERIFYEXEC(job); 0446 } 0447 Item modifiedItem(QStringLiteral("application/octet-stream")); 0448 modifiedItem.setRemoteId(QStringLiteral("rid3")); 0449 modifiedItem.setGid(QStringLiteral("gid2")); 0450 modifiedItem.setPayload<QByteArray>("payload2"); 0451 0452 auto syncer = new ItemSync(col); 0453 syncer->setTransactionMode(ItemSync::MultipleTransactions); 0454 syncer->setIncrementalSyncItems(Item::List() << modifiedItem, Item::List()); 0455 AKVERIFYEXEC(syncer); 0456 0457 Item::List resultItems = fetchItems(col); 0458 QCOMPARE(resultItems.count(), 3); 0459 0460 Item item; 0461 item.setGid(QStringLiteral("gid2")); 0462 auto fetchJob = new ItemFetchJob(item); 0463 fetchJob->fetchScope().fetchFullPayload(); 0464 AKVERIFYEXEC(fetchJob); 0465 QCOMPARE(fetchJob->items().size(), 2); 0466 QCOMPARE(fetchJob->items().first().payload<QByteArray>(), QByteArray("payload2")); 0467 QCOMPARE(fetchJob->items().first().remoteId(), QString::fromLatin1("rid3")); 0468 QCOMPARE(fetchJob->items().at(1).payload<QByteArray>(), QByteArray("payload1")); 0469 QCOMPARE(fetchJob->items().at(1).remoteId(), QStringLiteral("rid2")); 0470 } 0471 0472 /* 0473 * This test verifies that ItemSync doesn't prematurely emit its result if a job inside a transaction fails. 0474 * ItemSync is supposed to continue the sync but simply ignoring all delivered data. 0475 */ 0476 void testFailingJob() 0477 { 0478 const Collection col = Collection(AkonadiTest::collectionIdFromPath(QStringLiteral("res1/foo"))); 0479 QVERIFY(col.isValid()); 0480 Item::List origItems = fetchItems(col); 0481 0482 auto syncer = new ItemSync(col); 0483 QSignalSpy transactionSpy(syncer, &ItemSync::transactionCommitted); 0484 QVERIFY(transactionSpy.isValid()); 0485 QSignalSpy spy(syncer, &KJob::result); 0486 QVERIFY(spy.isValid()); 0487 syncer->setStreamingEnabled(true); 0488 syncer->setTransactionMode(ItemSync::MultipleTransactions); 0489 QTest::qWait(0); 0490 QCOMPARE(spy.count(), 0); 0491 0492 for (int i = 0; i < syncer->batchSize(); ++i) { 0493 Item::List l; 0494 // Modify to trigger a changed signal 0495 Item item = modifyItem(origItems[i]); 0496 // item.setRemoteId(QByteArray("foo")); 0497 item.setRemoteId(QString()); 0498 item.setId(-1); 0499 l << item; 0500 syncer->setIncrementalSyncItems(l, Item::List()); 0501 if (i < (syncer->batchSize() - 1)) { 0502 QTest::qWait(0); // enter the event loop so itemsync actually can do something 0503 } 0504 QCOMPARE(spy.count(), 0); 0505 } 0506 QTest::qWait(100); 0507 QTRY_COMPARE(spy.count(), 0); 0508 0509 for (int i = syncer->batchSize(); i < origItems.count(); ++i) { 0510 Item::List l; 0511 // Modify to trigger a changed signal 0512 l << modifyItem(origItems[i]); 0513 syncer->setIncrementalSyncItems(l, Item::List()); 0514 if (i < origItems.count() - 1) { 0515 QTest::qWait(0); // enter the event loop so itemsync actually can do something 0516 } 0517 QCOMPARE(spy.count(), 0); 0518 } 0519 0520 syncer->deliveryDone(); 0521 QTRY_COMPARE(spy.count(), 1); 0522 } 0523 0524 /* 0525 * This test verifies that ItemSync doesn't prematurely emit its result if a job inside a transaction fails, due to a duplicate. 0526 * This case used to break the TransactionSequence. 0527 * ItemSync is supposed to continue the sync but simply ignoring all delivered data. 0528 */ 0529 void testFailingDueToDuplicateItem() 0530 { 0531 const Collection col = Collection(AkonadiTest::collectionIdFromPath(QStringLiteral("res1/foo"))); 0532 QVERIFY(col.isValid()); 0533 Item::List origItems = fetchItems(col); 0534 0535 // Create a duplicate that will trigger an error during the first batch 0536 Item dupe = duplicateItem(origItems.at(0), col); 0537 origItems = fetchItems(col); 0538 0539 auto syncer = new ItemSync(col); 0540 QSignalSpy transactionSpy(syncer, &ItemSync::transactionCommitted); 0541 QVERIFY(transactionSpy.isValid()); 0542 QSignalSpy spy(syncer, &KJob::result); 0543 QVERIFY(spy.isValid()); 0544 syncer->setStreamingEnabled(true); 0545 syncer->setTransactionMode(ItemSync::MultipleTransactions); 0546 QTest::qWait(0); 0547 QCOMPARE(spy.count(), 0); 0548 0549 for (int i = 0; i < syncer->batchSize(); ++i) { 0550 Item::List l; 0551 // Modify to trigger a changed signal 0552 l << modifyItem(origItems[i]); 0553 syncer->setIncrementalSyncItems(l, Item::List()); 0554 if (i < (syncer->batchSize() - 1)) { 0555 QTest::qWait(0); // enter the event loop so itemsync actually can do something 0556 } 0557 QCOMPARE(spy.count(), 0); 0558 } 0559 QTest::qWait(100); 0560 // Ensure the job hasn't finished yet due to the errors 0561 QTRY_COMPARE(spy.count(), 0); 0562 0563 for (int i = syncer->batchSize(); i < origItems.count(); ++i) { 0564 Item::List l; 0565 // Modify to trigger a changed signal 0566 l << modifyItem(origItems[i]); 0567 syncer->setIncrementalSyncItems(l, Item::List()); 0568 if (i < origItems.count() - 1) { 0569 QTest::qWait(0); // enter the event loop so itemsync actually can do something 0570 } 0571 QCOMPARE(spy.count(), 0); 0572 } 0573 0574 syncer->deliveryDone(); 0575 QTRY_COMPARE(spy.count(), 1); 0576 0577 // cleanup 0578 auto del = new ItemDeleteJob(dupe, this); 0579 AKVERIFYEXEC(del); 0580 } 0581 0582 void testFullSyncFailingDueToDuplicateItem() 0583 { 0584 const Collection col = Collection(AkonadiTest::collectionIdFromPath(QStringLiteral("res1/foo"))); 0585 QVERIFY(col.isValid()); 0586 Item::List origItems = fetchItems(col); 0587 // Create a duplicate that will trigger an error during the first batch 0588 Item dupe = duplicateItem(origItems.at(0), col); 0589 origItems = fetchItems(col); 0590 0591 auto monitor = createCollectionMonitor(col); 0592 QSignalSpy deletedSpy(monitor.get(), &Monitor::itemRemoved); 0593 QSignalSpy addedSpy(monitor.get(), &Monitor::itemAdded); 0594 QSignalSpy changedSpy(monitor.get(), &Monitor::itemChanged); 0595 0596 auto syncer = new ItemSync(col); 0597 syncer->setTransactionMode(ItemSync::SingleTransaction); 0598 QSignalSpy transactionSpy(syncer, &ItemSync::transactionCommitted); 0599 QVERIFY(transactionSpy.isValid()); 0600 syncer->setFullSyncItems(origItems); 0601 QVERIFY(!syncer->exec()); 0602 QCOMPARE(transactionSpy.count(), 1); 0603 0604 Item::List resultItems = fetchItems(col); 0605 QCOMPARE(resultItems.count(), origItems.count()); 0606 QTest::qWait(100); 0607 // QCOMPARE(deletedSpy.count(), 1); // ## is this correct? 0608 // QCOMPARE(addedSpy.count(), 1); // ## is this correct? 0609 QCOMPARE(changedSpy.count(), 0); 0610 0611 // cleanup 0612 auto del = new ItemDeleteJob(dupe, this); 0613 AKVERIFYEXEC(del); 0614 } 0615 0616 void testFullSyncManyItems() 0617 { 0618 // Given a collection with 1000 items 0619 const Collection col = Collection(AkonadiTest::collectionIdFromPath(QStringLiteral("res2/foo2"))); 0620 QVERIFY(col.isValid()); 0621 0622 auto monitor = createCollectionMonitor(col); 0623 QSignalSpy addedSpy(monitor.get(), &Monitor::itemAdded); 0624 0625 const int itemCount = 1000; 0626 createItems(col, itemCount); 0627 QTRY_COMPARE(addedSpy.count(), itemCount); 0628 addedSpy.clear(); 0629 0630 const Item::List origItems = fetchItems(col); 0631 QCOMPARE(origItems.size(), itemCount); 0632 0633 QSignalSpy deletedSpy(monitor.get(), &Monitor::itemRemoved); 0634 QSignalSpy changedSpy(monitor.get(), &Monitor::itemChanged); 0635 0636 QBENCHMARK { 0637 auto syncer = new ItemSync(col); 0638 syncer->setTransactionMode(ItemSync::SingleTransaction); 0639 QSignalSpy transactionSpy(syncer, &ItemSync::transactionCommitted); 0640 QVERIFY(transactionSpy.isValid()); 0641 syncer->setFullSyncItems(origItems); 0642 0643 AKVERIFYEXEC(syncer); 0644 QCOMPARE(transactionSpy.count(), 1); 0645 } 0646 0647 const Item::List resultItems = fetchItems(col); 0648 QCOMPARE(resultItems.count(), origItems.count()); 0649 QTest::qWait(100); 0650 QCOMPARE(deletedSpy.count(), 0); 0651 QCOMPARE(addedSpy.count(), 0); 0652 QCOMPARE(changedSpy.count(), 0); 0653 0654 // delete all items; QBENCHMARK leads to the whole method being called more than once 0655 auto job = new ItemDeleteJob(resultItems); 0656 AKVERIFYEXEC(job); 0657 } 0658 0659 void testUserCancel() 0660 { 0661 // Given a collection with 100 items 0662 const Collection col = Collection(AkonadiTest::collectionIdFromPath(QStringLiteral("res2/foo2"))); 0663 QVERIFY(col.isValid()); 0664 0665 const Item::List itemsToDelete = fetchItems(col); 0666 if (!itemsToDelete.isEmpty()) { 0667 auto deleteJob = new ItemDeleteJob(itemsToDelete); 0668 AKVERIFYEXEC(deleteJob); 0669 } 0670 0671 const int itemCount = 100; 0672 createItems(col, itemCount); 0673 const Item::List origItems = fetchItems(col); 0674 QCOMPARE(origItems.size(), itemCount); 0675 0676 // and an ItemSync running 0677 auto syncer = new ItemSync(col); 0678 syncer->setTransactionMode(ItemSync::SingleTransaction); 0679 syncer->setFullSyncItems(origItems); 0680 0681 // When the user cancels the ItemSync 0682 QTimer::singleShot(10ms, syncer, &ItemSync::rollback); 0683 0684 // Then the itemsync should finish at some point, and not crash 0685 QVERIFY(!syncer->exec()); 0686 QCOMPARE(syncer->errorString(), QStringLiteral("User canceled operation.")); 0687 0688 // Cleanup 0689 auto job = new ItemDeleteJob(origItems); 0690 AKVERIFYEXEC(job); 0691 } 0692 }; 0693 0694 QTEST_AKONADIMAIN(ItemsyncTest) 0695 0696 #include "itemsynctest.moc"