From b4be5caff7691b5a4325938dc10abc02432af26e Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Tue, 24 May 2016 23:48:28 +0200 Subject: A much more comprehensive mail test --- common/genericresource.cpp | 2 +- common/genericresource.h | 2 + examples/maildirresource/maildirresource.cpp | 105 +++++++++++++++++-------- tests/mailtest.cpp | 110 ++++++++++++++++++++++++--- tests/mailtest.h | 3 + 5 files changed, 176 insertions(+), 46 deletions(-) diff --git a/common/genericresource.cpp b/common/genericresource.cpp index 74e829c..637e371 100644 --- a/common/genericresource.cpp +++ b/common/genericresource.cpp @@ -643,7 +643,7 @@ void GenericResource::scanForRemovals(Sink::Storage::Transaction &transaction, S }); } -static QSharedPointer getLatest(const Sink::Storage::NamedDatabase &db, const QByteArray &uid, DomainTypeAdaptorFactoryInterface &adaptorFactory) +QSharedPointer GenericResource::getLatest(const Sink::Storage::NamedDatabase &db, const QByteArray &uid, DomainTypeAdaptorFactoryInterface &adaptorFactory) { QSharedPointer current; db.findLatest(uid, diff --git a/common/genericresource.h b/common/genericresource.h index 9665d6b..9582f06 100644 --- a/common/genericresource.h +++ b/common/genericresource.h @@ -114,6 +114,8 @@ protected: void createOrModify(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, DomainTypeAdaptorFactoryInterface &adaptorFactory, const QByteArray &bufferType, const QByteArray &remoteId, const Sink::ApplicationDomain::ApplicationDomainType &entity); + static QSharedPointer getLatest(const Sink::Storage::NamedDatabase &db, const QByteArray &uid, DomainTypeAdaptorFactoryInterface &adaptorFactory); + MessageQueue mUserQueue; MessageQueue mSynchronizerQueue; QByteArray mResourceInstanceIdentifier; diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp index 42da667..6dc9990 100644 --- a/examples/maildirresource/maildirresource.cpp +++ b/examples/maildirresource/maildirresource.cpp @@ -49,6 +49,22 @@ #undef DEBUG_AREA #define DEBUG_AREA "resource.maildir" +static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath) +{ + auto parts = mimeMessagePath.split('/'); + const auto key = parts.takeLast(); + const auto path = parts.join("/") + "/cur/"; + + QDir dir(path); + const QFileInfoList list = dir.entryInfoList(QStringList() << (key+"*"), QDir::Files); + if (list.size() != 1) { + Warning() << "Failed to find message " << mimeMessagePath; + Warning() << "Failed to find message " << path; + return QString(); + } + return list.first().filePath(); +} + class FolderUpdater : public Sink::Preprocessor { public: @@ -76,7 +92,7 @@ public: }); return folderPath; } - + QString moveMessage(const QString &oldPath, const QByteArray &folder, Sink::Storage::Transaction &transaction) { if (oldPath.startsWith(Sink::temporaryFileLocation())) { @@ -90,22 +106,13 @@ public: } return oldPath; } + void updatedIndexedProperties(Sink::ApplicationDomain::BufferAdaptor &newEntity) { - const auto mimeMessagePath = newEntity.getProperty("mimeMessage").toString(); - auto parts = mimeMessagePath.split('/'); - const auto key = parts.takeLast(); - const auto path = parts.join("/") + "/cur/"; - - QDir dir(path); - const QFileInfoList list = dir.entryInfoList(QStringList() << (key+"*"), QDir::Files); - if (list.size() != 1) { - Warning() << "Failed to find message " << path << key << list.size(); - return; - } + const auto filePath = getFilePathFromMimeMessagePath(newEntity.getProperty("mimeMessage").toString()); KMime::Message *msg = new KMime::Message; - msg->setHead(KMime::CRLFtoLF(KPIM::Maildir::readEntryHeadersFromFile(list.first().filePath()))); + msg->setHead(KMime::CRLFtoLF(KPIM::Maildir::readEntryHeadersFromFile(filePath))); msg->parse(); newEntity.setProperty("subject", msg->subject(true)->asUnicodeString()); @@ -408,15 +415,7 @@ KAsync::Job MaildirResource::replay(Sink::Storage &synchronizationStore, c } else if (type == ENTITY_TYPE_MAIL) { if (operation == Sink::Operation_Creation) { const Sink::ApplicationDomain::Mail mail(mResourceInstanceIdentifier, Sink::Storage::uidFromKey(key), revision, mMailAdaptorFactory->createAdaptor(entity)); - auto parentFolder = mail.getProperty("folder").toByteArray(); - QByteArray parentFolderRemoteId; - if (!parentFolder.isEmpty()) { - parentFolderRemoteId = resolveLocalId(ENTITY_TYPE_FOLDER, parentFolder, synchronizationTransaction); - } else { - parentFolderRemoteId = mMaildirPath.toUtf8(); - } - const auto parentFolderPath = parentFolderRemoteId; - const auto remoteId = mail.getProperty("mimeMessage").toString().split('/').last(); + const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath()); Trace() << "Creating a new mail." << remoteId; if (remoteId.isEmpty()) { Warning() << "Failed to create mail: " << remoteId; @@ -436,11 +435,18 @@ KAsync::Job MaildirResource::replay(Sink::Storage &synchronizationStore, c const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, uid, synchronizationTransaction); Trace() << "Modifying a mail: " << remoteId; - const auto maildirPath = KPIM::Maildir::getDirectoryFromFile(remoteId); + const Sink::ApplicationDomain::Mail mail(mResourceInstanceIdentifier, Sink::Storage::uidFromKey(key), revision, mMailAdaptorFactory->createAdaptor(entity)); + + const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessagePath()); + const auto maildirPath = KPIM::Maildir::getDirectoryFromFile(filePath); KPIM::Maildir maildir(maildirPath, false); - const Sink::ApplicationDomain::Mail mail(mResourceInstanceIdentifier, Sink::Storage::uidFromKey(key), revision, mMailAdaptorFactory->createAdaptor(entity)); - auto newIdentifier = mail.getMimeMessagePath().split("/").last(); + const auto messagePathParts = filePath.split("/"); + if (messagePathParts.isEmpty()) { + Warning() << "No message path available: " << remoteId; + return KAsync::error(1, "No message path available."); + } + const auto newIdentifier = messagePathParts.last(); QString identifier; if (newIdentifier != KPIM::Maildir::getKeyFromFile(remoteId)) { //Remove the old mime message if it changed @@ -449,6 +455,7 @@ KAsync::Job MaildirResource::replay(Sink::Storage &synchronizationStore, c identifier = newIdentifier; } else { //The identifier needs to contain the flags for changeEntryFlags to work + Q_ASSERT(!remoteId.split('/').isEmpty()); identifier = remoteId.split('/').last(); } @@ -480,12 +487,23 @@ KAsync::Job MaildirResource::inspect(int inspectionType, const QByteArray { auto synchronizationStore = QSharedPointer::create(Sink::storageLocation(), mResourceInstanceIdentifier + ".synchronization", Sink::Storage::ReadOnly); auto synchronizationTransaction = synchronizationStore->createTransaction(Sink::Storage::ReadOnly); + + auto mainStore = QSharedPointer::create(Sink::storageLocation(), mResourceInstanceIdentifier, Sink::Storage::ReadOnly); + auto transaction = mainStore->createTransaction(Sink::Storage::ReadOnly); + Trace() << "Inspecting " << inspectionType << domainType << entityId << property << expectedValue; + if (domainType == ENTITY_TYPE_MAIL) { + auto mainDatabase = Sink::Storage::mainDatabase(transaction, ENTITY_TYPE_MAIL); + auto bufferAdaptor = getLatest(mainDatabase, entityId, *mMailAdaptorFactory); + Q_ASSERT(bufferAdaptor); + + const Sink::ApplicationDomain::Mail mail(mResourceInstanceIdentifier, entityId, 0, bufferAdaptor); + const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessagePath()); + if (inspectionType == Sink::ResourceControl::Inspection::PropertyInspectionType) { if (property == "unread") { - const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, entityId, synchronizationTransaction); - const auto flags = KPIM::Maildir::readEntryFlags(remoteId.split('/').last()); + const auto flags = KPIM::Maildir::readEntryFlags(filePath.split('/').last()); if (expectedValue.toBool() && (flags & KPIM::Maildir::Seen)) { return KAsync::error(1, "Expected unread but couldn't find it."); } @@ -495,10 +513,8 @@ KAsync::Job MaildirResource::inspect(int inspectionType, const QByteArray return KAsync::null(); } if (property == "subject") { - const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, entityId, synchronizationTransaction); - KMime::Message *msg = new KMime::Message; - msg->setHead(KMime::CRLFtoLF(KPIM::Maildir::readEntryHeadersFromFile(remoteId))); + msg->setHead(KMime::CRLFtoLF(KPIM::Maildir::readEntryHeadersFromFile(filePath))); msg->parse(); if (msg->subject(true)->asUnicodeString() != expectedValue.toString()) { @@ -508,9 +524,32 @@ KAsync::Job MaildirResource::inspect(int inspectionType, const QByteArray } } if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) { - const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, entityId, synchronizationTransaction); - if (QFileInfo(remoteId).exists() != expectedValue.toBool()) { - return KAsync::error(1, "Wrong file existence: " + remoteId); + if (QFileInfo(filePath).exists() != expectedValue.toBool()) { + return KAsync::error(1, "Wrong file existence: " + filePath); + } + } + } + if (domainType == ENTITY_TYPE_FOLDER) { + const auto remoteId = resolveLocalId(ENTITY_TYPE_FOLDER, entityId, synchronizationTransaction); + + if (inspectionType == Sink::ResourceControl::Inspection::CacheIntegrityInspectionType) { + if (!QDir(remoteId).exists()) { + return KAsync::error(1, "The directory is not existing: " + remoteId); + } + + int expectedCount = 0; + Index index("mail.index.folder", transaction); + index.lookup(entityId, [&](const QByteArray &sinkId) { + expectedCount++; + }, + [&](const Index::Error &error) { + Warning() << "Error in index: " << error.message << property; + }); + + QDir dir(remoteId + "/cur"); + const QFileInfoList list = dir.entryInfoList(QDir::Files); + if (list.size() != expectedCount) { + return KAsync::error(1, QString("Wrong number of files; found %1 instead of %2.").arg(list.size()).arg(expectedCount)); } } } diff --git a/tests/mailtest.cpp b/tests/mailtest.cpp index 3ca8eaa..496b7ef 100644 --- a/tests/mailtest.cpp +++ b/tests/mailtest.cpp @@ -42,6 +42,7 @@ void MailTest::initTestCase() VERIFYEXEC(Store::create(resource)); mResourceInstanceIdentifier = resource.identifier(); + mCapabilities = resource.getProperty("capabilities").value(); } void MailTest::cleanup() @@ -131,13 +132,8 @@ void MailTest::testCreateModifyDeleteFolder() void MailTest::testCreateModifyDeleteMail() { - const auto subject = QString::fromLatin1("Foobar"); - Query query; - query.resources << mResourceInstanceIdentifier; - query.request().request(); - auto folder = Folder::create(mResourceInstanceIdentifier); folder.setName("folder"); VERIFYEXEC(Store::create(folder)); @@ -151,7 +147,7 @@ void MailTest::testCreateModifyDeleteMail() mail.setFolder(folder); VERIFYEXEC(Store::create(mail)); - VERIFYEXEC(ResourceControl::flushMessageQueue(query.resources)); + VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); { auto job = Store::fetchAll(Query::RequestedProperties(QByteArrayList() << Mail::Folder::name << Mail::Subject::name << Mail::MimeMessage::name)) .then>([=](const QList &mails) { @@ -160,14 +156,13 @@ void MailTest::testCreateModifyDeleteMail() QCOMPARE(mail.getSubject(), subject); QCOMPARE(mail.getFolder(), folder.identifier()); QVERIFY(QFile(mail.getMimeMessagePath()).exists()); - - // return Store::remove(*mail) - // .then(ResourceControl::flushReplayQueue(query.resources)) // The change needs to be replayed already - // .then(ResourceControl::inspect(ResourceControl::Inspection::ExistenceInspection(*mail, false))); }); VERIFYEXEC(job); } + VERIFYEXEC(ResourceControl::inspect(ResourceControl::Inspection::ExistenceInspection(mail, true))); + VERIFYEXEC(ResourceControl::inspect(ResourceControl::Inspection::CacheIntegrityInspection(folder))); + const auto subject2 = QString::fromLatin1("Foobar2"); auto message2 = KMime::Message::Ptr::create(); message2->subject(true)->fromUnicodeString(subject2, "utf8"); @@ -175,7 +170,7 @@ void MailTest::testCreateModifyDeleteMail() mail.setMimeMessage(message2->encodedContent()); VERIFYEXEC(Store::modify(mail)); - VERIFYEXEC(ResourceControl::flushMessageQueue(query.resources)); + VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); { auto job = Store::fetchAll(Query::RequestedProperties(QByteArrayList() << Mail::Folder::name << Mail::Subject::name)) .then>([=](const QList &mails) { @@ -188,9 +183,11 @@ void MailTest::testCreateModifyDeleteMail() }); VERIFYEXEC(job); } + VERIFYEXEC(ResourceControl::inspect(ResourceControl::Inspection::ExistenceInspection(mail, true))); + VERIFYEXEC(ResourceControl::inspect(ResourceControl::Inspection::CacheIntegrityInspection(folder))); VERIFYEXEC(Store::remove(mail)); - VERIFYEXEC(ResourceControl::flushMessageQueue(query.resources)); + VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); { auto job = Store::fetchAll(Query::RequestedProperties(QByteArrayList() << Mail::Folder::name << Mail::Subject::name)) .then>([=](const QList &mails) { @@ -198,6 +195,95 @@ void MailTest::testCreateModifyDeleteMail() }); VERIFYEXEC(job); } + VERIFYEXEC(ResourceControl::inspect(ResourceControl::Inspection::ExistenceInspection(mail, false))); + VERIFYEXEC(ResourceControl::inspect(ResourceControl::Inspection::CacheIntegrityInspection(folder))); +} + +void MailTest::testMarkMailAsRead() +{ + auto folder = Folder::create(mResourceInstanceIdentifier); + folder.setName("folder"); + VERIFYEXEC(Store::create(folder)); + + auto message = KMime::Message::Ptr::create(); + message->subject(true)->fromUnicodeString("subject", "utf8"); + message->assemble(); + + auto mail = Mail::create(mResourceInstanceIdentifier); + mail.setMimeMessage(message->encodedContent()); + mail.setFolder(folder); + mail.setUnread(false); + VERIFYEXEC(Store::create(mail)); + VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); + + auto job = Store::fetchAll(Query::ResourceFilter(mResourceInstanceIdentifier) + + Query::RequestedProperties(QByteArrayList() << Mail::Folder::name + << Mail::Subject::name)) + .then, QList>([this](const QList &mails) { + ASYNCCOMPARE(mails.size(), 1); + auto mail = mails.first(); + mail->setProperty("unread", true); + return Store::modify(*mail) + .then(ResourceControl::flushReplayQueue(QByteArrayList() << mResourceInstanceIdentifier)) // The change needs to be replayed already + .then(ResourceControl::inspect(ResourceControl::Inspection::PropertyInspection(*mail, Mail::Unread::name, true))) + .then(ResourceControl::inspect(ResourceControl::Inspection::PropertyInspection(*mail, Mail::Subject::name, mail->getSubject()))); + }); + VERIFYEXEC(job); + + // Verify that we can still query for all relevant information + auto job2 = Store::fetchAll( + Query::ResourceFilter(mResourceInstanceIdentifier) + Query::RequestedProperties(QByteArrayList() << Mail::Folder::name + << Mail::Subject::name + << Mail::MimeMessage::name + << Mail::Unread::name)) + .then, QList>([](const QList &mails) { + ASYNCCOMPARE(mails.size(), 1); + auto mail = mails.first(); + ASYNCVERIFY(!mail->getSubject().isEmpty()); + ASYNCCOMPARE(mail->getUnread(), true); + ASYNCVERIFY(QFileInfo(mail->getMimeMessagePath()).exists()); + return KAsync::null(); + }); + VERIFYEXEC(job2); +} + +void MailTest::testCreateDraft() +{ + if (!mCapabilities.contains("drafts")) { + QSKIP("Resource doesn't have the drafts capability"); + } + + auto message = KMime::Message::Ptr::create(); + message->subject(true)->fromUnicodeString(QString::fromLatin1("Foobar"), "utf8"); + message->assemble(); + + auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); + mail.setMimeMessage(message->encodedContent()); + mail.setDraft(true); + + VERIFYEXEC(Store::create(mail)); + VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); + + QByteArray folderIdentifier; + auto job = Store::fetchOne(Query::IdentityFilter(mail.identifier()) + Query::RequestedProperties(QByteArrayList() << Mail::MimeMessage::name << Mail::Folder::name)) + .then([&](const ApplicationDomain::Mail &mail) { + folderIdentifier = mail.getProperty("folder").toByteArray(); + QVERIFY(!folderIdentifier.isEmpty()); + }); + VERIFYEXEC(job); + + //Ensure we can also query by folder + auto job2 = Store::fetchAll(Query::PropertyFilter("folder", folderIdentifier)) + .then >([&](const QList &mails) { + bool found = false; + for (const auto m : mails) { + if (m->identifier() == mail.identifier()) { + found = true; + } + } + QVERIFY(found); + }); + VERIFYEXEC(job2); } #include "mailtest.moc" diff --git a/tests/mailtest.h b/tests/mailtest.h index 0729a91..43d4f75 100644 --- a/tests/mailtest.h +++ b/tests/mailtest.h @@ -51,6 +51,7 @@ class MailTest : public QObject protected: QByteArray mResourceInstanceIdentifier; + QByteArrayList mCapabilities; virtual void resetTestEnvironment() = 0; virtual Sink::ApplicationDomain::SinkResource createResource() = 0; @@ -63,6 +64,8 @@ private slots: void testCreateModifyDeleteFolder(); void testCreateModifyDeleteMail(); + void testMarkMailAsRead(); + void testCreateDraft(); }; } -- cgit v1.2.3