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

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 "filestore/collectionfetchjob.h"
0011 
0012 #include "libmaildir/maildir.h"
0013 
0014 #include <KMime/Message>
0015 
0016 #include <QTemporaryDir>
0017 
0018 #include <QSignalSpy>
0019 
0020 #include <QDir>
0021 #include <QFileInfo>
0022 #include <QTest>
0023 
0024 using namespace Akonadi;
0025 
0026 static Collection::List collectionsFromSpy(QSignalSpy *spy)
0027 {
0028     Collection::List collections;
0029 
0030     QListIterator<QList<QVariant>> it(*spy);
0031     while (it.hasNext()) {
0032         const QList<QVariant> invocation = it.next();
0033         Q_ASSERT(invocation.count() == 1);
0034 
0035         collections << invocation.first().value<Collection::List>();
0036     }
0037 
0038     return collections;
0039 }
0040 
0041 class CollectionFetchTest : public QObject
0042 {
0043     Q_OBJECT
0044 
0045 public:
0046     CollectionFetchTest()
0047         : QObject()
0048         , mStore(nullptr)
0049         , mDir(nullptr)
0050     {
0051         // for monitoring signals
0052         qRegisterMetaType<Akonadi::Collection::List>();
0053     }
0054 
0055     ~CollectionFetchTest() override
0056     {
0057         delete mStore;
0058         delete mDir;
0059     }
0060 
0061 private:
0062     MixedMaildirStore *mStore = nullptr;
0063     QTemporaryDir *mDir = nullptr;
0064 
0065 private Q_SLOTS:
0066     void init();
0067     void cleanup();
0068     void testEmptyDir();
0069     void testMixedTree();
0070 };
0071 
0072 void CollectionFetchTest::init()
0073 {
0074     mStore = new MixedMaildirStore;
0075 
0076     mDir = new QTemporaryDir;
0077     QVERIFY(mDir->isValid());
0078     QVERIFY(QDir(mDir->path()).exists());
0079 }
0080 
0081 void CollectionFetchTest::cleanup()
0082 {
0083     delete mStore;
0084     mStore = nullptr;
0085     delete mDir;
0086     mDir = nullptr;
0087 }
0088 
0089 void CollectionFetchTest::testEmptyDir()
0090 {
0091     mStore->setPath(mDir->path());
0092 
0093     FileStore::CollectionFetchJob *job = nullptr;
0094     QSignalSpy *spy = nullptr;
0095     Collection::List collections;
0096 
0097     // test base fetch of top level collection
0098     job = mStore->fetchCollections(mStore->topLevelCollection(), FileStore::CollectionFetchJob::Base);
0099 
0100     spy = new QSignalSpy(job, &FileStore::CollectionFetchJob::collectionsReceived);
0101 
0102     QVERIFY(job->exec());
0103     QCOMPARE(job->error(), 0);
0104     QCOMPARE(spy->count(), 1);
0105 
0106     collections = collectionsFromSpy(spy);
0107     QCOMPARE(collections.count(), 1);
0108     QCOMPARE(collections.first(), mStore->topLevelCollection());
0109     QCOMPARE(job->collections(), collections);
0110 
0111     // test first level fetch of top level collection
0112     job = mStore->fetchCollections(mStore->topLevelCollection(), FileStore::CollectionFetchJob::FirstLevel);
0113 
0114     spy = new QSignalSpy(job, &FileStore::CollectionFetchJob::collectionsReceived);
0115 
0116     QVERIFY(job->exec());
0117     QCOMPARE(job->error(), 0);
0118     QCOMPARE(spy->count(), 0);
0119 
0120     collections = collectionsFromSpy(spy);
0121     QCOMPARE(collections.count(), 0);
0122     QCOMPARE(job->collections(), collections);
0123 
0124     // test recursive fetch of top level collection
0125     job = mStore->fetchCollections(mStore->topLevelCollection(), FileStore::CollectionFetchJob::Recursive);
0126 
0127     spy = new QSignalSpy(job, &FileStore::CollectionFetchJob::collectionsReceived);
0128 
0129     QVERIFY(job->exec());
0130     QCOMPARE(job->error(), 0);
0131     QCOMPARE(spy->count(), 0);
0132 
0133     collections = collectionsFromSpy(spy);
0134     QCOMPARE(collections.count(), 0);
0135     QCOMPARE(job->collections(), collections);
0136 
0137     // test fail of base fetching non existent collection
0138     Collection collection;
0139     collection.setName(QStringLiteral("collection"));
0140     collection.setRemoteId(QStringLiteral("collection"));
0141     collection.setParentCollection(mStore->topLevelCollection());
0142 
0143     job = mStore->fetchCollections(collection, FileStore::CollectionFetchJob::Base);
0144 
0145     spy = new QSignalSpy(job, &FileStore::CollectionFetchJob::collectionsReceived);
0146 
0147     QVERIFY(!job->exec());
0148     QCOMPARE(job->error(), (int)FileStore::Job::InvalidJobContext);
0149     QCOMPARE(spy->count(), 0);
0150 
0151     collections = collectionsFromSpy(spy);
0152     QCOMPARE(collections.count(), 0);
0153     QCOMPARE(job->collections(), collections);
0154 
0155     // test fail of first level fetching non existent collection
0156     job = mStore->fetchCollections(collection, FileStore::CollectionFetchJob::FirstLevel);
0157 
0158     spy = new QSignalSpy(job, &FileStore::CollectionFetchJob::collectionsReceived);
0159 
0160     QVERIFY(!job->exec());
0161     QCOMPARE(job->error(), (int)FileStore::Job::InvalidJobContext);
0162     QCOMPARE(spy->count(), 0);
0163 
0164     collections = collectionsFromSpy(spy);
0165     QCOMPARE(collections.count(), 0);
0166     QCOMPARE(job->collections(), collections);
0167 
0168     // test fail of recursive fetching non existent collection
0169     job = mStore->fetchCollections(collection, FileStore::CollectionFetchJob::FirstLevel);
0170 
0171     spy = new QSignalSpy(job, &FileStore::CollectionFetchJob::collectionsReceived);
0172 
0173     QVERIFY(!job->exec());
0174     QCOMPARE(job->error(), (int)FileStore::Job::InvalidJobContext);
0175     QCOMPARE(spy->count(), 0);
0176 
0177     collections = collectionsFromSpy(spy);
0178     QCOMPARE(collections.count(), 0);
0179     QCOMPARE(job->collections(), collections);
0180 }
0181 
0182 void CollectionFetchTest::testMixedTree()
0183 {
0184     QDir topDir(mDir->path());
0185 
0186     KPIM::Maildir topLevelMd(mDir->path(), true);
0187     QVERIFY(topLevelMd.isValid());
0188 
0189     KPIM::Maildir md1(topLevelMd.addSubFolder(QStringLiteral("collection1")), false);
0190     KPIM::Maildir md1_2(md1.addSubFolder(QStringLiteral("collection1_2")), false);
0191     KPIM::Maildir md1_2_1(md1_2.addSubFolder(QStringLiteral("collection1_2_1")), false);
0192 
0193     // simulate second level mbox in maildir parent
0194     QFileInfo fileInfo1_1(KPIM::Maildir::subDirPathForFolderPath(md1.path()), QStringLiteral("collection1_1"));
0195     QFile file1_1(fileInfo1_1.absoluteFilePath());
0196     file1_1.open(QIODevice::WriteOnly);
0197     file1_1.close();
0198     QVERIFY(fileInfo1_1.exists());
0199 
0200     QFileInfo subDirInfo1_1(KPIM::Maildir::subDirPathForFolderPath(fileInfo1_1.absoluteFilePath()));
0201     QVERIFY(topDir.mkpath(subDirInfo1_1.absoluteFilePath()));
0202     KPIM::Maildir md1_1(subDirInfo1_1.absoluteFilePath(), true);
0203     KPIM::Maildir md1_1_1(md1_1.addSubFolder(QStringLiteral("collection1_1_1")), false);
0204 
0205     // simulate third level mbox in mbox parent
0206     QFileInfo fileInfo1_1_2(md1_1.path(), QStringLiteral("collection1_1_2"));
0207     QFile file1_1_2(fileInfo1_1_2.absoluteFilePath());
0208     file1_1_2.open(QIODevice::WriteOnly);
0209     file1_1_2.close();
0210     QVERIFY(fileInfo1_1_2.exists());
0211 
0212     KPIM::Maildir md2(topLevelMd.addSubFolder(QStringLiteral("collection2")), false);
0213 
0214     // simulate first level mbox
0215     QFileInfo fileInfo3(mDir->path(), QStringLiteral("collection3"));
0216     QFile file3(fileInfo3.absoluteFilePath());
0217     file3.open(QIODevice::WriteOnly);
0218     file3.close();
0219     QVERIFY(fileInfo3.exists());
0220 
0221     // simulate first level mbox with subtree
0222     QFileInfo fileInfo4(mDir->path(), QStringLiteral("collection4"));
0223     QFile file4(fileInfo4.absoluteFilePath());
0224     file4.open(QIODevice::WriteOnly);
0225     file4.close();
0226     QVERIFY(fileInfo4.exists());
0227 
0228     QFileInfo subDirInfo4(KPIM::Maildir::subDirPathForFolderPath(fileInfo4.absoluteFilePath()));
0229     QVERIFY(topDir.mkpath(subDirInfo4.absoluteFilePath()));
0230 
0231     KPIM::Maildir md4(subDirInfo4.absoluteFilePath(), true);
0232     KPIM::Maildir md4_1(md4.addSubFolder(QStringLiteral("collection4_1")), false);
0233 
0234     // simulate second level mbox in mbox parent
0235     QFileInfo fileInfo4_2(subDirInfo4.absoluteFilePath(), QStringLiteral("collection4_2"));
0236     QFile file4_2(fileInfo4_2.absoluteFilePath());
0237     file4_2.open(QIODevice::WriteOnly);
0238     file4_2.close();
0239     QVERIFY(fileInfo4_2.exists());
0240 
0241     QSet<QString> firstLevelNames;
0242     firstLevelNames << md1.name() << md2.name() << fileInfo3.fileName() << fileInfo4.fileName();
0243 
0244     QSet<QString> secondLevelNames;
0245     secondLevelNames << md1_2.name() << md4_1.name() << fileInfo1_1.fileName() << fileInfo4_2.fileName();
0246 
0247     QSet<QString> thirdLevelNames;
0248     thirdLevelNames << md1_1_1.name() << fileInfo1_1_2.fileName() << md1_2_1.name();
0249 
0250     mStore->setPath(mDir->path());
0251     // mDir = 0;
0252 
0253     FileStore::CollectionFetchJob *job = nullptr;
0254     QSignalSpy *spy = nullptr;
0255     Collection::List collections;
0256 
0257     // test base fetch of top level collection
0258     job = mStore->fetchCollections(mStore->topLevelCollection(), FileStore::CollectionFetchJob::Base);
0259 
0260     spy = new QSignalSpy(job, &FileStore::CollectionFetchJob::collectionsReceived);
0261 
0262     QVERIFY(job->exec());
0263     QCOMPARE(job->error(), 0);
0264     QCOMPARE(spy->count(), 1);
0265 
0266     collections = collectionsFromSpy(spy);
0267     QCOMPARE(collections.count(), 1);
0268     QCOMPARE(collections.first(), mStore->topLevelCollection());
0269     QCOMPARE(job->collections(), collections);
0270 
0271     // test first level fetch of top level collection
0272     job = mStore->fetchCollections(mStore->topLevelCollection(), FileStore::CollectionFetchJob::FirstLevel);
0273 
0274     spy = new QSignalSpy(job, &FileStore::CollectionFetchJob::collectionsReceived);
0275 
0276     QVERIFY(job->exec());
0277     QCOMPARE(job->error(), 0);
0278     QVERIFY(!spy->isEmpty());
0279 
0280     collections = collectionsFromSpy(spy);
0281     QCOMPARE(collections.count(), firstLevelNames.count());
0282     QCOMPARE(job->collections(), collections);
0283 
0284     for (const Collection &collection : std::as_const(collections)) {
0285         QVERIFY(!collection.remoteId().isEmpty());
0286         QCOMPARE(collection.remoteId(), collection.name());
0287         QCOMPARE(collection.contentMimeTypes(), QStringList() << Collection::mimeType() << KMime::Message::mimeType());
0288 
0289         QCOMPARE(collection.rights(),
0290                  Collection::CanCreateItem | Collection::CanChangeItem | Collection::CanDeleteItem | Collection::CanCreateCollection
0291                      | Collection::CanChangeCollection | Collection::CanDeleteCollection);
0292 
0293         QCOMPARE(collection.parentCollection(), mStore->topLevelCollection());
0294         QVERIFY(firstLevelNames.contains(collection.name()));
0295     }
0296 
0297     // test recursive fetch of top level collection
0298     job = mStore->fetchCollections(mStore->topLevelCollection(), FileStore::CollectionFetchJob::Recursive);
0299 
0300     spy = new QSignalSpy(job, &FileStore::CollectionFetchJob::collectionsReceived);
0301 
0302     QVERIFY(job->exec());
0303     QCOMPARE(job->error(), 0);
0304     QVERIFY(!spy->isEmpty());
0305 
0306     collections = collectionsFromSpy(spy);
0307     QCOMPARE(collections.count(), firstLevelNames.count() + secondLevelNames.count() + thirdLevelNames.count());
0308     QCOMPARE(job->collections(), collections);
0309 
0310     for (const Collection &collection : std::as_const(collections)) {
0311         QVERIFY(!collection.remoteId().isEmpty());
0312         QCOMPARE(collection.remoteId(), collection.name());
0313         QCOMPARE(collection.contentMimeTypes(), QStringList() << Collection::mimeType() << KMime::Message::mimeType());
0314 
0315         QCOMPARE(collection.rights(),
0316                  Collection::CanCreateItem | Collection::CanChangeItem | Collection::CanDeleteItem | Collection::CanCreateCollection
0317                      | Collection::CanChangeCollection | Collection::CanDeleteCollection);
0318 
0319         if (firstLevelNames.contains(collection.name())) {
0320             QCOMPARE(collection.parentCollection(), mStore->topLevelCollection());
0321         } else if (secondLevelNames.contains(collection.name())) {
0322             QVERIFY(firstLevelNames.contains(collection.parentCollection().name()));
0323             QCOMPARE(collection.parentCollection().parentCollection(), mStore->topLevelCollection());
0324         } else if (thirdLevelNames.contains(collection.name())) {
0325             QVERIFY(secondLevelNames.contains(collection.parentCollection().name()));
0326             QCOMPARE(collection.parentCollection().parentCollection().parentCollection(), mStore->topLevelCollection());
0327         }
0328     }
0329 
0330     // test base fetching all collections
0331     for (const Collection &collection : std::as_const(collections)) {
0332         job = mStore->fetchCollections(collection, FileStore::CollectionFetchJob::Base);
0333 
0334         spy = new QSignalSpy(job, &FileStore::CollectionFetchJob::collectionsReceived);
0335 
0336         QVERIFY(job->exec());
0337         QCOMPARE(job->error(), 0);
0338         QCOMPARE(spy->count(), 1);
0339 
0340         const Collection::List list = collectionsFromSpy(spy);
0341         QCOMPARE(list.count(), 1);
0342         QCOMPARE(list.first(), collection);
0343         QCOMPARE(job->collections(), list);
0344 
0345         const Collection col = list.first();
0346         QVERIFY(!col.remoteId().isEmpty());
0347         QCOMPARE(col.remoteId(), col.name());
0348         QCOMPARE(col.contentMimeTypes(), QStringList() << Collection::mimeType() << KMime::Message::mimeType());
0349 
0350         QCOMPARE(col.rights(),
0351                  Collection::CanCreateItem | Collection::CanChangeItem | Collection::CanDeleteItem | Collection::CanCreateCollection
0352                      | Collection::CanChangeCollection | Collection::CanDeleteCollection);
0353     }
0354 
0355     // test first level fetching all collections
0356     for (const Collection &collection : std::as_const(collections)) {
0357         job = mStore->fetchCollections(collection, FileStore::CollectionFetchJob::FirstLevel);
0358 
0359         spy = new QSignalSpy(job, &FileStore::CollectionFetchJob::collectionsReceived);
0360 
0361         QVERIFY(job->exec());
0362         QCOMPARE(job->error(), 0);
0363 
0364         const Collection::List list = collectionsFromSpy(spy);
0365         QCOMPARE(job->collections(), list);
0366 
0367         for (const Collection &childCollection : list) {
0368             QCOMPARE(childCollection.parentCollection(), collection);
0369 
0370             QVERIFY(!childCollection.remoteId().isEmpty());
0371             QCOMPARE(childCollection.remoteId(), childCollection.name());
0372             QCOMPARE(childCollection.contentMimeTypes(), QStringList() << Collection::mimeType() << KMime::Message::mimeType());
0373 
0374             QCOMPARE(childCollection.rights(),
0375                      Collection::CanCreateItem | Collection::CanChangeItem | Collection::CanDeleteItem | Collection::CanCreateCollection
0376                          | Collection::CanChangeCollection | Collection::CanDeleteCollection);
0377         }
0378 
0379         if (firstLevelNames.contains(collection.name())) {
0380             for (const Collection &childCollection : list) {
0381                 QVERIFY(secondLevelNames.contains(childCollection.name()));
0382             }
0383         } else if (secondLevelNames.contains(collection.name())) {
0384             for (const Collection &childCollection : list) {
0385                 QVERIFY(thirdLevelNames.contains(childCollection.name()));
0386             }
0387             if (collection.name() == md1_2.name()) {
0388                 QCOMPARE(list.count(), 1);
0389                 QCOMPARE(list.first().name(), md1_2_1.name());
0390             } else if (collection.name() == fileInfo1_1.fileName()) {
0391                 QCOMPARE(list.count(), 2);
0392             }
0393         } else {
0394             QCOMPARE(list.count(), 0);
0395         }
0396     }
0397 
0398     // test recursive fetching all collections
0399     for (const Collection &collection : std::as_const(collections)) {
0400         job = mStore->fetchCollections(collection, FileStore::CollectionFetchJob::Recursive);
0401 
0402         spy = new QSignalSpy(job, &FileStore::CollectionFetchJob::collectionsReceived);
0403 
0404         QVERIFY(job->exec());
0405         QCOMPARE(job->error(), 0);
0406 
0407         const Collection::List list = collectionsFromSpy(spy);
0408         QCOMPARE(job->collections(), list);
0409 
0410         for (const Collection &childCollection : list) {
0411             QVERIFY(childCollection.parentCollection() == collection || childCollection.parentCollection().parentCollection() == collection);
0412             QVERIFY(!childCollection.remoteId().isEmpty());
0413             QCOMPARE(childCollection.remoteId(), childCollection.name());
0414             QCOMPARE(childCollection.contentMimeTypes(), QStringList() << Collection::mimeType() << KMime::Message::mimeType());
0415 
0416             QCOMPARE(childCollection.rights(),
0417                      Collection::CanCreateItem | Collection::CanChangeItem | Collection::CanDeleteItem | Collection::CanCreateCollection
0418                          | Collection::CanChangeCollection | Collection::CanDeleteCollection);
0419         }
0420 
0421         if (firstLevelNames.contains(collection.name())) {
0422             for (const Collection &childCollection : list) {
0423                 QVERIFY(secondLevelNames.contains(childCollection.name()) || thirdLevelNames.contains(childCollection.name()));
0424             }
0425         } else if (secondLevelNames.contains(collection.name())) {
0426             for (const Collection &childCollection : list) {
0427                 QVERIFY(thirdLevelNames.contains(childCollection.name()));
0428             }
0429             if (collection.name() == md1_2.name()) {
0430                 QCOMPARE(list.count(), 1);
0431                 QCOMPARE(list.first().name(), md1_2_1.name());
0432             } else if (collection.name() == fileInfo1_1.fileName()) {
0433                 QCOMPARE(list.count(), 2);
0434             }
0435         } else {
0436             QCOMPARE(list.count(), 0);
0437         }
0438     }
0439 }
0440 
0441 QTEST_MAIN(CollectionFetchTest)
0442 
0443 #include "collectionfetchtest.moc"