File indexing completed on 2024-12-22 04:57:37

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 "mixedmaildirresource_debug.h"
0009 #include "mixedmaildirstore.h"
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 <Akonadi/MessageParts>
0020 
0021 #include <Akonadi/ItemFetchScope>
0022 
0023 #include <KRandom>
0024 #include <KRandomSequence>
0025 #include <QTemporaryDir>
0026 
0027 #include <QSignalSpy>
0028 
0029 #include <QDir>
0030 #include <QFileInfo>
0031 #include <QTest>
0032 using namespace Akonadi;
0033 using namespace KMBox;
0034 
0035 static Item::List itemsFromSpy(QSignalSpy *spy)
0036 {
0037     Item::List items;
0038 
0039     QListIterator<QList<QVariant>> it(*spy);
0040     while (it.hasNext()) {
0041         const QList<QVariant> invocation = it.next();
0042         Q_ASSERT(invocation.count() == 1);
0043 
0044         items << invocation.first().value<Item::List>();
0045     }
0046 
0047     return items;
0048 }
0049 
0050 // copied from mail serializer plugin, SPDX-FileCopyrightText: 2007 Till Adam <adam@kde.org>
0051 static QSet<QByteArray> messageParts(const KMime::Message::Ptr &msgPtr)
0052 {
0053     QSet<QByteArray> set;
0054     // FIXME: we actually want "has any header" here, but the kmime api doesn't offer that yet
0055     if (msgPtr->hasContent() || msgPtr->hasHeader("Message-ID")) {
0056         set << MessagePart::Envelope << MessagePart::Header;
0057         if (!msgPtr->body().isEmpty() || !msgPtr->contents().isEmpty()) {
0058             set << MessagePart::Body;
0059         }
0060     }
0061     return set;
0062 }
0063 
0064 // needed to sort maildir directory entries by filename which is their
0065 // remoteId. tagListHash.contains tests below need sorting of entries.
0066 static bool itemLessThanByRemoteId(const Item &item1, const Item &item2)
0067 {
0068     return item1.remoteId() < item2.remoteId();
0069 }
0070 
0071 class ItemFetchTest : public QObject
0072 {
0073     Q_OBJECT
0074 
0075 public:
0076     ItemFetchTest()
0077         : QObject()
0078         , mStore(0)
0079         , mDir(0)
0080         , mIndexFilePattern(QLatin1StringView(".%1.index"))
0081     {
0082         // for monitoring signals
0083         qRegisterMetaType<Akonadi::Collection::List>();
0084         qRegisterMetaType<Akonadi::Item::List>();
0085     }
0086 
0087     ~ItemFetchTest()
0088     {
0089         delete mStore;
0090         delete mDir;
0091     }
0092 
0093     QString indexFile(const QString &folder) const
0094     {
0095         return mIndexFilePattern.arg(folder);
0096     }
0097 
0098     QString indexFile(const QFileInfo &folderFileInfo) const
0099     {
0100         return QFileInfo(folderFileInfo.absolutePath(), mIndexFilePattern.arg(folderFileInfo.fileName())).absoluteFilePath();
0101     }
0102 
0103 private:
0104     MixedMaildirStore *mStore;
0105     QTemporaryDir *mDir;
0106 
0107     const QString mIndexFilePattern;
0108 
0109 private Q_SLOTS:
0110     void init();
0111     void cleanup();
0112     void testListingMaildir();
0113     void testListingMBox();
0114     void testSingleItemFetchMaildir();
0115     void testSingleItemFetchMBox();
0116 };
0117 
0118 void ItemFetchTest::init()
0119 {
0120     mStore = new MixedMaildirStore;
0121 
0122     mDir = new QTemporaryDir;
0123     QVERIFY(mDir->isValid());
0124     QVERIFY(QDir(mDir->path()).exists());
0125 }
0126 
0127 void ItemFetchTest::cleanup()
0128 {
0129     delete mStore;
0130     mStore = 0;
0131     delete mDir;
0132     mDir = 0;
0133 }
0134 
0135 void ItemFetchTest::testListingMaildir()
0136 {
0137     QDir topDir(mDir->path());
0138 
0139     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir"), topDir.path(), QLatin1StringView("collection1")));
0140     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir"), topDir.path(), QLatin1StringView("collection2")));
0141     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir-tagged"), topDir.path(), QLatin1StringView("collection3")));
0142     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("dimap"), topDir.path(), QLatin1StringView("collection4")));
0143     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir-tagged"), topDir.path(), QLatin1StringView("collection5")));
0144 
0145     KPIM::Maildir topLevelMd(topDir.path(), true);
0146 
0147     KPIM::Maildir md1 = topLevelMd.subFolder(QLatin1StringView("collection1"));
0148     const QStringList md1EntryList = md1.entryList();
0149     QSet<QString> entrySet1(md1EntryList.cbegin().md1EntryList.cend());
0150     QCOMPARE((int)entrySet1.count(), 4);
0151 
0152     QFileInfo indexFileInfo1(indexFile(QFileInfo(md1.path())));
0153     QVERIFY(QFile::remove(indexFileInfo1.absoluteFilePath()));
0154 
0155     KPIM::Maildir md2 = topLevelMd.subFolder(QLatin1StringView("collection2"));
0156     const QStringList md2EntryList = md2.entryList();
0157     QSet<QString> entrySet2(md2EntryList.cbegin(), md2EntryList.cend());
0158     QCOMPARE((int)entrySet2.count(), 4);
0159 
0160     KPIM::Maildir md3 = topLevelMd.subFolder(QLatin1StringView("collection3"));
0161     const QStringList md3EntryList = md3.entryList();
0162     QSet<QString> entrySet3(md3EntryList.cbegin(), md3EntryList.cend());
0163     QCOMPARE((int)entrySet3.count(), 4);
0164 
0165     KPIM::Maildir md4 = topLevelMd.subFolder(QLatin1StringView("collection4"));
0166     const QStringList md4EntryList = md4.entryList();
0167     QSet<QString> entrySet4(md4EntryList.cbegin(), md4EntryList.cend());
0168     QCOMPARE((int)entrySet4.count(), 4);
0169 
0170     KPIM::Maildir md5 = topLevelMd.subFolder(QLatin1StringView("collection5"));
0171     const QStringList md5EntryList = md5.entryList();
0172     QSet<QString> entrySet5(md5EntryList.cbegin(), md5EntryList.cend());
0173     QCOMPARE((int)entrySet5.count(), 4);
0174 
0175     mStore->setPath(topDir.path());
0176 
0177     // common variables
0178     FileStore::ItemFetchJob *job = 0;
0179 
0180     QSignalSpy *spy = 0;
0181     Item::List items;
0182 
0183     QHash<QString, QVariant> uidHash;
0184     const QVariant varUidHash = QVariant::fromValue<QHash<QString, QVariant>>(uidHash);
0185     QHash<QString, QVariant> tagListHash;
0186     const QVariant varTagListHash = QVariant::fromValue<QHash<QString, QVariant>>(tagListHash);
0187     QVariant var;
0188 
0189     QSet<QString> entrySet;
0190     QMap<QByteArray, int> flagCounts;
0191 
0192     // test listing maildir without index
0193     Collection collection1;
0194     collection1.setName(QLatin1StringView("collection1"));
0195     collection1.setRemoteId(QLatin1StringView("collection1"));
0196     collection1.setParentCollection(mStore->topLevelCollection());
0197 
0198     job = mStore->fetchItems(collection1);
0199 
0200     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0201 
0202     QVERIFY(job->exec());
0203     QCOMPARE(job->error(), 0);
0204 
0205     items = job->items();
0206     QCOMPARE((int)items.count(), 4);
0207     QCOMPARE(itemsFromSpy(spy), items);
0208 
0209     entrySet = entrySet1;
0210     QVERIFY(entrySet.remove(items[0].remoteId()));
0211     QVERIFY(entrySet.remove(items[1].remoteId()));
0212     QVERIFY(entrySet.remove(items[2].remoteId()));
0213     QVERIFY(entrySet.remove(items[3].remoteId()));
0214 
0215     QCOMPARE(items[0].parentCollection(), collection1);
0216     QCOMPARE(items[1].parentCollection(), collection1);
0217     QCOMPARE(items[2].parentCollection(), collection1);
0218     QCOMPARE(items[3].parentCollection(), collection1);
0219 
0220     QVERIFY(!items[0].hasPayload<KMime::Message::Ptr>());
0221     QVERIFY(!items[1].hasPayload<KMime::Message::Ptr>());
0222     QVERIFY(!items[2].hasPayload<KMime::Message::Ptr>());
0223     QVERIFY(!items[3].hasPayload<KMime::Message::Ptr>());
0224 
0225     for (const Item &item : std::as_const(items)) {
0226         const auto flags{item.flags()};
0227         for (const QByteArray &flag : flags) {
0228             ++flagCounts[flag];
0229         }
0230     }
0231 
0232     // no flags from maildir file name, no advanced flags without index
0233     QCOMPARE(flagCounts.count(), 0);
0234     QCOMPARE(flagCounts["\\SEEN"], 0);
0235     QCOMPARE(flagCounts["\\FLAGGED"], 0);
0236     QCOMPARE(flagCounts["$TODO"], 0);
0237     flagCounts.clear();
0238 
0239     var = job->property("remoteIdToTagList");
0240     QVERIFY(!var.isValid());
0241 
0242     // test listing empty mbox without index
0243     for (const QString &entry, std::as_const(entrySet1)) {
0244         QVERIFY(md1.removeEntry(entry));
0245     }
0246     QCOMPARE(md1.entryList().count(), 0);
0247 
0248     job = mStore->fetchItems(collection1);
0249 
0250     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0251 
0252     QVERIFY(job->exec());
0253     QCOMPARE(job->error(), 0);
0254 
0255     items = job->items();
0256     QCOMPARE((int)items.count(), 0);
0257     QCOMPARE(spy->count(), 0);
0258 
0259     var = job->property("remoteIdToTagList");
0260     QVERIFY(!var.isValid());
0261 
0262     // test listing maildir with index
0263     Collection collection2;
0264     collection2.setName(QLatin1StringView("collection2"));
0265     collection2.setRemoteId(QLatin1StringView("collection2"));
0266     collection2.setParentCollection(mStore->topLevelCollection());
0267 
0268     job = mStore->fetchItems(collection2);
0269 
0270     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0271 
0272     QVERIFY(job->exec());
0273     QCOMPARE(job->error(), 0);
0274 
0275     items = job->items();
0276     QCOMPARE((int)items.count(), 4);
0277     QCOMPARE(itemsFromSpy(spy), items);
0278 
0279     entrySet = entrySet2;
0280     QVERIFY(entrySet.remove(items[0].remoteId()));
0281     QVERIFY(entrySet.remove(items[1].remoteId()));
0282     QVERIFY(entrySet.remove(items[2].remoteId()));
0283     QVERIFY(entrySet.remove(items[3].remoteId()));
0284 
0285     QCOMPARE(items[0].parentCollection(), collection2);
0286     QCOMPARE(items[1].parentCollection(), collection2);
0287     QCOMPARE(items[2].parentCollection(), collection2);
0288     QCOMPARE(items[3].parentCollection(), collection2);
0289 
0290     QVERIFY(!items[0].hasPayload<KMime::Message::Ptr>());
0291     QVERIFY(!items[1].hasPayload<KMime::Message::Ptr>());
0292     QVERIFY(!items[2].hasPayload<KMime::Message::Ptr>());
0293     QVERIFY(!items[3].hasPayload<KMime::Message::Ptr>());
0294 
0295     // see data/README
0296     for (const Item &item : std::as_const(items)) {
0297         const auto flags{item.flags()};
0298         for (const QByteArray &flag : flags) {
0299             ++flagCounts[flag];
0300         }
0301     }
0302     QCOMPARE(flagCounts["\\SEEN"], 2);
0303     QCOMPARE(flagCounts["\\FLAGGED"], 1);
0304     QCOMPARE(flagCounts["$TODO"], 1);
0305     flagCounts.clear();
0306 
0307     var = job->property("remoteIdToTagList");
0308     QVERIFY(!var.isValid());
0309 
0310     // test listing empty maildir with index
0311     for (const QString &entry : std::as_const(entrySet2)) {
0312         QVERIFY(md2.removeEntry(entry));
0313     }
0314     QCOMPARE(md2.entryList().count(), 0);
0315 
0316     job = mStore->fetchItems(collection2);
0317 
0318     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0319 
0320     QVERIFY(job->exec());
0321     QCOMPARE(job->error(), 0);
0322 
0323     items = job->items();
0324     QCOMPARE((int)items.count(), 0);
0325     QCOMPARE(spy->count(), 0);
0326 
0327     var = job->property("remoteIdToTagList");
0328     QVERIFY(!var.isValid());
0329 
0330     // test listing maildir with index which has tags
0331     Collection collection3;
0332     collection3.setName(QLatin1StringView("collection3"));
0333     collection3.setRemoteId(QLatin1StringView("collection3"));
0334     collection3.setParentCollection(mStore->topLevelCollection());
0335 
0336     job = mStore->fetchItems(collection3);
0337 
0338     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0339 
0340     QVERIFY(job->exec());
0341     QCOMPARE(job->error(), 0);
0342 
0343     items = job->items();
0344     QCOMPARE((int)items.count(), 4);
0345     QCOMPARE(itemsFromSpy(spy), items);
0346 
0347     entrySet = entrySet3;
0348     QVERIFY(entrySet.remove(items[0].remoteId()));
0349     QVERIFY(entrySet.remove(items[1].remoteId()));
0350     QVERIFY(entrySet.remove(items[2].remoteId()));
0351     QVERIFY(entrySet.remove(items[3].remoteId()));
0352 
0353     QCOMPARE(items[0].parentCollection(), collection3);
0354     QCOMPARE(items[1].parentCollection(), collection3);
0355     QCOMPARE(items[2].parentCollection(), collection3);
0356     QCOMPARE(items[3].parentCollection(), collection3);
0357 
0358     QVERIFY(!items[0].hasPayload<KMime::Message::Ptr>());
0359     QVERIFY(!items[1].hasPayload<KMime::Message::Ptr>());
0360     QVERIFY(!items[2].hasPayload<KMime::Message::Ptr>());
0361     QVERIFY(!items[3].hasPayload<KMime::Message::Ptr>());
0362 
0363     // see data/README
0364     for (const Item &item : std::as_const(items)) {
0365         const auto flags{item.flags()};
0366         for (const QByteArray &flag : flags) {
0367             ++flagCounts[flag];
0368         }
0369     }
0370 
0371     // 2x \SEEN flags: 2x from index, none from file name
0372     QCOMPARE(flagCounts["\\SEEN"], 2);
0373     QCOMPARE(flagCounts["\\FLAGGED"], 1);
0374     QCOMPARE(flagCounts["$TODO"], 1);
0375     flagCounts.clear();
0376 
0377     var = job->property("remoteIdToTagList");
0378     QVERIFY(var.isValid());
0379 
0380     // tagListHash.contains tests below needs sorting of entries,
0381     // but libmaildir does not sort for performance reasons.
0382     // TODO: Check should not depend on any specific ordering.
0383     qSort(items.begin(), items.end(), itemLessThanByRemoteId);
0384 
0385     tagListHash = var.value<QHash<QString, QVariant>>();
0386     QCOMPARE((int)tagListHash.count(), 3);
0387     QVERIFY(!tagListHash.contains(items[0].remoteId()));
0388     QVERIFY(!tagListHash.value(items[1].remoteId()).toString().isEmpty());
0389     QVERIFY(!tagListHash.value(items[2].remoteId()).toString().isEmpty());
0390     QVERIFY(!tagListHash.value(items[3].remoteId()).toString().isEmpty());
0391 
0392     // test listing maildir with index which contains IMAP UIDs (dimap cache directory)
0393     Collection collection4;
0394     collection4.setName(QLatin1StringView("collection4"));
0395     collection4.setRemoteId(QLatin1StringView("collection4"));
0396     collection4.setParentCollection(mStore->topLevelCollection());
0397 
0398     job = mStore->fetchItems(collection4);
0399 
0400     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0401 
0402     QVERIFY(job->exec());
0403     QCOMPARE(job->error(), 0);
0404 
0405     items = job->items();
0406     QCOMPARE((int)items.count(), 4);
0407     QCOMPARE(itemsFromSpy(spy), items);
0408 
0409     entrySet = entrySet4;
0410     QVERIFY(entrySet.remove(items[0].remoteId()));
0411     QVERIFY(entrySet.remove(items[1].remoteId()));
0412     QVERIFY(entrySet.remove(items[2].remoteId()));
0413     QVERIFY(entrySet.remove(items[3].remoteId()));
0414 
0415     QCOMPARE(items[0].parentCollection(), collection4);
0416     QCOMPARE(items[1].parentCollection(), collection4);
0417     QCOMPARE(items[2].parentCollection(), collection4);
0418     QCOMPARE(items[3].parentCollection(), collection4);
0419 
0420     QVERIFY(!items[0].hasPayload<KMime::Message::Ptr>());
0421     QVERIFY(!items[1].hasPayload<KMime::Message::Ptr>());
0422     QVERIFY(!items[2].hasPayload<KMime::Message::Ptr>());
0423     QVERIFY(!items[3].hasPayload<KMime::Message::Ptr>());
0424 
0425     // see data/README
0426     for (const Item &item : std::as_const(items)) {
0427         const auto flags{item.flags()};
0428         for (const QByteArray &flag : flags) {
0429             ++flagCounts[flag];
0430         }
0431     }
0432     QCOMPARE(flagCounts["\\SEEN"], 2);
0433     QCOMPARE(flagCounts["\\FLAGGED"], 1);
0434     QCOMPARE(flagCounts["$TODO"], 1);
0435     flagCounts.clear();
0436 
0437     var = job->property("remoteIdToTagList");
0438     QVERIFY(!var.isValid());
0439 
0440     var = job->property("remoteIdToIndexUid");
0441     QVERIFY(var.isValid());
0442 
0443     uidHash = var.value<QHash<QString, QVariant>>();
0444     QCOMPARE((int)uidHash.count(), 4);
0445     bool ok = false;
0446     QVERIFY(!uidHash.value(items[0].remoteId()).toString().toInt(&ok) >= 0 && ok);
0447     QVERIFY(!uidHash.value(items[1].remoteId()).toString().toInt(&ok) >= 0 && ok);
0448     QVERIFY(!uidHash.value(items[2].remoteId()).toString().toInt(&ok) >= 0 && ok);
0449     QVERIFY(!uidHash.value(items[3].remoteId()).toString().toInt(&ok) >= 0 && ok);
0450 
0451     // test listing maildir with index but newer modification date than index's one
0452     const QByteArray data5 = md5.readEntry(*entrySet5.cbegin());
0453     QVERIFY(!data5.isEmpty());
0454 
0455     QTest::qSleep(1000);
0456 
0457     QString newRemoteId = md5.addEntry(data5);
0458     QVERIFY(!newRemoteId.isEmpty());
0459 
0460     const QStringList md5EntryList = md5.entryList();
0461     entrySet = QSet<QString>(md5EntryList.cbegin(), md5EntryList.cend());
0462     entrySet.remove(newRemoteId);
0463     QCOMPARE(entrySet, entrySet5);
0464     QFileInfo fileInfo5(md5.path(), QLatin1StringView("new"));
0465     QFileInfo indexFileInfo5 = indexFile(QFileInfo(md5.path()));
0466     QVERIFY(fileInfo5.lastModified() > indexFileInfo5.lastModified());
0467 
0468     Collection collection5;
0469     collection5.setName(QLatin1StringView("collection5"));
0470     collection5.setRemoteId(QLatin1StringView("collection5"));
0471     collection5.setParentCollection(mStore->topLevelCollection());
0472 
0473     job = mStore->fetchItems(collection5);
0474 
0475     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0476 
0477     QVERIFY(job->exec());
0478     QCOMPARE(job->error(), 0);
0479 
0480     items = job->items();
0481     QCOMPARE((int)items.count(), 5);
0482     QCOMPARE(itemsFromSpy(spy), items);
0483 
0484     entrySet = entrySet5;
0485     entrySet << newRemoteId;
0486     QVERIFY(entrySet.remove(items[0].remoteId()));
0487     QVERIFY(entrySet.remove(items[1].remoteId()));
0488     QVERIFY(entrySet.remove(items[2].remoteId()));
0489     QVERIFY(entrySet.remove(items[3].remoteId()));
0490     QVERIFY(entrySet.remove(items[4].remoteId()));
0491 
0492     QCOMPARE(items[0].parentCollection(), collection5);
0493     QCOMPARE(items[1].parentCollection(), collection5);
0494     QCOMPARE(items[2].parentCollection(), collection5);
0495     QCOMPARE(items[3].parentCollection(), collection5);
0496     QCOMPARE(items[4].parentCollection(), collection5);
0497 
0498     QVERIFY(!items[0].hasPayload<KMime::Message::Ptr>());
0499     QVERIFY(!items[1].hasPayload<KMime::Message::Ptr>());
0500     QVERIFY(!items[2].hasPayload<KMime::Message::Ptr>());
0501     QVERIFY(!items[3].hasPayload<KMime::Message::Ptr>());
0502     QVERIFY(!items[4].hasPayload<KMime::Message::Ptr>());
0503 
0504     // not flags from index, no flags from file names
0505     QCOMPARE(items[0].flags(), QSet<QByteArray>());
0506     QCOMPARE(items[1].flags(), QSet<QByteArray>());
0507     QCOMPARE(items[2].flags(), QSet<QByteArray>());
0508     QCOMPARE(items[3].flags(), QSet<QByteArray>());
0509     QCOMPARE(items[4].flags(), QSet<QByteArray>());
0510 
0511     var = job->property("remoteIdToTagList");
0512     QVERIFY(!var.isValid());
0513 }
0514 
0515 void ItemFetchTest::testListingMBox()
0516 {
0517     QDir topDir(mDir->path());
0518 
0519     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("mbox"), topDir.path(), QLatin1StringView("collection1")));
0520     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("mbox"), topDir.path(), QLatin1StringView("collection2")));
0521     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("mbox-tagged"), topDir.path(), QLatin1StringView("collection3")));
0522     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("mbox-unpurged"), topDir.path(), QLatin1StringView("collection4")));
0523     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("mbox-tagged"), topDir.path(), QLatin1StringView("collection5")));
0524 
0525     QFileInfo fileInfo1(topDir.path(), QLatin1StringView("collection1"));
0526     MBox mbox1;
0527     QVERIFY(mbox1.load(fileInfo1.absoluteFilePath()));
0528     MBoxEntry::List entryList1 = mbox1.entries();
0529     QCOMPARE((int)entryList1.count(), 4);
0530 
0531     QFileInfo indexFileInfo1 = indexFile(fileInfo1);
0532     QVERIFY(QFile::remove(indexFileInfo1.absoluteFilePath()));
0533 
0534     QFileInfo fileInfo2(topDir.path(), QLatin1StringView("collection2"));
0535     MBox mbox2;
0536     QVERIFY(mbox2.load(fileInfo2.absoluteFilePath()));
0537     MBoxEntry::List entryList2 = mbox2.entries();
0538     QCOMPARE((int)entryList2.count(), 4);
0539 
0540     QFileInfo fileInfo3(topDir.path(), QLatin1StringView("collection3"));
0541     MBox mbox3;
0542     QVERIFY(mbox3.load(fileInfo3.absoluteFilePath()));
0543     MBoxEntry::List entryList3 = mbox3.entries();
0544     QCOMPARE((int)entryList3.count(), 4);
0545 
0546     QFileInfo fileInfo4(topDir.path(), QLatin1StringView("collection4"));
0547     MBox mbox4;
0548     QVERIFY(mbox4.load(fileInfo4.absoluteFilePath()));
0549     MBoxEntry::List entryList4 = mbox4.entries();
0550     QCOMPARE((int)entryList4.count(), 4);
0551 
0552     QFileInfo fileInfo5(topDir.path(), QLatin1StringView("collection5"));
0553     MBox mbox5;
0554     QVERIFY(mbox5.load(fileInfo5.absoluteFilePath()));
0555     MBoxEntry::List entryList5 = mbox5.entries();
0556     QCOMPARE((int)entryList5.count(), 4);
0557 
0558     mStore->setPath(topDir.path());
0559 
0560     // common variables
0561     FileStore::ItemFetchJob *job = 0;
0562 
0563     QSignalSpy *spy = 0;
0564     Item::List items;
0565 
0566     QHash<QString, QVariant> tagListHash;
0567     const QVariant varTagListHash = QVariant::fromValue<QHash<QString, QVariant>>(tagListHash);
0568     QVariant var;
0569 
0570     // test listing mbox without index
0571     Collection collection1;
0572     collection1.setName(QLatin1StringView("collection1"));
0573     collection1.setRemoteId(QLatin1StringView("collection1"));
0574     collection1.setParentCollection(mStore->topLevelCollection());
0575 
0576     job = mStore->fetchItems(collection1);
0577 
0578     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0579 
0580     QVERIFY(job->exec());
0581     QCOMPARE(job->error(), 0);
0582 
0583     items = job->items();
0584     QCOMPARE((int)items.count(), 4);
0585     QCOMPARE(itemsFromSpy(spy), items);
0586 
0587     QCOMPARE(items[0].remoteId(), QString::number(entryList1[0].messageOffset()));
0588     QCOMPARE(items[1].remoteId(), QString::number(entryList1[1].messageOffset()));
0589     QCOMPARE(items[2].remoteId(), QString::number(entryList1[2].messageOffset()));
0590     QCOMPARE(items[3].remoteId(), QString::number(entryList1[3].messageOffset()));
0591 
0592     QCOMPARE(items[0].parentCollection(), collection1);
0593     QCOMPARE(items[1].parentCollection(), collection1);
0594     QCOMPARE(items[2].parentCollection(), collection1);
0595     QCOMPARE(items[3].parentCollection(), collection1);
0596 
0597     QVERIFY(!items[0].hasPayload<KMime::Message::Ptr>());
0598     QVERIFY(!items[1].hasPayload<KMime::Message::Ptr>());
0599     QVERIFY(!items[2].hasPayload<KMime::Message::Ptr>());
0600     QVERIFY(!items[3].hasPayload<KMime::Message::Ptr>());
0601 
0602     QCOMPARE(items[0].flags(), QSet<QByteArray>());
0603     QCOMPARE(items[1].flags(), QSet<QByteArray>());
0604     QCOMPARE(items[2].flags(), QSet<QByteArray>());
0605     QCOMPARE(items[3].flags(), QSet<QByteArray>());
0606 
0607     var = job->property("remoteIdToTagList");
0608     QVERIFY(!var.isValid());
0609 
0610     // test listing empty mbox without index
0611     QFile file1(fileInfo1.absoluteFilePath());
0612     QVERIFY(file1.open(QIODevice::WriteOnly | QIODevice::Truncate));
0613     file1.close();
0614     QCOMPARE((int)file1.size(), 0);
0615 
0616     job = mStore->fetchItems(collection1);
0617 
0618     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0619 
0620     QVERIFY(job->exec());
0621     QCOMPARE(job->error(), 0);
0622 
0623     items = job->items();
0624     QCOMPARE((int)items.count(), 0);
0625     QCOMPARE(spy->count(), 0);
0626 
0627     var = job->property("remoteIdToTagList");
0628     QVERIFY(!var.isValid());
0629 
0630     // test listing mbox with index
0631     Collection collection2;
0632     collection2.setName(QLatin1StringView("collection2"));
0633     collection2.setRemoteId(QLatin1StringView("collection2"));
0634     collection2.setParentCollection(mStore->topLevelCollection());
0635 
0636     job = mStore->fetchItems(collection2);
0637 
0638     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0639 
0640     QVERIFY(job->exec());
0641     QCOMPARE(job->error(), 0);
0642 
0643     items = job->items();
0644     QCOMPARE((int)items.count(), 4);
0645     QCOMPARE(itemsFromSpy(spy), items);
0646 
0647     QCOMPARE(items[0].remoteId(), QString::number(entryList2[0].messageOffset()));
0648     QCOMPARE(items[1].remoteId(), QString::number(entryList2[1].messageOffset()));
0649     QCOMPARE(items[2].remoteId(), QString::number(entryList2[2].messageOffset()));
0650     QCOMPARE(items[3].remoteId(), QString::number(entryList2[3].messageOffset()));
0651 
0652     QCOMPARE(items[0].parentCollection(), collection2);
0653     QCOMPARE(items[1].parentCollection(), collection2);
0654     QCOMPARE(items[2].parentCollection(), collection2);
0655     QCOMPARE(items[3].parentCollection(), collection2);
0656 
0657     QVERIFY(!items[0].hasPayload<KMime::Message::Ptr>());
0658     QVERIFY(!items[1].hasPayload<KMime::Message::Ptr>());
0659     QVERIFY(!items[2].hasPayload<KMime::Message::Ptr>());
0660     QVERIFY(!items[3].hasPayload<KMime::Message::Ptr>());
0661 
0662     // see data/README
0663     QCOMPARE(items[0].flags(), QSet<QByteArray>());
0664     QCOMPARE(items[1].flags(),
0665              QSet<QByteArray>() << "\\SEEN"
0666                                 << "$TODO");
0667     QCOMPARE(items[2].flags(), QSet<QByteArray>());
0668     QCOMPARE(items[3].flags(),
0669              QSet<QByteArray>() << "\\SEEN"
0670                                 << "\\FLAGGED");
0671 
0672     var = job->property("remoteIdToTagList");
0673     QVERIFY(!var.isValid());
0674 
0675     // test listing empty mbox with index
0676     QFile file2(fileInfo2.absoluteFilePath());
0677     QVERIFY(file2.open(QIODevice::WriteOnly | QIODevice::Truncate));
0678     file2.close();
0679     QCOMPARE((int)file2.size(), 0);
0680 
0681     job = mStore->fetchItems(collection2);
0682 
0683     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0684 
0685     QVERIFY(job->exec());
0686     QCOMPARE(job->error(), 0);
0687 
0688     items = job->items();
0689     QCOMPARE((int)items.count(), 0);
0690     QCOMPARE(spy->count(), 0);
0691 
0692     var = job->property("remoteIdToTagList");
0693     QVERIFY(!var.isValid());
0694 
0695     // test listing mbox with index which has tags
0696     Collection collection3;
0697     collection3.setName(QLatin1StringView("collection3"));
0698     collection3.setRemoteId(QLatin1StringView("collection3"));
0699     collection3.setParentCollection(mStore->topLevelCollection());
0700 
0701     job = mStore->fetchItems(collection3);
0702 
0703     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0704 
0705     QVERIFY(job->exec());
0706     QCOMPARE(job->error(), 0);
0707 
0708     items = job->items();
0709     QCOMPARE((int)items.count(), 4);
0710     QCOMPARE(itemsFromSpy(spy), items);
0711 
0712     QCOMPARE(items[0].remoteId(), QString::number(entryList3[0].messageOffset()));
0713     QCOMPARE(items[1].remoteId(), QString::number(entryList3[1].messageOffset()));
0714     QCOMPARE(items[2].remoteId(), QString::number(entryList3[2].messageOffset()));
0715     QCOMPARE(items[3].remoteId(), QString::number(entryList3[3].messageOffset()));
0716 
0717     QCOMPARE(items[0].parentCollection(), collection3);
0718     QCOMPARE(items[1].parentCollection(), collection3);
0719     QCOMPARE(items[2].parentCollection(), collection3);
0720     QCOMPARE(items[3].parentCollection(), collection3);
0721 
0722     QVERIFY(!items[0].hasPayload<KMime::Message::Ptr>());
0723     QVERIFY(!items[1].hasPayload<KMime::Message::Ptr>());
0724     QVERIFY(!items[2].hasPayload<KMime::Message::Ptr>());
0725     QVERIFY(!items[3].hasPayload<KMime::Message::Ptr>());
0726 
0727     // see data/README
0728     QCOMPARE(items[0].flags(), QSet<QByteArray>());
0729     QCOMPARE(items[1].flags(),
0730              QSet<QByteArray>() << "\\SEEN"
0731                                 << "$TODO");
0732     QCOMPARE(items[2].flags(), QSet<QByteArray>());
0733     QCOMPARE(items[3].flags(),
0734              QSet<QByteArray>() << "\\SEEN"
0735                                 << "\\FLAGGED");
0736 
0737     var = job->property("remoteIdToTagList");
0738     QVERIFY(var.isValid());
0739 
0740     tagListHash = var.value<QHash<QString, QVariant>>();
0741     QCOMPARE((int)tagListHash.count(), 3);
0742     QVERIFY(!tagListHash.value(items[0].remoteId()).toString().isEmpty());
0743     QVERIFY(!tagListHash.contains(items[1].remoteId()));
0744     QVERIFY(!tagListHash.value(items[2].remoteId()).toString().isEmpty());
0745     QVERIFY(!tagListHash.value(items[3].remoteId()).toString().isEmpty());
0746 
0747     // test listing mbox with index and unpurged messages (in mbox but not in index)
0748     Collection collection4;
0749     collection4.setName(QLatin1StringView("collection4"));
0750     collection4.setRemoteId(QLatin1StringView("collection4"));
0751     collection4.setParentCollection(mStore->topLevelCollection());
0752 
0753     job = mStore->fetchItems(collection4);
0754 
0755     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0756 
0757     QVERIFY(job->exec());
0758     QCOMPARE(job->error(), 0);
0759 
0760     items = job->items();
0761     QCOMPARE((int)items.count(), 4);
0762     QCOMPARE(itemsFromSpy(spy), items);
0763 
0764     QCOMPARE(items[0].remoteId(), QString::number(entryList4[0].messageOffset()));
0765     QCOMPARE(items[1].remoteId(), QString::number(entryList4[1].messageOffset()));
0766     QCOMPARE(items[2].remoteId(), QString::number(entryList4[2].messageOffset()));
0767     QCOMPARE(items[3].remoteId(), QString::number(entryList4[3].messageOffset()));
0768 
0769     QCOMPARE(items[0].parentCollection(), collection4);
0770     QCOMPARE(items[1].parentCollection(), collection4);
0771     QCOMPARE(items[2].parentCollection(), collection4);
0772     QCOMPARE(items[3].parentCollection(), collection4);
0773 
0774     QVERIFY(!items[0].hasPayload<KMime::Message::Ptr>());
0775     QVERIFY(!items[1].hasPayload<KMime::Message::Ptr>());
0776     QVERIFY(!items[2].hasPayload<KMime::Message::Ptr>());
0777     QVERIFY(!items[3].hasPayload<KMime::Message::Ptr>());
0778 
0779     // see data/README
0780     QCOMPARE(items[0].flags(), QSet<QByteArray>() << "\\SEEN");
0781     QCOMPARE(items[1].flags(), QSet<QByteArray>() << "\\DELETED");
0782     QCOMPARE(items[2].flags(), QSet<QByteArray>() << "\\DELETED");
0783     QCOMPARE(items[3].flags(), QSet<QByteArray>() << "\\SEEN");
0784 
0785     var = job->property("remoteIdToTagList");
0786     QVERIFY(!var.isValid());
0787 
0788     // test listing mbox with index but newer modification date than index's one
0789     QFile file5(fileInfo5.absoluteFilePath());
0790     QVERIFY(file5.open(QIODevice::ReadOnly));
0791     const QByteArray data5 = file5.readAll();
0792     file5.close();
0793 
0794     QTest::qSleep(1000);
0795 
0796     QVERIFY(file5.open(QIODevice::WriteOnly));
0797     file5.write(data5);
0798     file5.close();
0799 
0800     QCOMPARE(file5.size(), fileInfo5.size());
0801     fileInfo5.refresh();
0802     QFileInfo indexFileInfo5 = indexFile(fileInfo5);
0803     QVERIFY(fileInfo5.lastModified() > indexFileInfo5.lastModified());
0804 
0805     Collection collection5;
0806     collection5.setName(QLatin1StringView("collection5"));
0807     collection5.setRemoteId(QLatin1StringView("collection5"));
0808     collection5.setParentCollection(mStore->topLevelCollection());
0809 
0810     job = mStore->fetchItems(collection5);
0811 
0812     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0813 
0814     QVERIFY(job->exec());
0815     QCOMPARE(job->error(), 0);
0816 
0817     items = job->items();
0818     QCOMPARE((int)items.count(), 4);
0819     QCOMPARE(itemsFromSpy(spy), items);
0820 
0821     QCOMPARE(items[0].remoteId(), QString::number(entryList5[0].messageOffset()));
0822     QCOMPARE(items[1].remoteId(), QString::number(entryList5[1].messageOffset()));
0823     QCOMPARE(items[2].remoteId(), QString::number(entryList5[2].messageOffset()));
0824     QCOMPARE(items[3].remoteId(), QString::number(entryList5[3].messageOffset()));
0825 
0826     QCOMPARE(items[0].parentCollection(), collection5);
0827     QCOMPARE(items[1].parentCollection(), collection5);
0828     QCOMPARE(items[2].parentCollection(), collection5);
0829     QCOMPARE(items[3].parentCollection(), collection5);
0830 
0831     QVERIFY(!items[0].hasPayload<KMime::Message::Ptr>());
0832     QVERIFY(!items[1].hasPayload<KMime::Message::Ptr>());
0833     QVERIFY(!items[2].hasPayload<KMime::Message::Ptr>());
0834     QVERIFY(!items[3].hasPayload<KMime::Message::Ptr>());
0835 
0836     QCOMPARE(items[0].flags(), QSet<QByteArray>());
0837     QCOMPARE(items[1].flags(), QSet<QByteArray>());
0838     QCOMPARE(items[2].flags(), QSet<QByteArray>());
0839     QCOMPARE(items[3].flags(), QSet<QByteArray>());
0840 
0841     var = job->property("remoteIdToTagList");
0842     QVERIFY(!var.isValid());
0843 
0844     // test that a new message in an mbox with index it not marked as deleted
0845     KMime::Message::Ptr msgPtr(new KMime::Message);
0846     msgPtr->subject()->from7BitString("Test 5");
0847     msgPtr->to()->from7BitString("kevin.krammer@gmx.at");
0848     msgPtr->assemble();
0849 
0850     Item item3_5;
0851     item3_5.setMimeType(KMime::Message::mimeType());
0852     item3_5.setPayload<KMime::Message::Ptr>(msgPtr);
0853 
0854     FileStore::ItemCreateJob *itemCreate = mStore->createItem(item3_5, collection3);
0855     QVERIFY(itemCreate->exec());
0856 
0857     item3_5 = itemCreate->item();
0858     QVERIFY(!item3_5.remoteId().isEmpty());
0859 
0860     job = mStore->fetchItems(collection3);
0861 
0862     spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0863 
0864     QVERIFY(job->exec());
0865     QCOMPARE(job->error(), 0);
0866 
0867     items = job->items();
0868     QCOMPARE((int)items.count(), 5);
0869     QCOMPARE(itemsFromSpy(spy), items);
0870 
0871     QCOMPARE(items[0].remoteId(), QString::number(entryList3[0].messageOffset()));
0872     QCOMPARE(items[1].remoteId(), QString::number(entryList3[1].messageOffset()));
0873     QCOMPARE(items[2].remoteId(), QString::number(entryList3[2].messageOffset()));
0874     QCOMPARE(items[3].remoteId(), QString::number(entryList3[3].messageOffset()));
0875     QCOMPARE(items[4].remoteId(), item3_5.remoteId());
0876 
0877     QCOMPARE(items[0].parentCollection(), collection3);
0878     QCOMPARE(items[1].parentCollection(), collection3);
0879     QCOMPARE(items[2].parentCollection(), collection3);
0880     QCOMPARE(items[3].parentCollection(), collection3);
0881     QCOMPARE(items[4].parentCollection(), collection3);
0882 
0883     QVERIFY(!items[0].hasPayload<KMime::Message::Ptr>());
0884     QVERIFY(!items[1].hasPayload<KMime::Message::Ptr>());
0885     QVERIFY(!items[2].hasPayload<KMime::Message::Ptr>());
0886     QVERIFY(!items[3].hasPayload<KMime::Message::Ptr>());
0887     QVERIFY(!items[4].hasPayload<KMime::Message::Ptr>());
0888 
0889     // see data/README
0890     QCOMPARE(items[0].flags(), QSet<QByteArray>());
0891     QCOMPARE(items[1].flags(),
0892              QSet<QByteArray>() << "\\SEEN"
0893                                 << "$TODO");
0894     QCOMPARE(items[2].flags(), QSet<QByteArray>());
0895     QCOMPARE(items[3].flags(),
0896              QSet<QByteArray>() << "\\SEEN"
0897                                 << "\\FLAGGED");
0898     QCOMPARE(items[4].flags(), QSet<QByteArray>());
0899 
0900     var = job->property("remoteIdToTagList");
0901     QVERIFY(var.isValid());
0902 }
0903 
0904 void ItemFetchTest::testSingleItemFetchMaildir()
0905 {
0906     QDir topDir(mDir->path());
0907 
0908     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("maildir"), topDir.path(), QLatin1StringView("collection1")));
0909 
0910     KPIM::Maildir topLevelMd(topDir.path(), true);
0911 
0912     KPIM::Maildir md1 = topLevelMd.subFolder(QLatin1StringView("collection1"));
0913     QStringList entryList1 = md1.entryList();
0914     QCOMPARE((int)entryList1.count(), 4);
0915 
0916     KRandomSequence randomSequence;
0917     QStringList randomList1 = entryList1;
0918     randomSequence.randomize(randomList1);
0919 
0920     mStore->setPath(topDir.path());
0921 
0922     // common variables
0923     FileStore::ItemFetchJob *job = 0;
0924 
0925     QSignalSpy *spy = 0;
0926     Item::List items;
0927 
0928     // test fetching from maildir, headers only
0929     Collection collection1;
0930     collection1.setName(QLatin1StringView("collection1"));
0931     collection1.setRemoteId(QLatin1StringView("collection1"));
0932     collection1.setParentCollection(mStore->topLevelCollection());
0933 
0934     for (const QString &entry : std::as_const(randomList1)) {
0935         Item item1;
0936         item1.setId(KRandom::random());
0937         item1.setRemoteId(entry);
0938         item1.setParentCollection(collection1);
0939 
0940         job = mStore->fetchItem(item1);
0941         job->fetchScope().fetchPayloadPart(MessagePart::Header);
0942 
0943         spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0944 
0945         QVERIFY(job->exec());
0946         QCOMPARE(job->error(), 0);
0947 
0948         items = job->items();
0949         QCOMPARE((int)items.count(), 1);
0950         QCOMPARE(itemsFromSpy(spy), items);
0951 
0952         Item item = items.first();
0953         QCOMPARE(item, item1);
0954         QVERIFY(item.hasPayload<KMime::Message::Ptr>());
0955 
0956         KMime::Message::Ptr msgPtr = item.payload<KMime::Message::Ptr>();
0957         QVERIFY(msgPtr != 0);
0958 
0959         const QSet<QByteArray> parts = messageParts(msgPtr);
0960         QVERIFY(!parts.isEmpty());
0961         QVERIFY(parts.contains(MessagePart::Header));
0962         QVERIFY(!parts.contains(MessagePart::Body));
0963     }
0964 
0965     // test fetching from maildir, including body
0966     randomSequence.randomize(randomList1);
0967     for (const QString &entry : std::as_const(randomList1)) {
0968         Item item1;
0969         item1.setId(KRandom::random());
0970         item1.setRemoteId(entry);
0971         item1.setParentCollection(collection1);
0972 
0973         job = mStore->fetchItem(item1);
0974         job->fetchScope().fetchPayloadPart(MessagePart::Header);
0975         job->fetchScope().fetchPayloadPart(MessagePart::Body);
0976 
0977         spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
0978 
0979         QVERIFY(job->exec());
0980         QCOMPARE(job->error(), 0);
0981 
0982         items = job->items();
0983         QCOMPARE((int)items.count(), 1);
0984         QCOMPARE(itemsFromSpy(spy), items);
0985 
0986         Item item = items.first();
0987         QCOMPARE(item, item1);
0988         QVERIFY(item.hasPayload<KMime::Message::Ptr>());
0989 
0990         KMime::Message::Ptr msgPtr = item.payload<KMime::Message::Ptr>();
0991         QVERIFY(msgPtr != 0);
0992 
0993         const QSet<QByteArray> parts = messageParts(msgPtr);
0994         QVERIFY(!parts.isEmpty());
0995         QVERIFY(parts.contains(MessagePart::Header));
0996         QVERIFY(parts.contains(MessagePart::Body));
0997     }
0998 
0999     // test fetching from maildir, just specifying full payload
1000     randomSequence.randomize(randomList1);
1001     for (const QString &entry : std::as_const(randomList1)) {
1002         Item item1;
1003         item1.setId(KRandom::random());
1004         item1.setRemoteId(entry);
1005         item1.setParentCollection(collection1);
1006 
1007         job = mStore->fetchItem(item1);
1008         job->fetchScope().fetchFullPayload(true);
1009 
1010         spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
1011 
1012         QVERIFY(job->exec());
1013         QCOMPARE(job->error(), 0);
1014 
1015         items = job->items();
1016         QCOMPARE((int)items.count(), 1);
1017         QCOMPARE(itemsFromSpy(spy), items);
1018 
1019         Item item = items.first();
1020         QCOMPARE(item, item1);
1021         QVERIFY(item.hasPayload<KMime::Message::Ptr>());
1022 
1023         KMime::Message::Ptr msgPtr = item.payload<KMime::Message::Ptr>();
1024         QVERIFY(msgPtr != 0);
1025 
1026         const QSet<QByteArray> parts = messageParts(msgPtr);
1027         QVERIFY(!parts.isEmpty());
1028         QVERIFY(parts.contains(MessagePart::Header));
1029         QVERIFY(parts.contains(MessagePart::Body));
1030     }
1031 }
1032 
1033 void ItemFetchTest::testSingleItemFetchMBox()
1034 {
1035     QDir topDir(mDir->path());
1036 
1037     QVERIFY(TestDataUtil::installFolder(QLatin1StringView("mbox"), topDir.path(), QLatin1StringView("collection1")));
1038     // one message has no body
1039     const QByteArray messageIdOfEmptyBodyMsg = "201007241551.37547.kevin.krammer@demo.kolab.org";
1040 
1041     QFileInfo fileInfo1(topDir.path(), QLatin1StringView("collection1"));
1042     MBox mbox1;
1043     QVERIFY(mbox1.load(fileInfo1.absoluteFilePath()));
1044     MBoxEntry::List entryList1 = mbox1.entries();
1045     QCOMPARE((int)entryList1.count(), 4);
1046 
1047     KRandomSequence randomSequence;
1048     MBoxEntry::List randomList1 = entryList1;
1049     randomSequence.randomize(randomList1);
1050 
1051     mStore->setPath(topDir.path());
1052 
1053     // common variables
1054     FileStore::ItemFetchJob *job = 0;
1055 
1056     QSignalSpy *spy = 0;
1057     Item::List items;
1058 
1059     // test fetching from mbox, headers only
1060     Collection collection1;
1061     collection1.setName(QLatin1StringView("collection1"));
1062     collection1.setRemoteId(QLatin1StringView("collection1"));
1063     collection1.setParentCollection(mStore->topLevelCollection());
1064 
1065     for (const QString &entry : std::as_const(randomList1)) {
1066         Item item1;
1067         item1.setId(KRandom::random());
1068         item1.setRemoteId(QString::number(entry.messageOffset()));
1069         item1.setParentCollection(collection1);
1070 
1071         job = mStore->fetchItem(item1);
1072         job->fetchScope().fetchPayloadPart(MessagePart::Header);
1073 
1074         spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
1075 
1076         QVERIFY(job->exec());
1077         QCOMPARE(job->error(), 0);
1078 
1079         items = job->items();
1080         QCOMPARE((int)items.count(), 1);
1081         QCOMPARE(itemsFromSpy(spy), items);
1082 
1083         Item item = items.first();
1084         QCOMPARE(item, item1);
1085         QVERIFY(item.hasPayload<KMime::Message::Ptr>());
1086 
1087         KMime::Message::Ptr msgPtr = item.payload<KMime::Message::Ptr>();
1088         QVERIFY(msgPtr != 0);
1089 
1090         const QSet<QByteArray> parts = messageParts(msgPtr);
1091         QVERIFY(!parts.isEmpty());
1092         QVERIFY(parts.contains(MessagePart::Header));
1093         QVERIFY(!parts.contains(MessagePart::Body));
1094     }
1095 
1096     // test fetching from mbox, including body
1097     randomSequence.randomize(randomList1);
1098     for (const QString &entry : std::as_const(randomList1)) {
1099         Item item1;
1100         item1.setId(KRandom::random());
1101         item1.setRemoteId(QString::number(entry.messageOffset()));
1102         item1.setParentCollection(collection1);
1103 
1104         job = mStore->fetchItem(item1);
1105         job->fetchScope().fetchPayloadPart(MessagePart::Header);
1106         job->fetchScope().fetchPayloadPart(MessagePart::Body);
1107 
1108         spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
1109 
1110         QVERIFY(job->exec());
1111         QCOMPARE(job->error(), 0);
1112 
1113         items = job->items();
1114         QCOMPARE((int)items.count(), 1);
1115         QCOMPARE(itemsFromSpy(spy), items);
1116 
1117         Item item = items.first();
1118         QCOMPARE(item, item1);
1119         QVERIFY(item.hasPayload<KMime::Message::Ptr>());
1120 
1121         KMime::Message::Ptr msgPtr = item.payload<KMime::Message::Ptr>();
1122         QVERIFY(msgPtr != 0);
1123 
1124         const QSet<QByteArray> parts = messageParts(msgPtr);
1125         qCDebug(MIXEDMAILDIRRESOURCE_LOG) << msgPtr->messageID()->identifier();
1126         QVERIFY(!parts.isEmpty());
1127         QVERIFY(parts.contains(MessagePart::Header));
1128         if (msgPtr->messageID()->identifier() == messageIdOfEmptyBodyMsg) {
1129             QVERIFY(!parts.contains(MessagePart::Body));
1130         } else {
1131             QVERIFY(parts.contains(MessagePart::Body));
1132         }
1133     }
1134 
1135     // test fetching from mbox, just specifying full payload
1136     randomSequence.randomize(randomList1);
1137     for (const QString &entry : std::as_const(randomList1)) {
1138         Item item1;
1139         item1.setId(KRandom::random());
1140         item1.setRemoteId(QString::number(entry.messageOffset()));
1141         item1.setParentCollection(collection1);
1142 
1143         job = mStore->fetchItem(item1);
1144         job->fetchScope().fetchFullPayload(true);
1145 
1146         spy = new QSignalSpy(job, &FileStore::ItemFetchJob::itemsReceived);
1147 
1148         QVERIFY(job->exec());
1149         QCOMPARE(job->error(), 0);
1150 
1151         items = job->items();
1152         QCOMPARE((int)items.count(), 1);
1153         QCOMPARE(itemsFromSpy(spy), items);
1154 
1155         Item item = items.first();
1156         QCOMPARE(item, item1);
1157         QVERIFY(item.hasPayload<KMime::Message::Ptr>());
1158 
1159         KMime::Message::Ptr msgPtr = item.payload<KMime::Message::Ptr>();
1160         QVERIFY(msgPtr != 0);
1161 
1162         const QSet<QByteArray> parts = messageParts(msgPtr);
1163         QVERIFY(!parts.isEmpty());
1164         QVERIFY(parts.contains(MessagePart::Header));
1165         if (msgPtr->messageID()->identifier() == messageIdOfEmptyBodyMsg) {
1166             QVERIFY(!parts.contains(MessagePart::Body));
1167         } else {
1168             QVERIFY(parts.contains(MessagePart::Body));
1169         }
1170     }
1171 }
1172 
1173 QTEST_MAIN(ItemFetchTest)
1174 
1175 #include "itemfetchtest.moc"