File indexing completed on 2024-12-22 04:57:36
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net> 0003 SPDX-FileContributor: Kevin Krammer <krake@kdab.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "mixedmaildirstore.h" 0009 0010 #include "testdatautil.h" 0011 0012 #include "filestore/itemcreatejob.h" 0013 #include "filestore/itemfetchjob.h" 0014 0015 #include "libmaildir/maildir.h" 0016 0017 #include <KMbox/MBox> 0018 0019 #include <KMime/Message> 0020 0021 #include <QRandomGenerator> 0022 #include <QTemporaryDir> 0023 0024 #include <QDir> 0025 #include <QFileInfo> 0026 #include <QTest> 0027 0028 using namespace Akonadi; 0029 0030 class ItemCreateTest : public QObject 0031 { 0032 Q_OBJECT 0033 0034 public: 0035 ItemCreateTest() 0036 : QObject() 0037 , mStore(nullptr) 0038 , mDir(nullptr) 0039 { 0040 } 0041 0042 ~ItemCreateTest() override 0043 { 0044 delete mStore; 0045 delete mDir; 0046 } 0047 0048 private: 0049 MixedMaildirStore *mStore = nullptr; 0050 QTemporaryDir *mDir = nullptr; 0051 0052 private Q_SLOTS: 0053 void init(); 0054 void cleanup(); 0055 void testExpectedFail(); 0056 void testMBox(); 0057 void testMaildir(); 0058 }; 0059 0060 void ItemCreateTest::init() 0061 { 0062 mStore = new MixedMaildirStore; 0063 0064 mDir = new QTemporaryDir; 0065 QVERIFY(mDir->isValid()); 0066 QVERIFY(QDir(mDir->path()).exists()); 0067 } 0068 0069 void ItemCreateTest::cleanup() 0070 { 0071 delete mStore; 0072 mStore = nullptr; 0073 delete mDir; 0074 mDir = nullptr; 0075 } 0076 0077 void ItemCreateTest::testExpectedFail() 0078 { 0079 QDir topDir(mDir->path()); 0080 0081 QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir"), topDir.path(), QStringLiteral("data"))); 0082 QDir dataDir = topDir; 0083 QVERIFY(dataDir.cd(QLatin1StringView("data"))); 0084 KPIM::Maildir dataMd(dataDir.path(), false); 0085 QVERIFY(dataMd.isValid()); 0086 0087 const QStringList dataEntryList = dataMd.entryList(); 0088 QCOMPARE(dataEntryList.count(), 4); 0089 KMime::Message::Ptr msgPtr(new KMime::Message); 0090 msgPtr->setContent(KMime::CRLFtoLF(dataMd.readEntry(dataEntryList.first()))); 0091 0092 QVERIFY(topDir.mkdir(QLatin1StringView("store"))); 0093 QVERIFY(topDir.cd(QLatin1StringView("store"))); 0094 mStore->setPath(topDir.path()); 0095 0096 FileStore::ItemCreateJob *job = nullptr; 0097 0098 // test failure of adding item to top level collection 0099 Item item; 0100 item.setMimeType(KMime::Message::mimeType()); 0101 item.setPayload<KMime::Message::Ptr>(msgPtr); 0102 0103 job = mStore->createItem(item, mStore->topLevelCollection()); 0104 0105 QVERIFY(!job->exec()); 0106 QCOMPARE(job->error(), (int)FileStore::Job::InvalidJobContext); 0107 0108 // test failure of adding item to non existent collection 0109 Collection collection; 0110 collection.setName(QStringLiteral("collection")); 0111 collection.setRemoteId(QStringLiteral("collection")); 0112 collection.setParentCollection(mStore->topLevelCollection()); 0113 0114 job = mStore->createItem(item, collection); 0115 0116 QVERIFY(!job->exec()); 0117 QCOMPARE(job->error(), (int)FileStore::Job::InvalidJobContext); 0118 } 0119 0120 void ItemCreateTest::testMBox() 0121 { 0122 QDir topDir(mDir->path()); 0123 0124 QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir"), topDir.path(), QStringLiteral("data"))); 0125 QDir dataDir = topDir; 0126 QVERIFY(dataDir.cd(QLatin1StringView("data"))); 0127 KPIM::Maildir dataMd(dataDir.path(), false); 0128 QVERIFY(dataMd.isValid()); 0129 0130 const QStringList dataEntryList = dataMd.entryList(); 0131 QCOMPARE(dataEntryList.count(), 4); 0132 KMime::Message::Ptr msgPtr1(new KMime::Message); 0133 msgPtr1->setContent(KMime::CRLFtoLF(dataMd.readEntry(dataEntryList.first()))); 0134 KMime::Message::Ptr msgPtr2(new KMime::Message); 0135 msgPtr2->setContent(KMime::CRLFtoLF(dataMd.readEntry(dataEntryList.last()))); 0136 0137 QVERIFY(topDir.mkdir(QLatin1StringView("store"))); 0138 QVERIFY(topDir.cd(QLatin1StringView("store"))); 0139 0140 QVERIFY(TestDataUtil::installFolder(QLatin1StringView("mbox"), topDir.path(), QStringLiteral("collection1"))); 0141 0142 QFileInfo fileInfo1(topDir.path(), QStringLiteral("collection1")); 0143 KMBox::MBox mbox1; 0144 QVERIFY(mbox1.load(fileInfo1.absoluteFilePath())); 0145 QCOMPARE((int)mbox1.entries().count(), 4); 0146 const int size1 = fileInfo1.size(); 0147 0148 // simulate empty mbox 0149 QFileInfo fileInfo2(topDir.path(), QStringLiteral("collection2")); 0150 QFile file2(fileInfo2.absoluteFilePath()); 0151 QVERIFY(file2.open(QIODevice::WriteOnly)); 0152 file2.close(); 0153 QVERIFY(file2.exists()); 0154 QCOMPARE((int)file2.size(), 0); 0155 0156 mStore->setPath(topDir.path()); 0157 0158 // common variables 0159 const QVariant colListVar = QVariant::fromValue<Collection::List>(Collection::List()); 0160 QVariant var; 0161 Collection::List collections; 0162 Item::List items; 0163 QMap<QByteArray, int> flagCounts; 0164 0165 FileStore::ItemCreateJob *job = nullptr; 0166 FileStore::ItemFetchJob *itemFetch = nullptr; 0167 0168 // test adding to empty mbox 0169 Collection collection2; 0170 collection2.setName(QStringLiteral("collection2")); 0171 collection2.setRemoteId(QStringLiteral("collection2")); 0172 collection2.setParentCollection(mStore->topLevelCollection()); 0173 0174 Item item1; 0175 item1.setId(QRandomGenerator::global()->generate()); 0176 item1.setMimeType(KMime::Message::mimeType()); 0177 item1.setPayload<KMime::Message::Ptr>(msgPtr1); 0178 0179 job = mStore->createItem(item1, collection2); 0180 0181 QVERIFY(job->exec()); 0182 QCOMPARE(job->error(), 0); 0183 0184 Item item = job->item(); 0185 QCOMPARE(item.id(), item1.id()); 0186 QVERIFY(!item.remoteId().isEmpty()); 0187 QCOMPARE(item.remoteId(), QStringLiteral("0")); 0188 QCOMPARE(item.parentCollection(), collection2); 0189 0190 fileInfo2.refresh(); 0191 QVERIFY(fileInfo2.size() > 0); 0192 const int size2 = fileInfo2.size(); 0193 0194 KMBox::MBox mbox2; 0195 QVERIFY(mbox2.load(fileInfo2.absoluteFilePath())); 0196 QCOMPARE((int)mbox2.entries().count(), 1); 0197 0198 Item item2; 0199 item2.setId(QRandomGenerator::global()->generate()); 0200 item2.setMimeType(KMime::Message::mimeType()); 0201 item2.setPayload<KMime::Message::Ptr>(msgPtr2); 0202 0203 job = mStore->createItem(item2, collection2); 0204 0205 QVERIFY(job->exec()); 0206 QCOMPARE(job->error(), 0); 0207 0208 item = job->item(); 0209 QCOMPARE(item.id(), item2.id()); 0210 QVERIFY(!item.remoteId().isEmpty()); 0211 QCOMPARE(item.remoteId(), QString::number(size2 + 1)); 0212 QCOMPARE(item.parentCollection(), collection2); 0213 0214 fileInfo2.refresh(); 0215 QVERIFY(fileInfo2.size() > 0); 0216 0217 QVERIFY(mbox2.load(fileInfo2.absoluteFilePath())); 0218 QCOMPARE((int)mbox2.entries().count(), 2); 0219 0220 // test adding to non-empty mbox 0221 Collection collection1; 0222 collection1.setName(QStringLiteral("collection1")); 0223 collection1.setRemoteId(QStringLiteral("collection1")); 0224 collection1.setParentCollection(mStore->topLevelCollection()); 0225 0226 job = mStore->createItem(item1, collection1); 0227 0228 QVERIFY(job->exec()); 0229 QCOMPARE(job->error(), 0); 0230 0231 item = job->item(); 0232 QCOMPARE(item.id(), item1.id()); 0233 QVERIFY(!item.remoteId().isEmpty()); 0234 QCOMPARE(item.remoteId(), QString::number(size1 + 1)); 0235 QCOMPARE(item.parentCollection(), collection1); 0236 0237 fileInfo1.refresh(); 0238 QVERIFY(fileInfo1.size() > size1); 0239 0240 QVERIFY(mbox1.load(fileInfo1.absoluteFilePath())); 0241 QCOMPARE((int)mbox1.entries().count(), 5); 0242 0243 // check for index preservation 0244 var = job->property("onDiskIndexInvalidated"); 0245 QVERIFY(var.isValid()); 0246 QCOMPARE(var.userType(), colListVar.userType()); 0247 0248 collections = var.value<Collection::List>(); 0249 QCOMPARE((int)collections.count(), 1); 0250 QCOMPARE(collections.first(), collection1); 0251 0252 // get the items and check the flags (see data/README) 0253 itemFetch = mStore->fetchItems(collection1); 0254 QVERIFY(itemFetch->exec()); 0255 QCOMPARE(itemFetch->error(), 0); 0256 0257 items = itemFetch->items(); 0258 QCOMPARE((int)items.count(), 5); 0259 for (const Item &item : std::as_const(items)) { 0260 const auto flags{item.flags()}; 0261 for (const QByteArray &flag : flags) { 0262 ++flagCounts[flag]; 0263 } 0264 } 0265 0266 QCOMPARE(flagCounts["\\SEEN"], 2); 0267 QCOMPARE(flagCounts["\\FLAGGED"], 1); 0268 QCOMPARE(flagCounts["$TODO"], 1); 0269 flagCounts.clear(); 0270 0271 job = mStore->createItem(item2, collection1); 0272 0273 QVERIFY(job->exec()); 0274 QCOMPARE(job->error(), 0); 0275 0276 item = job->item(); 0277 QCOMPARE(item.id(), item2.id()); 0278 QVERIFY(!item.remoteId().isEmpty()); 0279 QCOMPARE(item.remoteId(), QString::number(size1 + 1 + size2 + 1)); 0280 QCOMPARE(item.parentCollection(), collection1); 0281 0282 fileInfo1.refresh(); 0283 QVERIFY(fileInfo1.size() > (size1 + size2)); 0284 0285 QVERIFY(mbox1.load(fileInfo1.absoluteFilePath())); 0286 QCOMPARE((int)mbox1.entries().count(), 6); 0287 0288 // check for index preservation 0289 var = job->property("onDiskIndexInvalidated"); 0290 QVERIFY(var.isValid()); 0291 QCOMPARE(var.userType(), colListVar.userType()); 0292 0293 collections = var.value<Collection::List>(); 0294 QCOMPARE((int)collections.count(), 1); 0295 QCOMPARE(collections.first(), collection1); 0296 0297 // get the items and check the flags (see data/README) 0298 itemFetch = mStore->fetchItems(collection1); 0299 QVERIFY(itemFetch->exec()); 0300 QCOMPARE(itemFetch->error(), 0); 0301 0302 items = itemFetch->items(); 0303 QCOMPARE((int)items.count(), 6); 0304 for (const Item &item : std::as_const(items)) { 0305 const auto flags{item.flags()}; 0306 for (const QByteArray &flag : flags) { 0307 ++flagCounts[flag]; 0308 } 0309 } 0310 0311 QCOMPARE(flagCounts["\\SEEN"], 2); 0312 QCOMPARE(flagCounts["\\FLAGGED"], 1); 0313 QCOMPARE(flagCounts["$TODO"], 1); 0314 flagCounts.clear(); 0315 } 0316 0317 void ItemCreateTest::testMaildir() 0318 { 0319 QDir topDir(mDir->path()); 0320 0321 QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir"), topDir.path(), QStringLiteral("data"))); 0322 QDir dataDir = topDir; 0323 QVERIFY(dataDir.cd(QLatin1StringView("data"))); 0324 KPIM::Maildir dataMd(dataDir.path(), false); 0325 QVERIFY(dataMd.isValid()); 0326 0327 const QStringList dataEntryList = dataMd.entryList(); 0328 QCOMPARE(dataEntryList.count(), 4); 0329 KMime::Message::Ptr msgPtr1(new KMime::Message); 0330 msgPtr1->setContent(KMime::CRLFtoLF(dataMd.readEntry(dataEntryList.first()))); 0331 KMime::Message::Ptr msgPtr2(new KMime::Message); 0332 msgPtr2->setContent(KMime::CRLFtoLF(dataMd.readEntry(dataEntryList.last()))); 0333 0334 QVERIFY(topDir.mkdir(QLatin1StringView("store"))); 0335 QVERIFY(topDir.cd(QLatin1StringView("store"))); 0336 0337 QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir"), topDir.path(), QStringLiteral("collection1"))); 0338 0339 KPIM::Maildir topLevelMd(topDir.path(), true); 0340 KPIM::Maildir md1 = topLevelMd.subFolder(QStringLiteral("collection1")); 0341 QVERIFY(md1.isValid()); 0342 0343 const QStringList md1EntryList = md1.entryList(); 0344 QSet<QString> entrySet1(md1EntryList.cbegin(), md1EntryList.cend()); 0345 QCOMPARE((int)entrySet1.count(), 4); 0346 0347 // simulate empty maildir 0348 KPIM::Maildir md2(topLevelMd.addSubFolder(QStringLiteral("collection2")), false); 0349 QVERIFY(md2.isValid()); 0350 0351 const QStringList md2EntryList = md2.entryList(); 0352 QSet<QString> entrySet2(md2EntryList.cbegin(), md2EntryList.cend()); 0353 QCOMPARE((int)entrySet2.count(), 0); 0354 0355 mStore->setPath(topDir.path()); 0356 0357 // common variables 0358 const QVariant colListVar = QVariant::fromValue<Collection::List>(Collection::List()); 0359 QVariant var; 0360 Collection::List collections; 0361 Item::List items; 0362 QMap<QByteArray, int> flagCounts; 0363 0364 QStringList entryList; 0365 QSet<QString> entrySet; 0366 QSet<QString> newIdSet; 0367 QString newId; 0368 0369 FileStore::ItemCreateJob *job = nullptr; 0370 FileStore::ItemFetchJob *itemFetch = nullptr; 0371 0372 // test adding to empty maildir 0373 Collection collection2; 0374 collection2.setName(QStringLiteral("collection2")); 0375 collection2.setRemoteId(QStringLiteral("collection2")); 0376 collection2.setParentCollection(mStore->topLevelCollection()); 0377 0378 Item item1; 0379 item1.setId(QRandomGenerator::global()->generate()); 0380 item1.setMimeType(KMime::Message::mimeType()); 0381 item1.setPayload<KMime::Message::Ptr>(msgPtr1); 0382 0383 job = mStore->createItem(item1, collection2); 0384 0385 QVERIFY(job->exec()); 0386 QCOMPARE(job->error(), 0); 0387 0388 Item item = job->item(); 0389 QCOMPARE(item.id(), item1.id()); 0390 QVERIFY(!item.remoteId().isEmpty()); 0391 QCOMPARE(item.parentCollection(), collection2); 0392 0393 entryList = md2.entryList(); 0394 entrySet = QSet<QString>(entryList.cbegin(), entryList.cend()); 0395 QCOMPARE((int)entrySet.count(), 1); 0396 0397 newIdSet = entrySet.subtract(entrySet2); 0398 QCOMPARE((int)newIdSet.count(), 1); 0399 0400 newId = *newIdSet.cbegin(); 0401 QCOMPARE(item.remoteId(), newId); 0402 entrySet2 << newId; 0403 QCOMPARE((int)entrySet2.count(), 1); 0404 0405 Item item2; 0406 item2.setId(QRandomGenerator::global()->generate()); 0407 item2.setMimeType(KMime::Message::mimeType()); 0408 item2.setPayload<KMime::Message::Ptr>(msgPtr2); 0409 0410 job = mStore->createItem(item2, collection2); 0411 0412 QVERIFY(job->exec()); 0413 QCOMPARE(job->error(), 0); 0414 0415 item = job->item(); 0416 QCOMPARE(item.id(), item2.id()); 0417 QVERIFY(!item.remoteId().isEmpty()); 0418 QCOMPARE(item.parentCollection(), collection2); 0419 0420 entryList = md2.entryList(); 0421 entrySet = QSet<QString>(entryList.cbegin(), entryList.cend()); 0422 QCOMPARE((int)entrySet.count(), 2); 0423 0424 newIdSet = entrySet.subtract(entrySet2); 0425 QCOMPARE((int)newIdSet.count(), 1); 0426 0427 newId = *newIdSet.cbegin(); 0428 QCOMPARE(item.remoteId(), newId); 0429 entrySet2 << newId; 0430 QCOMPARE((int)entrySet2.count(), 2); 0431 0432 // test adding to non-empty maildir 0433 Collection collection1; 0434 collection1.setName(QStringLiteral("collection1")); 0435 collection1.setRemoteId(QStringLiteral("collection1")); 0436 collection1.setParentCollection(mStore->topLevelCollection()); 0437 0438 job = mStore->createItem(item1, collection1); 0439 0440 QVERIFY(job->exec()); 0441 QCOMPARE(job->error(), 0); 0442 0443 item = job->item(); 0444 QCOMPARE(item.id(), item1.id()); 0445 QVERIFY(!item.remoteId().isEmpty()); 0446 QCOMPARE(item.parentCollection(), collection1); 0447 0448 entryList = md1.entryList(); 0449 entrySet = QSet<QString>(entryList.cbegin(), entryList.cend()); 0450 QCOMPARE((int)entrySet.count(), 5); 0451 0452 newIdSet = entrySet.subtract(entrySet1); 0453 QCOMPARE((int)newIdSet.count(), 1); 0454 0455 newId = *newIdSet.cbegin(); 0456 QCOMPARE(item.remoteId(), newId); 0457 entrySet1 << newId; 0458 QCOMPARE((int)entrySet1.count(), 5); 0459 0460 // check for index preservation 0461 var = job->property("onDiskIndexInvalidated"); 0462 QVERIFY(var.isValid()); 0463 QCOMPARE(var.userType(), colListVar.userType()); 0464 0465 collections = var.value<Collection::List>(); 0466 QCOMPARE((int)collections.count(), 1); 0467 QCOMPARE(collections.first(), collection1); 0468 0469 // get the items and check the flags (see data/README) 0470 itemFetch = mStore->fetchItems(collection1); 0471 QVERIFY(itemFetch->exec()); 0472 QCOMPARE(itemFetch->error(), 0); 0473 0474 items = itemFetch->items(); 0475 QCOMPARE((int)items.count(), 5); 0476 for (const Item &item : std::as_const(items)) { 0477 const auto flags{item.flags()}; 0478 for (const QByteArray &flag : flags) { 0479 ++flagCounts[flag]; 0480 } 0481 } 0482 0483 QCOMPARE(flagCounts["\\SEEN"], 2); 0484 QCOMPARE(flagCounts["\\FLAGGED"], 1); 0485 QCOMPARE(flagCounts["$TODO"], 1); 0486 flagCounts.clear(); 0487 0488 job = mStore->createItem(item2, collection1); 0489 0490 QVERIFY(job->exec()); 0491 QCOMPARE(job->error(), 0); 0492 0493 item = job->item(); 0494 QCOMPARE(item.id(), item2.id()); 0495 QVERIFY(!item.remoteId().isEmpty()); 0496 QCOMPARE(item.parentCollection(), collection1); 0497 0498 entryList = md1.entryList(); 0499 entrySet = QSet<QString>(entryList.cbegin(), entryList.cend()); 0500 QCOMPARE((int)entrySet.count(), 6); 0501 0502 newIdSet = entrySet.subtract(entrySet1); 0503 QCOMPARE((int)newIdSet.count(), 1); 0504 0505 newId = *newIdSet.cbegin(); 0506 QCOMPARE(item.remoteId(), newId); 0507 entrySet1 << newId; 0508 QCOMPARE((int)entrySet1.count(), 6); 0509 0510 // check for index preservation 0511 var = job->property("onDiskIndexInvalidated"); 0512 QVERIFY(var.isValid()); 0513 QCOMPARE(var.userType(), colListVar.userType()); 0514 0515 collections = var.value<Collection::List>(); 0516 QCOMPARE((int)collections.count(), 1); 0517 QCOMPARE(collections.first(), collection1); 0518 0519 // get the items and check the flags (see data/README) 0520 itemFetch = mStore->fetchItems(collection1); 0521 QVERIFY(itemFetch->exec()); 0522 QCOMPARE(itemFetch->error(), 0); 0523 0524 items = itemFetch->items(); 0525 QCOMPARE((int)items.count(), 6); 0526 for (const Item &item : std::as_const(items)) { 0527 const auto flags{item.flags()}; 0528 for (const QByteArray &flag : flags) { 0529 ++flagCounts[flag]; 0530 } 0531 } 0532 0533 QCOMPARE(flagCounts["\\SEEN"], 2); 0534 QCOMPARE(flagCounts["\\FLAGGED"], 1); 0535 QCOMPARE(flagCounts["$TODO"], 1); 0536 flagCounts.clear(); 0537 } 0538 0539 QTEST_MAIN(ItemCreateTest) 0540 0541 #include "itemcreatetest.moc"