diff --git a/src/akonadi/akonadicachingstorage.cpp b/src/akonadi/akonadicachingstorage.cpp index 19ec79b1..89a59ac5 100644 --- a/src/akonadi/akonadicachingstorage.cpp +++ b/src/akonadi/akonadicachingstorage.cpp @@ -1,381 +1,381 @@ /* This file is part of Zanshin Copyright 2015 Mario Bensi Copyright 2017 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonadicachingstorage.h" #include "akonadistorage.h" #include "akonadicollectionfetchjobinterface.h" #include "akonadiitemfetchjobinterface.h" #include using namespace Akonadi; class CachingCollectionFetchJob : public KCompositeJob, public CollectionFetchJobInterface { Q_OBJECT public: CachingCollectionFetchJob(const StorageInterface::Ptr &storage, const Cache::Ptr &cache, const Collection &collection, StorageInterface::FetchDepth depth, QObject *parent = nullptr) : KCompositeJob(parent), m_started(false), m_storage(storage), m_cache(cache), m_collection(collection), m_depth(depth) { QTimer::singleShot(0, this, &CachingCollectionFetchJob::start); } void start() override { if (m_started) return; if (m_cache->isCollectionListPopulated()) { QTimer::singleShot(0, this, &CachingCollectionFetchJob::retrieveFromCache); } else { auto job = m_storage->fetchCollections(Akonadi::Collection::root(), Akonadi::StorageInterface::Recursive); job->setResource(m_resource); addSubjob(job->kjob()); } m_started = true; } Collection::List collections() const override { const auto isInputCollection = [this] (const Collection &collection) { return collection.id() == m_collection.id() || (!m_collection.remoteId().isEmpty() && collection.remoteId() == m_collection.remoteId()); }; if (m_depth == StorageInterface::Base) { auto it = std::find_if(m_collections.cbegin(), m_collections.cend(), isInputCollection); if (it != m_collections.cend()) return Collection::List() << *it; else return Collection::List(); } auto collections = m_collections; auto it = collections.begin(); if (m_depth == StorageInterface::FirstLevel) { it = std::remove_if(collections.begin(), collections.end(), [isInputCollection] (const Collection &collection) { return !isInputCollection(collection.parentCollection()); }); } else { it = std::remove_if(collections.begin(), collections.end(), [isInputCollection] (const Collection &collection) { auto parent = collection.parentCollection(); while (parent.isValid() && !isInputCollection(parent)) parent = parent.parentCollection(); return !isInputCollection(parent); }); } collections.erase(it, collections.end()); return collections; } void setResource(const QString &resource) override { m_resource = resource; } private: void slotResult(KJob *kjob) override { if (kjob->error()) { KCompositeJob::slotResult(kjob); return; } auto job = dynamic_cast(kjob); Q_ASSERT(job); auto cachedCollections = job->collections(); for (const auto &collection : job->collections()) { auto parent = collection.parentCollection(); while (parent.isValid() && parent != Akonadi::Collection::root()) { if (!cachedCollections.contains(parent)) { cachedCollections.append(parent); } parent = parent.parentCollection(); } } m_cache->setCollections(cachedCollections); m_collections = job->collections(); emitResult(); } void retrieveFromCache() { m_collections = m_cache->collections(); emitResult(); } bool m_started; StorageInterface::Ptr m_storage; Cache::Ptr m_cache; QString m_resource; const Collection m_collection; const StorageInterface::FetchDepth m_depth; Collection::List m_collections; }; class CachingCollectionItemsFetchJob : public KCompositeJob, public ItemFetchJobInterface { Q_OBJECT public: CachingCollectionItemsFetchJob(const StorageInterface::Ptr &storage, const Cache::Ptr &cache, const Collection &collection, QObject *parent = nullptr) : KCompositeJob(parent), m_started(false), m_storage(storage), m_cache(cache), m_collection(collection) { QTimer::singleShot(0, this, &CachingCollectionItemsFetchJob::start); } void start() override { if (m_started) return; if (m_cache->isCollectionPopulated(m_collection.id())) { QTimer::singleShot(0, this, &CachingCollectionItemsFetchJob::retrieveFromCache); } else { auto job = m_storage->fetchItems(m_collection, this); addSubjob(job->kjob()); } m_started = true; } Item::List items() const override { return m_items; } void setCollection(const Collection &collection) override { m_collection = collection; } private: void slotResult(KJob *kjob) override { if (kjob->error()) { KCompositeJob::slotResult(kjob); return; } auto job = dynamic_cast(kjob); Q_ASSERT(job); m_items = job->items(); m_cache->populateCollection(m_collection, m_items); emitResult(); } void retrieveFromCache() { m_items = m_cache->items(m_collection); emitResult(); } bool m_started; StorageInterface::Ptr m_storage; Cache::Ptr m_cache; Collection m_collection; Item::List m_items; }; class CachingSingleItemFetchJob : public KCompositeJob, public ItemFetchJobInterface { Q_OBJECT public: CachingSingleItemFetchJob(const StorageInterface::Ptr &storage, const Cache::Ptr &cache, const Item &item, QObject *parent = nullptr) : KCompositeJob(parent), m_started(false), m_storage(storage), m_cache(cache), m_item(item) { QTimer::singleShot(0, this, &CachingSingleItemFetchJob::start); } void start() override { if (m_started) return; const auto item = m_cache->item(m_item.id()); if (item.isValid()) { QTimer::singleShot(0, this, [this, item] { retrieveFromCache(item); }); } else { auto job = m_storage->fetchItem(m_item, this); job->setCollection(m_collection); addSubjob(job->kjob()); } m_started = true; } Item::List items() const override { return m_items; } void setCollection(const Collection &collection) override { m_collection = collection; } private: void slotResult(KJob *kjob) override { if (kjob->error()) { KCompositeJob::slotResult(kjob); return; } auto job = dynamic_cast(kjob); Q_ASSERT(job); m_items = job->items(); emitResult(); } void retrieveFromCache(const Item &item) { m_items = Item::List() << item; emitResult(); } bool m_started; StorageInterface::Ptr m_storage; Cache::Ptr m_cache; Item m_item; Collection m_collection; Item::List m_items; }; CachingStorage::CachingStorage(const Cache::Ptr &cache, const StorageInterface::Ptr &storage) : m_cache(cache), m_storage(storage) { } CachingStorage::~CachingStorage() { } Collection CachingStorage::defaultCollection() { return m_storage->defaultCollection(); } KJob *CachingStorage::createItem(Item item, Collection collection) { return m_storage->createItem(item, collection); } KJob *CachingStorage::updateItem(Item item, QObject *parent) { return m_storage->updateItem(item, parent); } -KJob *CachingStorage::removeItem(Item item) +KJob *CachingStorage::removeItem(Item item, QObject *parent) { - return m_storage->removeItem(item); + return m_storage->removeItem(item, parent); } KJob *CachingStorage::removeItems(Item::List items, QObject *parent) { return m_storage->removeItems(items, parent); } KJob *CachingStorage::moveItem(Item item, Collection collection, QObject *parent) { return m_storage->moveItem(item, collection, parent); } KJob *CachingStorage::moveItems(Item::List items, Collection collection, QObject *parent) { return m_storage->moveItems(items, collection, parent); } KJob *CachingStorage::createCollection(Collection collection, QObject *parent) { return m_storage->createCollection(collection, parent); } KJob *CachingStorage::updateCollection(Collection collection, QObject *parent) { return m_storage->updateCollection(collection, parent); } KJob *CachingStorage::removeCollection(Collection collection, QObject *parent) { return m_storage->removeCollection(collection, parent); } KJob *CachingStorage::createTransaction() { return m_storage->createTransaction(); } CollectionFetchJobInterface *CachingStorage::fetchCollections(Collection collection, StorageInterface::FetchDepth depth) { return new CachingCollectionFetchJob(m_storage, m_cache, collection, depth); } ItemFetchJobInterface *CachingStorage::fetchItems(Collection collection, QObject *parent) { return new CachingCollectionItemsFetchJob(m_storage, m_cache, collection, parent); } ItemFetchJobInterface *CachingStorage::fetchItem(Akonadi::Item item, QObject *parent) { return new CachingSingleItemFetchJob(m_storage, m_cache, item, parent); } #include "akonadicachingstorage.moc" diff --git a/src/akonadi/akonadicachingstorage.h b/src/akonadi/akonadicachingstorage.h index 029d1b4c..576f3d1c 100644 --- a/src/akonadi/akonadicachingstorage.h +++ b/src/akonadi/akonadicachingstorage.h @@ -1,65 +1,65 @@ /* This file is part of Zanshin Copyright 2015 Mario Bensi Copyright 2017 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_CACHING_STORAGE_H #define AKONADI_CACHING_STORAGE_H #include "akonadistorageinterface.h" #include "akonadicache.h" namespace Akonadi { class CachingStorage : public StorageInterface { public: explicit CachingStorage(const Cache::Ptr &cache, const StorageInterface::Ptr &storage); virtual ~CachingStorage(); Akonadi::Collection defaultCollection() override; KJob *createItem(Item item, Collection collection) override; KJob *updateItem(Item item, QObject *parent) override; - KJob *removeItem(Akonadi::Item item) override; + KJob *removeItem(Akonadi::Item item, QObject *parent) override; KJob *removeItems(Item::List items, QObject *parent) override; KJob *moveItem(Item item, Collection collection, QObject *parent) override; KJob *moveItems(Item::List item, Collection collection, QObject *parent = nullptr) override; KJob *createCollection(Collection collection, QObject *parent) override; KJob *updateCollection(Collection collection, QObject *parent) override; KJob *removeCollection(Collection collection, QObject *parent) override; KJob *createTransaction() override; CollectionFetchJobInterface *fetchCollections(Akonadi::Collection collection, FetchDepth depth) override; ItemFetchJobInterface *fetchItems(Akonadi::Collection collection, QObject *parent) override; ItemFetchJobInterface *fetchItem(Akonadi::Item item, QObject *parent) override; private: Cache::Ptr m_cache; StorageInterface::Ptr m_storage; }; } #endif // AKONADI_CACHING_STORAGE_H diff --git a/src/akonadi/akonadicontextrepository.cpp b/src/akonadi/akonadicontextrepository.cpp index 0a8fdacf..32cd00ae 100644 --- a/src/akonadi/akonadicontextrepository.cpp +++ b/src/akonadi/akonadicontextrepository.cpp @@ -1,132 +1,132 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens Copyright 2014 Franck Arrecot Copyright 2014 Rémi Benoit This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonadicontextrepository.h" #include "akonadiitemfetchjobinterface.h" #include "utils/compositejob.h" using namespace Akonadi; ContextRepository::ContextRepository(const StorageInterface::Ptr &storage, const SerializerInterface::Ptr &serializer): m_storage(storage), m_serializer(serializer) { } KJob *ContextRepository::create(Domain::Context::Ptr context, Domain::DataSource::Ptr source) { auto item = m_serializer->createItemFromContext(context); Q_ASSERT(!item.isValid()); auto collection = m_serializer->createCollectionFromDataSource(source); Q_ASSERT(collection.isValid()); return m_storage->createItem(item, collection); } KJob *ContextRepository::update(Domain::Context::Ptr context) { auto item = m_serializer->createItemFromContext(context); Q_ASSERT(item.isValid()); return m_storage->updateItem(item, this); } KJob *ContextRepository::remove(Domain::Context::Ptr context) { auto item = m_serializer->createItemFromContext(context); Q_ASSERT(item.isValid()); - return m_storage->removeItem(item); + return m_storage->removeItem(item, this); } KJob *ContextRepository::associate(Domain::Context::Ptr context, Domain::Task::Ptr child) { Item childItem = m_serializer->createItemFromTask(child); Q_ASSERT(childItem.isValid()); auto job = new Utils::CompositeJob(); ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(childItem, this); job->install(fetchItemJob->kjob(), [fetchItemJob, context, job, this] { if (fetchItemJob->kjob()->error() != KJob::NoError) return; Q_ASSERT(fetchItemJob->items().size() == 1); auto childItem = fetchItemJob->items().at(0); m_serializer->addContextToTask(context, childItem); auto updateJob = m_storage->updateItem(childItem, this); job->addSubjob(updateJob); updateJob->start(); }); return job; } KJob *ContextRepository::dissociate(Domain::Context::Ptr context, Domain::Task::Ptr child) { Item childItem = m_serializer->createItemFromTask(child); Q_ASSERT(childItem.isValid()); auto job = new Utils::CompositeJob(); ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(childItem, this); job->install(fetchItemJob->kjob(), [fetchItemJob, context, job, this] { if (fetchItemJob->kjob()->error() != KJob::NoError) return; Q_ASSERT(fetchItemJob->items().size() == 1); auto childItem = fetchItemJob->items().at(0); m_serializer->removeContextFromTask(context, childItem); auto updateJob = m_storage->updateItem(childItem, this); job->addSubjob(updateJob); updateJob->start(); }); return job; } KJob *ContextRepository::dissociateAll(Domain::Task::Ptr child) { Item childItem; childItem = m_serializer->createItemFromTask(child); Q_ASSERT(childItem.isValid()); auto job = new Utils::CompositeJob(); ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(childItem, this); job->install(fetchItemJob->kjob(), [fetchItemJob, job, this] { if (fetchItemJob->kjob()->error() != KJob::NoError) return; Q_ASSERT(fetchItemJob->items().size() == 1); auto childItem = fetchItemJob->items().at(0); childItem.clearTags(); auto updateJob = m_storage->updateItem(childItem, this); job->addSubjob(updateJob); updateJob->start(); }); return job; } diff --git a/src/akonadi/akonadiprojectrepository.cpp b/src/akonadi/akonadiprojectrepository.cpp index 2e683c84..5f75cb74 100644 --- a/src/akonadi/akonadiprojectrepository.cpp +++ b/src/akonadi/akonadiprojectrepository.cpp @@ -1,139 +1,139 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonadiprojectrepository.h" #include "akonadiitemfetchjobinterface.h" #include "utils/compositejob.h" using namespace Akonadi; ProjectRepository::ProjectRepository(const StorageInterface::Ptr &storage, const SerializerInterface::Ptr &serializer) : m_storage(storage), m_serializer(serializer) { } KJob *ProjectRepository::create(Domain::Project::Ptr project, Domain::DataSource::Ptr source) { auto item = m_serializer->createItemFromProject(project); Q_ASSERT(!item.isValid()); auto collection = m_serializer->createCollectionFromDataSource(source); Q_ASSERT(collection.isValid()); return m_storage->createItem(item, collection); } KJob *ProjectRepository::update(Domain::Project::Ptr project) { auto item = m_serializer->createItemFromProject(project); Q_ASSERT(item.isValid()); return m_storage->updateItem(item, this); } KJob *ProjectRepository::remove(Domain::Project::Ptr project) { auto item = m_serializer->createItemFromProject(project); Q_ASSERT(item.isValid()); - return m_storage->removeItem(item); + return m_storage->removeItem(item, this); } KJob *ProjectRepository::associate(Domain::Project::Ptr parent, Domain::Task::Ptr child) { Item childItem = m_serializer->createItemFromTask(child); Q_ASSERT(childItem.isValid()); auto job = new Utils::CompositeJob(); ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(childItem, this); job->install(fetchItemJob->kjob(), [fetchItemJob, parent, child, job, this] { if (fetchItemJob->kjob()->error() != KJob::NoError) return; Q_ASSERT(fetchItemJob->items().size() == 1); auto childItem = fetchItemJob->items().at(0); m_serializer->updateItemProject(childItem, parent); // Check collections to know if we need to move child auto parentItem = m_serializer->createItemFromProject(parent); ItemFetchJobInterface *fetchParentItemJob = m_storage->fetchItem(parentItem, this); job->install(fetchParentItemJob->kjob(), [fetchParentItemJob, child, childItem, job, this] { if (fetchParentItemJob->kjob()->error() != KJob::NoError) return; Q_ASSERT(fetchParentItemJob->items().size() == 1); auto parentItem = fetchParentItemJob->items().at(0); const int itemCollectionId = childItem.parentCollection().id(); const int parentCollectionId = parentItem.parentCollection().id(); if (itemCollectionId != parentCollectionId) { ItemFetchJobInterface *fetchChildrenItemJob = m_storage->fetchItems(childItem.parentCollection(), this); job->install(fetchChildrenItemJob->kjob(), [fetchChildrenItemJob, childItem, parentItem, job, this] { if (fetchChildrenItemJob->kjob()->error() != KJob::NoError) return; Item::List childItems = m_serializer->filterDescendantItems(fetchChildrenItemJob->items(), childItem); auto transaction = m_storage->createTransaction(); m_storage->updateItem(childItem, transaction); childItems.push_front(childItem); m_storage->moveItems(childItems, parentItem.parentCollection(), transaction); job->addSubjob(transaction); transaction->start(); }); } else { auto updateJob = m_storage->updateItem(childItem, this); job->addSubjob(updateJob); updateJob->start(); } }); }); return job; } KJob *ProjectRepository::dissociate(Domain::Task::Ptr child) { auto job = new Utils::CompositeJob(); const auto childItem = m_serializer->createItemFromTask(child); Q_ASSERT(childItem.isValid()); ItemFetchJobInterface *fetchItemJob = m_storage->fetchItem(childItem, this); job->install(fetchItemJob->kjob(), [fetchItemJob, job, this] { if (fetchItemJob->kjob()->error() != KJob::NoError) return; Q_ASSERT(fetchItemJob->items().size() == 1); auto childItem = fetchItemJob->items().at(0); m_serializer->removeItemParent(childItem); auto updateJob = m_storage->updateItem(childItem, this); job->addSubjob(updateJob); updateJob->start(); }); return job; } diff --git a/src/akonadi/akonadistorage.cpp b/src/akonadi/akonadistorage.cpp index 29bc88e0..677614f2 100644 --- a/src/akonadi/akonadistorage.cpp +++ b/src/akonadi/akonadistorage.cpp @@ -1,269 +1,269 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonadistorage.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "akonadi/akonadicollectionfetchjobinterface.h" #include "akonadi/akonadiitemfetchjobinterface.h" #include "akonadi/akonadistoragesettings.h" using namespace Akonadi; class CollectionJob : public CollectionFetchJob, public CollectionFetchJobInterface { Q_OBJECT public: CollectionJob(const Collection &collection, Type type = FirstLevel, QObject *parent = nullptr) : CollectionFetchJob(collection, type, parent), m_collection(collection), m_type(type) { } Collection::List collections() const override { auto collections = CollectionFetchJob::collections(); // Memorize them to reconstruct the ancestor chain later QMap collectionsMap; collectionsMap[m_collection.id()] = m_collection; foreach (const auto &collection, collections) { collectionsMap[collection.id()] = collection; } // Why the hell isn't fetchScope() const and returning a reference??? auto self = const_cast(this); const auto allowedMimeTypes = self->fetchScope().contentMimeTypes().toSet(); if (!allowedMimeTypes.isEmpty()) { collections.erase(std::remove_if(collections.begin(), collections.end(), [allowedMimeTypes] (const Collection &collection) { auto mimeTypes = collection.contentMimeTypes().toSet(); return !mimeTypes.intersects(allowedMimeTypes); }), collections.end()); } if (m_type != Base) { // Replace the dummy parents in the ancestor chain with proper ones // full of juicy data std::function reconstructAncestors = [collectionsMap, &reconstructAncestors, this] (const Collection &collection) -> Collection { Q_ASSERT(collection.isValid()); if (collection == m_collection) return collection; auto parent = collection.parentCollection(); auto reconstructedParent = reconstructAncestors(collectionsMap[parent.id()]); auto result = collection; result.setParentCollection(reconstructedParent); return result; }; std::transform(collections.begin(), collections.end(), collections.begin(), reconstructAncestors); } return collections; } void setResource(const QString &resource) override { fetchScope().setResource(resource); } private: const Collection m_collection; const Type m_type; }; class ItemJob : public ItemFetchJob, public ItemFetchJobInterface { Q_OBJECT public: using ItemFetchJob::ItemFetchJob; Item::List items() const override { return ItemFetchJob::items(); } void setCollection(const Collection &collection) override { ItemFetchJob::setCollection(collection); } }; Storage::Storage() { } Storage::~Storage() { } Collection Storage::defaultCollection() { return StorageSettings::instance().defaultCollection(); } KJob *Storage::createItem(Item item, Collection collection) { return new ItemCreateJob(item, collection); } KJob *Storage::updateItem(Item item, QObject *parent) { return new ItemModifyJob(item, parent); } -KJob *Storage::removeItem(Item item) +KJob *Storage::removeItem(Item item, QObject *parent) { - return new ItemDeleteJob(item); + return new ItemDeleteJob(item, parent); } KJob *Storage::removeItems(Item::List items, QObject *parent) { return new ItemDeleteJob(items, parent); } KJob *Storage::moveItem(Item item, Collection collection, QObject *parent) { return new ItemMoveJob(item, collection, parent); } KJob *Storage::moveItems(Item::List items, Collection collection, QObject *parent) { return new ItemMoveJob(items, collection, parent); } KJob *Storage::createCollection(Collection collection, QObject *parent) { return new CollectionCreateJob(collection, parent); } KJob *Storage::updateCollection(Collection collection, QObject *parent) { return new CollectionModifyJob(collection, parent); } KJob *Storage::removeCollection(Collection collection, QObject *parent) { return new CollectionDeleteJob(collection, parent); } KJob *Storage::createTransaction() { return new TransactionSequence(); } CollectionFetchJobInterface *Storage::fetchCollections(Collection collection, StorageInterface::FetchDepth depth) { auto job = new CollectionJob(collection, jobTypeFromDepth(depth)); auto scope = job->fetchScope(); scope.setContentMimeTypes({KCalCore::Todo::todoMimeType()}); scope.setIncludeStatistics(true); scope.setAncestorRetrieval(CollectionFetchScope::All); scope.setListFilter(Akonadi::CollectionFetchScope::Display); job->setFetchScope(scope); return job; } ItemFetchJobInterface *Storage::fetchItems(Collection collection, QObject *parent) { auto job = new ItemJob(collection, parent); configureItemFetchJob(job); return job; } ItemFetchJobInterface *Storage::fetchItem(Akonadi::Item item, QObject *parent) { auto job = new ItemJob(item, parent); configureItemFetchJob(job); return job; } ItemFetchJobInterface *Storage::fetchItemsWithTags(Collection collection) { auto job = new ItemJob(collection); configureItemFetchJob(job); job->fetchScope().setFetchTags(true); job->fetchScope().tagFetchScope().setFetchIdOnly(false); return job; } CollectionFetchJob::Type Storage::jobTypeFromDepth(StorageInterface::FetchDepth depth) { auto jobType = CollectionJob::Base; switch (depth) { case Base: jobType = CollectionJob::Base; break; case FirstLevel: jobType = CollectionJob::FirstLevel; break; case Recursive: jobType = CollectionJob::Recursive; break; default: qFatal("Unexpected enum value"); break; } return jobType; } void Storage::configureItemFetchJob(ItemJob *job) { auto scope = job->fetchScope(); scope.fetchFullPayload(); scope.fetchAllAttributes(); scope.setFetchTags(false); scope.setAncestorRetrieval(ItemFetchScope::All); job->setFetchScope(scope); } #include "akonadistorage.moc" diff --git a/src/akonadi/akonadistorage.h b/src/akonadi/akonadistorage.h index 795651a1..fd21a621 100644 --- a/src/akonadi/akonadistorage.h +++ b/src/akonadi/akonadistorage.h @@ -1,68 +1,68 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_STORAGE_H #define AKONADI_STORAGE_H #include "akonadistorageinterface.h" #include class ItemJob; namespace Akonadi { class Storage : public StorageInterface { public: Storage(); virtual ~Storage(); Akonadi::Collection defaultCollection() override; KJob *createItem(Item item, Collection collection) override; KJob *updateItem(Item item, QObject *parent) override; - KJob *removeItem(Akonadi::Item item) override; + KJob *removeItem(Akonadi::Item item, QObject *parent) override; KJob *removeItems(Item::List items, QObject *parent) override; KJob *moveItem(Item item, Collection collection, QObject *parent) override; KJob *moveItems(Item::List item, Collection collection, QObject *parent) override; KJob *createCollection(Collection collection, QObject *parent) override; KJob *updateCollection(Collection collection, QObject *parent) override; KJob *removeCollection(Collection collection, QObject *parent) override; KJob *createTransaction() override; CollectionFetchJobInterface *fetchCollections(Akonadi::Collection collection, FetchDepth depth) override; ItemFetchJobInterface *fetchItems(Akonadi::Collection collection, QObject *parent) override; ItemFetchJobInterface *fetchItem(Akonadi::Item item, QObject *parent) override; ItemFetchJobInterface *fetchItemsWithTags(Akonadi::Collection collection); private: CollectionFetchJob::Type jobTypeFromDepth(StorageInterface::FetchDepth depth); void configureItemFetchJob(ItemJob *job); }; } #endif // AKONADI_STORAGE_H diff --git a/src/akonadi/akonadistorageinterface.h b/src/akonadi/akonadistorageinterface.h index a0b425ff..97466240 100644 --- a/src/akonadi/akonadistorageinterface.h +++ b/src/akonadi/akonadistorageinterface.h @@ -1,77 +1,77 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_STORAGEINTERFACE_H #define AKONADI_STORAGEINTERFACE_H #include #include class KJob; class QObject; class QByteArray; namespace Akonadi { class Collection; class CollectionFetchJobInterface; class ItemFetchJobInterface; class StorageInterface { public: typedef QSharedPointer Ptr; enum FetchDepth { Base, FirstLevel, Recursive }; StorageInterface(); virtual ~StorageInterface(); virtual Akonadi::Collection defaultCollection() = 0; virtual KJob *createItem(Akonadi::Item item, Akonadi::Collection collection) = 0; virtual KJob *updateItem(Akonadi::Item item, QObject *parent) = 0; - virtual KJob *removeItem(Akonadi::Item item) = 0; + virtual KJob *removeItem(Akonadi::Item item, QObject *parent) = 0; virtual KJob *removeItems(Item::List items, QObject *parent) = 0; virtual KJob *moveItem(Item item, Collection collection, QObject *parent) = 0; virtual KJob *moveItems(Item::List item, Collection collection, QObject *parent) = 0; virtual KJob *createCollection(Collection collection, QObject *parent) = 0; virtual KJob *updateCollection(Collection collection, QObject *parent) = 0; virtual KJob *removeCollection(Collection collection, QObject *parent) = 0; virtual KJob *createTransaction() = 0; virtual CollectionFetchJobInterface *fetchCollections(Akonadi::Collection collection, FetchDepth depth) = 0; virtual ItemFetchJobInterface *fetchItems(Akonadi::Collection collection, QObject *parent) = 0; virtual ItemFetchJobInterface *fetchItem(Akonadi::Item item, QObject *parent) = 0; }; } #endif // AKONADI_STORAGEINTERFACE_H diff --git a/tests/testlib/akonadifakestorage.cpp b/tests/testlib/akonadifakestorage.cpp index 7ae3e575..51774ad7 100644 --- a/tests/testlib/akonadifakestorage.cpp +++ b/tests/testlib/akonadifakestorage.cpp @@ -1,430 +1,430 @@ /* This file is part of Zanshin Copyright 2015 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonadifakestorage.h" #include #include "akonadi/akonadistoragesettings.h" #include "akonadifakedata.h" #include "akonadifakejobs.h" #include "utils/jobhandler.h" using namespace Testlib; class AkonadiFakeTransaction : public FakeJob { Q_OBJECT public: explicit AkonadiFakeTransaction() : FakeJob(), m_nextIdx(0) { } private slots: void onTimeout() override { auto jobs = childJobs(); if (m_nextIdx == 0) { const auto it = std::find_if(jobs.constBegin(), jobs.constEnd(), [] (FakeJob *job) { return job->expectedError() != 0; }); if (it != jobs.constEnd()) { setError((*it)->expectedError()); setErrorText((*it)->expectedErrorText()); emitResult(); return; } } if (m_nextIdx >= jobs.size()) { emitResult(); return; } auto job = jobs[m_nextIdx]; connect(job, &KJob::result, this, &AkonadiFakeTransaction::onTimeout); job->start(); m_nextIdx++; } private: QList childJobs() const { QList jobs = findChildren(); jobs.erase(std::remove_if(jobs.begin(), jobs.end(), [this] (FakeJob *job) { return job->parent() != this; }), jobs.end()); return jobs; } int m_nextIdx; }; Utils::JobHandler::StartMode startModeForParent(QObject *parent) { bool isTransaction = qobject_cast(parent); return isTransaction ? Utils::JobHandler::ManualStart : Utils::JobHandler::AutoStart; } void noop() {} AkonadiFakeStorage::AkonadiFakeStorage(AkonadiFakeData *data) : m_data(data) { } Akonadi::Collection AkonadiFakeStorage::defaultCollection() { return Akonadi::StorageSettings::instance().defaultCollection(); } KJob *AkonadiFakeStorage::createItem(Akonadi::Item item, Akonadi::Collection collection) { Q_ASSERT(!item.isValid()); auto job = new FakeJob; if (!m_data->item(item.id()).isValid()) { job->setExpectedError(m_data->storageBehavior().createNextItemErrorCode(), m_data->storageBehavior().createNextItemErrorText()); Utils::JobHandler::install(job, [=] () mutable { if (!job->error()) { item.setId(m_data->maxItemId() + 1); item.setParentCollection(collection); // Force payload detach item.setPayloadFromData(item.payloadData()); m_data->createItem(item); } }); } else { job->setExpectedError(1, QStringLiteral("Item already exists")); Utils::JobHandler::install(job, noop); } return job; } KJob *AkonadiFakeStorage::updateItem(Akonadi::Item item, QObject *parent) { auto job = new FakeJob(parent); auto startMode = startModeForParent(parent); if (m_data->item(item.id()).isValid()) { job->setExpectedError(m_data->storageBehavior().updateNextItemErrorCode(), m_data->storageBehavior().updateNextItemErrorText()); Utils::JobHandler::install(job, [=] () mutable { if (!job->error()) { // Force payload detach item.setPayloadFromData(item.payloadData()); m_data->modifyItem(item); } }, startMode); } else { job->setExpectedError(1, QStringLiteral("Item doesn't exist")); Utils::JobHandler::install(job, noop, startMode); } return job; } -KJob *AkonadiFakeStorage::removeItem(Akonadi::Item item) +KJob *AkonadiFakeStorage::removeItem(Akonadi::Item item, QObject *parent) { - auto job = new FakeJob; + auto job = new FakeJob(parent); if (m_data->item(item.id()).isValid()) { Utils::JobHandler::install(job, [=] { if (!job->error()) { m_data->removeItem(item); } }); } else { job->setExpectedError(1, QStringLiteral("Item doesn't exist")); Utils::JobHandler::install(job, noop); } return job; } KJob *AkonadiFakeStorage::removeItems(Akonadi::Item::List items, QObject *parent) { auto job = new FakeJob; auto startMode = startModeForParent(parent); bool allItemsExist = std::all_of(items.constBegin(), items.constEnd(), [=] (const Akonadi::Item &item) { return m_data->item(item.id()).isValid(); }); if (allItemsExist) { job->setExpectedError(m_data->storageBehavior().deleteNextItemErrorCode(), m_data->storageBehavior().deleteNextItemErrorText()); Utils::JobHandler::install(job, [=] { if (!job->error()) { foreach (const Akonadi::Item &item, items) { m_data->removeItem(item); } } }, startMode); } else { job->setExpectedError(1, QStringLiteral("At least one item doesn't exist")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::moveItem(Akonadi::Item item, Akonadi::Collection collection, QObject *parent) { auto job = new FakeJob(parent); auto startMode = startModeForParent(parent); if (m_data->item(item.id()).isValid() && m_data->collection(collection.id()).isValid()) { Utils::JobHandler::install(job, [=] () mutable { if (!job->error()) { item.setParentCollection(collection); // Force payload detach item.setPayloadFromData(item.payloadData()); m_data->modifyItem(item); } }, startMode); } else { job->setExpectedError(1, QStringLiteral("The item or the collection doesn't exist")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::moveItems(Akonadi::Item::List items, Akonadi::Collection collection, QObject *parent) { using namespace std::placeholders; auto job = new FakeJob(parent); auto startMode = startModeForParent(parent); bool allItemsExist = std::all_of(items.constBegin(), items.constEnd(), [=] (const Akonadi::Item &item) { return m_data->item(item.id()).isValid(); }); if (allItemsExist && m_data->collection(collection.id()).isValid()) { Utils::JobHandler::install(job, [=] () mutable { if (!job->error()) { std::transform(items.constBegin(), items.constEnd(), items.begin(), [=] (const Akonadi::Item &item) { auto result = item; result.setParentCollection(collection); // Force payload detach result.setPayloadFromData(result.payloadData()); return result; }); foreach (const Akonadi::Item &item, items) { m_data->modifyItem(item); } } }, startMode); } else { job->setExpectedError(1, QStringLiteral("One of the items or the collection doesn't exist")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::createCollection(Akonadi::Collection collection, QObject *parent) { Q_ASSERT(!collection.isValid()); auto job = new FakeJob(parent); auto startMode = startModeForParent(parent); if (!m_data->collection(collection.id()).isValid()) { Utils::JobHandler::install(job, [=] () mutable { if (!job->error()) { collection.setId(m_data->maxCollectionId() + 1); m_data->createCollection(collection); } }, startMode); } else { job->setExpectedError(1, QStringLiteral("The collection already exists")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::updateCollection(Akonadi::Collection collection, QObject *parent) { auto job = new FakeJob(parent); auto startMode = startModeForParent(parent); if (m_data->collection(collection.id()).isValid()) { Utils::JobHandler::install(job, [=] { if (!job->error()) { m_data->modifyCollection(collection); } }, startMode); } else { job->setExpectedError(1, QStringLiteral("The collection doesn't exist")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::removeCollection(Akonadi::Collection collection, QObject *parent) { auto job = new FakeJob(parent); auto startMode = startModeForParent(parent); if (m_data->collection(collection.id()).isValid()) { Utils::JobHandler::install(job, [=] { if (!job->error()) { m_data->removeCollection(collection); } }, startMode); } else { job->setExpectedError(1, QStringLiteral("The collection doesn't exist")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::createTransaction() { auto job = new AkonadiFakeTransaction; Utils::JobHandler::install(job, noop); return job; } Akonadi::CollectionFetchJobInterface *AkonadiFakeStorage::fetchCollections(Akonadi::Collection collection, Akonadi::StorageInterface::FetchDepth depth) { auto job = new AkonadiFakeCollectionFetchJob; auto children = Akonadi::Collection::List(); switch (depth) { case Base: children << m_data->collection(findId(collection)); break; case FirstLevel: children << m_data->childCollections(findId(collection)); break; case Recursive: children = collectChildren(collection); break; } auto collections = children; if (depth != Base) { // Replace the dummy parents in the ancestor chain with proper ones // full of juicy data using namespace std::placeholders; auto completeCollection = std::bind(&AkonadiFakeData::reconstructAncestors, m_data, _1, collection); std::transform(collections.begin(), collections.end(), collections.begin(), completeCollection); } const auto behavior = m_data->storageBehavior().fetchCollectionsBehavior(collection.id()); if (behavior == AkonadiFakeStorageBehavior::NormalFetch) job->setCollections(collections); job->setExpectedError(m_data->storageBehavior().fetchCollectionsErrorCode(collection.id())); Utils::JobHandler::install(job, noop); return job; } Akonadi::ItemFetchJobInterface *AkonadiFakeStorage::fetchItems(Akonadi::Collection collection, QObject *parent) { auto items = m_data->childItems(findId(collection)); std::transform(items.begin(), items.end(), items.begin(), [this] (const Akonadi::Item &item) { auto result = m_data->reconstructItemDependencies(item); // Force payload detach result.setPayloadFromData(result.payloadData()); return result; }); auto job = new AkonadiFakeItemFetchJob(parent); const auto behavior = m_data->storageBehavior().fetchItemsBehavior(collection.id()); if (behavior == AkonadiFakeStorageBehavior::NormalFetch) job->setItems(items); job->setExpectedError(m_data->storageBehavior().fetchItemsErrorCode(collection.id())); Utils::JobHandler::install(job, noop); return job; } Akonadi::ItemFetchJobInterface *AkonadiFakeStorage::fetchItem(Akonadi::Item item, QObject *parent) { auto fullItem = m_data->item(findId(item)); fullItem = m_data->reconstructItemDependencies(fullItem); // Force payload detach fullItem.setPayloadFromData(fullItem.payloadData()); auto job = new AkonadiFakeItemFetchJob(parent); const auto behavior = m_data->storageBehavior().fetchItemBehavior(item.id()); if (behavior == AkonadiFakeStorageBehavior::NormalFetch) job->setItems(Akonadi::Item::List() << fullItem); job->setExpectedError(m_data->storageBehavior().fetchItemErrorCode(item.id())); Utils::JobHandler::install(job, noop); return job; } Akonadi::Collection::Id AkonadiFakeStorage::findId(const Akonadi::Collection &collection) { if (collection.isValid() || collection.remoteId().isEmpty()) return collection.id(); const auto remoteId = collection.remoteId(); auto collections = m_data->collections(); auto result = std::find_if(collections.constBegin(), collections.constEnd(), [remoteId] (const Akonadi::Collection &collection) { return collection.remoteId() == remoteId; }); return (result != collections.constEnd()) ? result->id() : collection.id(); } Akonadi::Item::Id AkonadiFakeStorage::findId(const Akonadi::Item &item) { if (item.isValid() || item.remoteId().isEmpty()) return item.id(); const auto remoteId = item.remoteId(); auto items = m_data->items(); auto result = std::find_if(items.constBegin(), items.constEnd(), [remoteId] (const Akonadi::Item &item) { return item.remoteId() == remoteId; }); return (result != items.constEnd()) ? result->id() : item.id(); } Akonadi::Collection::List AkonadiFakeStorage::collectChildren(const Akonadi::Collection &root) { auto collections = Akonadi::Collection::List(); foreach (const auto &child, m_data->childCollections(findId(root))) { if (child.enabled()) collections << m_data->collection(findId(child)); collections += collectChildren(child); } return collections; } #include "akonadifakestorage.moc" diff --git a/tests/testlib/akonadifakestorage.h b/tests/testlib/akonadifakestorage.h index 82bc2246..761a0edc 100644 --- a/tests/testlib/akonadifakestorage.h +++ b/tests/testlib/akonadifakestorage.h @@ -1,67 +1,67 @@ /* This file is part of Zanshin Copyright 2015 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TESTLIB_AKONADIFAKESTORAGE_H #define TESTLIB_AKONADIFAKESTORAGE_H #include "akonadi/akonadistorageinterface.h" namespace Testlib { class AkonadiFakeData; class AkonadiFakeStorage : public Akonadi::StorageInterface { public: explicit AkonadiFakeStorage(AkonadiFakeData *data); Akonadi::Collection defaultCollection() override; KJob *createItem(Akonadi::Item item, Akonadi::Collection collection) override; KJob *updateItem(Akonadi::Item item, QObject *parent) override; - KJob *removeItem(Akonadi::Item item) override; + KJob *removeItem(Akonadi::Item item, QObject *parent) override; KJob *removeItems(Akonadi::Item::List items, QObject *parent) override; KJob *moveItem(Akonadi::Item item, Akonadi::Collection collection, QObject *parent) override; KJob *moveItems(Akonadi::Item::List items, Akonadi::Collection collection, QObject *parent) override; KJob *createCollection(Akonadi::Collection collection, QObject *parent) override; KJob *updateCollection(Akonadi::Collection collection, QObject *parent) override; KJob *removeCollection(Akonadi::Collection collection, QObject *parent) override; KJob *createTransaction() override; Akonadi::CollectionFetchJobInterface *fetchCollections(Akonadi::Collection collection, FetchDepth depth) override; Akonadi::ItemFetchJobInterface *fetchItems(Akonadi::Collection collection, QObject *parent) override; Akonadi::ItemFetchJobInterface *fetchItem(Akonadi::Item item, QObject *parent) override; private: Akonadi::Collection::Id findId(const Akonadi::Collection &collection); Akonadi::Item::Id findId(const Akonadi::Item &item); Akonadi::Collection::List collectChildren(const Akonadi::Collection &root); AkonadiFakeData *m_data; }; } #endif // TESTLIB_AKONADIFAKESTORAGE_H diff --git a/tests/testlib/akonadistoragetestbase.cpp b/tests/testlib/akonadistoragetestbase.cpp index d60dcf3b..fbe43a0f 100644 --- a/tests/testlib/akonadistoragetestbase.cpp +++ b/tests/testlib/akonadistoragetestbase.cpp @@ -1,830 +1,830 @@ /* This file is part of Zanshin Copyright 2014-2015 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonadistoragetestbase.h" #include #include #include #include #include #include #include "utils/mem_fn.h" #include "AkonadiCore/qtest_akonadi.h" #include #include "akonadi/akonadiapplicationselectedattribute.h" #include "akonadi/akonadicollectionfetchjobinterface.h" #include "akonadi/akonadiitemfetchjobinterface.h" #include "akonadi/akonadimonitorimpl.h" #include "akonadi/akonadistorage.h" #include "akonadi/akonadistoragesettings.h" #include "akonadi/akonaditimestampattribute.h" using namespace Testlib; AkonadiStorageTestBase::AkonadiStorageTestBase(QObject *parent) : QObject(parent) { qRegisterMetaType(); qRegisterMetaType(); } void AkonadiStorageTestBase::cleanupTestCase() { // Give a chance for jobs still waiting for an event loop // run to be deleted through deleteLater() QTest::qWait(10); } void AkonadiStorageTestBase::dumpTree() { TestLib::AkonadiDebug::dumpTree(createStorage()); } void AkonadiStorageTestBase::shouldListCollections_data() { QTest::addColumn("collection"); QTest::addColumn("expectedNames"); QTest::addColumn("depth"); QTest::newRow("all") << Akonadi::Collection::root() << QStringList({ "Calendar1", "Calendar2", "Calendar3", "Change me!", "Destroy me!" }) << Akonadi::Storage::Recursive; QTest::newRow("base type") << calendar2() << QStringList({"Calendar2"}) << Akonadi::Storage::Base; QTest::newRow("firstLevel type") << calendar1() << QStringList({"Calendar2"}) << Akonadi::Storage::FirstLevel; QTest::newRow("recursive type") << calendar1() << QStringList({"Calendar2", "Calendar3"}) << Akonadi::Storage::Recursive; } void AkonadiStorageTestBase::shouldListCollections() { // GIVEN QFETCH(Akonadi::Collection, collection); QFETCH(QStringList, expectedNames); QFETCH(Akonadi::StorageInterface::FetchDepth, depth); auto storage = createStorage(); // WHEN auto job = storage->fetchCollections(collection, depth); AKVERIFYEXEC(job->kjob()); // THEN auto collections = job->collections(); QStringList collectionNames; collectionNames.reserve(collections.size()); foreach (const auto &collection, collections) { collectionNames << collection.name(); } collectionNames.sort(); QCOMPARE(collectionNames, expectedNames); } void AkonadiStorageTestBase::shouldRetrieveAllCollectionAncestors() { // GIVEN auto storage = createStorage(); // WHEN auto job = storage->fetchCollections(Akonadi::Collection::root(), Akonadi::Storage::Recursive); AKVERIFYEXEC(job->kjob()); // THEN auto collections = job->collections(); foreach (const auto &collection, collections) { auto parent = collection.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); QVERIFY(!parent.displayName().isEmpty()); parent = parent.parentCollection(); } } } void AkonadiStorageTestBase::shouldListFullItemsInACollection() { // GIVEN auto storage = createStorage(); const QStringList expectedRemoteIds = { "rid-errands-context", "rid-online-context", "{1d33862f-f274-4c67-ab6c-362d56521ff4}", "{1d33862f-f274-4c67-ab6c-362d56521ff5}", "{1d33862f-f274-4c67-ab6c-362d56521ff6}", "{7824df00-2fd6-47a4-8319-52659dc82005}", "{7824df00-2fd6-47a4-8319-52659dc82006}", "{d0159c99-0d23-41fa-bb5f-tasktoremove}", }; // WHEN auto job = storage->fetchItems(calendar2(), nullptr); AKVERIFYEXEC(job->kjob()); // THEN auto items = job->items(); QStringList itemRemoteIds; itemRemoteIds.reserve(items.size()); foreach (const auto &item, items) { const auto rid = item.remoteId(); itemRemoteIds << rid; QVERIFY(item.loadedPayloadParts().contains(Akonadi::Item::FullPayload)); QVERIFY(item.modificationTime().isValid()); if (rid.endsWith("context")) { QVERIFY2(item.attributes().isEmpty(), qPrintable(rid)); QVERIFY2(item.flags().isEmpty(), qPrintable(rid)); } else { QVERIFY2(!item.attributes().isEmpty(), qPrintable(rid)); QVERIFY2(!item.flags().isEmpty(), qPrintable(rid)); } auto parent = item.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } itemRemoteIds.sort(); QCOMPARE(itemRemoteIds, expectedRemoteIds); } void AkonadiStorageTestBase::shouldNotifyCollectionAdded() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::collectionAdded); MonitorSpy monitorSpy(monitor.data()); // A collection Akonadi::Collection collection; collection.setParentCollection(calendar2()); collection.setName(QStringLiteral("Foo!")); collection.setContentMimeTypes(QStringList() << QStringLiteral("application/x-vnd.akonadi.calendar.todo")); // WHEN auto storage = createStorage(); auto job = storage->createCollection(collection, nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedCollection = spy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.name(), collection.name()); auto parent = notifiedCollection.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldNotifyCollectionRemoved() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::collectionRemoved); MonitorSpy monitorSpy(monitor.data()); // An existing item (if we trust the test data) Akonadi::Collection collection = fetchCollectionByRID(QStringLiteral("{1f78b360-a01b-4785-9187-75450190342c}")); QVERIFY(collection.isValid()); // WHEN auto storage = createStorage(); auto job = storage->removeCollection(collection, nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedCollection= spy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); } void AkonadiStorageTestBase::shouldNotifyCollectionChanged() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::collectionChanged); MonitorSpy monitorSpy(monitor.data()); // A collection with an existing id (if we trust the test data) Akonadi::Collection collection = fetchCollectionByRID(QStringLiteral("{28ef9f03-4ebc-4e33-970f-f379775894f9}")); QVERIFY(collection.isValid()); collection.setName(QStringLiteral("Bar!")); // WHEN auto storage = createStorage(); auto job = storage->updateCollection(collection, nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedCollection = spy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); QCOMPARE(notifiedCollection.name(), collection.name()); auto parent = notifiedCollection.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldNotifyItemAdded() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemAdded); MonitorSpy monitorSpy(monitor.data()); // A todo... KCalCore::Todo::Ptr todo(new KCalCore::Todo); todo->setSummary(QStringLiteral("summary")); todo->setDescription(QStringLiteral("content")); todo->setCompleted(false); todo->setDtStart(QDateTime(QDate(2013, 11, 24))); todo->setDtDue(QDateTime(QDate(2014, 03, 01))); // ... as payload of an item... Akonadi::Item item; item.setMimeType(QStringLiteral("application/x-vnd.akonadi.calendar.todo")); item.setPayload(todo); item.addAttribute(new Akonadi::EntityDisplayAttribute); // WHEN auto storage = createStorage(); auto job = storage->createItem(item, calendar2()); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(*notifiedItem.payload(), *todo); QVERIFY(notifiedItem.hasAttribute()); auto parent = notifiedItem.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldNotifyItemRemoved() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemRemoved); MonitorSpy monitorSpy(monitor.data()); Akonadi::Item item = fetchItemByRID(QStringLiteral("{d0159c99-0d23-41fa-bb5f-tasktoremove}"), calendar2()); QVERIFY(item.isValid()); // WHEN auto storage = createStorage(); - auto job = storage->removeItem(item); + auto job = storage->removeItem(item, nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item.id()); auto parent = notifiedItem.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldNotifyItemChanged() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemChanged); MonitorSpy monitorSpy(monitor.data()); // A todo... KCalCore::Todo::Ptr todo(new KCalCore::Todo); todo->setSummary(QStringLiteral("summary")); todo->setDescription(QStringLiteral("content")); todo->setCompleted(false); todo->setDtStart(QDateTime(QDate(2013, 11, 24))); todo->setDtDue(QDateTime(QDate(2014, 03, 01))); // ... as payload of an existing item (if we trust the test data)... Akonadi::Item item = fetchItemByRID(QStringLiteral("{1d33862f-f274-4c67-ab6c-362d56521ff6}"), calendar2()); QVERIFY(item.isValid()); item.setMimeType(QStringLiteral("application/x-vnd.akonadi.calendar.todo")); item.setPayload(todo); item.addAttribute(new Akonadi::EntityDisplayAttribute); // WHEN auto storage = createStorage(); auto job = storage->updateItem(item, nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item.id()); QCOMPARE(*notifiedItem.payload(), *todo); QVERIFY(notifiedItem.hasAttribute()); auto parent = notifiedItem.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldReadDefaultCollectionFromSettings() { // GIVEN // A storage implementation auto storage = createStorage(); // WHEN Akonadi::StorageSettings::instance().setDefaultCollection(Akonadi::Collection(24)); // THEN QCOMPARE(storage->defaultCollection(), Akonadi::Collection(24)); } void AkonadiStorageTestBase::shouldUpdateItem() { // GIVEN // A storage implementation auto storage = createStorage(); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemChanged); MonitorSpy monitorSpy(monitor.data()); // A todo... KCalCore::Todo::Ptr todo(new KCalCore::Todo); todo->setSummary(QStringLiteral("new summary")); todo->setDescription(QStringLiteral("new content")); // ... as payload of an existing item (if we trust the test data)... Akonadi::Item item = fetchItemByRID(QStringLiteral("{1d33862f-f274-4c67-ab6c-362d56521ff4}"), calendar2()); item.setMimeType(QStringLiteral("application/x-vnd.akonadi.calendar.todo")); item.setPayload(todo); // WHEN auto job = storage->updateItem(item, nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item.id()); QCOMPARE(*notifiedItem.payload(), *todo); } void AkonadiStorageTestBase::shouldUseTransaction() { // GIVEN auto storage = createStorage(); Akonadi::Item item1 = fetchItemByRID(QStringLiteral("{0aa4dc30-a2c2-4e08-8241-033b3344debc}"), calendar1()); QVERIFY(item1.isValid()); Akonadi::Item item2 = fetchItemByRID(QStringLiteral("{5dc1aba7-eead-4254-ba7a-58e397de1179}"), calendar1()); QVERIFY(item2.isValid()); // create wrong item Akonadi::Item item3(10000); item3.setRemoteId(QStringLiteral("wrongId")); // A spied monitor auto monitor = createMonitor(); QSignalSpy spyUpdated(monitor.data(), &Akonadi::MonitorInterface::itemChanged); MonitorSpy monitorSpy(monitor.data()); // WHEN auto todo = item1.payload(); todo->setSummary(QStringLiteral("Buy tomatoes")); todo = item2.payload(); todo->setSummary(QStringLiteral("Buy chocolate")); auto transaction = storage->createTransaction(); storage->updateItem(item1, transaction); storage->updateItem(item3, transaction); // this job should fail storage->updateItem(item2, transaction); QVERIFY(!transaction->exec()); monitorSpy.waitForStableState(); // THEN QCOMPARE(spyUpdated.size(), 0); auto job = storage->fetchItem(item1, nullptr); AKVERIFYEXEC(job->kjob()); QCOMPARE(job->items().size(), 1); item1 = job->items().at(0); job = storage->fetchItem(item2, nullptr); AKVERIFYEXEC(job->kjob()); QCOMPARE(job->items().size(), 1); item2 = job->items().at(0); QCOMPARE(item1.payload()->summary(), QStringLiteral("Buy kiwis")); QCOMPARE(item2.payload()->summary(), QStringLiteral("Buy cheese")); } void AkonadiStorageTestBase::shouldCreateItem() { // GIVEN // A storage implementation auto storage = createStorage(); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemAdded); MonitorSpy monitorSpy(monitor.data()); // A todo... KCalCore::Todo::Ptr todo(new KCalCore::Todo); todo->setSummary(QStringLiteral("summary")); todo->setDescription(QStringLiteral("content")); todo->setCompleted(false); todo->setDtStart(QDateTime(QDate(2013, 11, 24))); todo->setDtDue(QDateTime(QDate(2014, 03, 01))); // ... as payload of a new item Akonadi::Item item; item.setMimeType(QStringLiteral("application/x-vnd.akonadi.calendar.todo")); item.setPayload(todo); // WHEN auto job = storage->createItem(item, calendar2()); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.parentCollection(), calendar2()); QCOMPARE(*notifiedItem.payload(), *todo); } void AkonadiStorageTestBase::shouldRetrieveItem() { // GIVEN auto storage = createStorage(); Akonadi::Item findItem = fetchItemByRID(QStringLiteral("{7824df00-2fd6-47a4-8319-52659dc82005}"), calendar2()); QVERIFY(findItem.isValid()); // WHEN auto job = storage->fetchItem(findItem, nullptr); AKVERIFYEXEC(job->kjob()); // THEN auto items = job->items(); QCOMPARE(items.size(), 1); const auto &item = items[0]; QCOMPARE(item.id(), findItem.id()); QVERIFY(item.loadedPayloadParts().contains(Akonadi::Item::FullPayload)); QVERIFY(!item.attributes().isEmpty()); QVERIFY(item.modificationTime().isValid()); QVERIFY(!item.flags().isEmpty()); auto parent = item.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldMoveItem() { // GIVEN auto storage = createStorage(); Akonadi::Item item = fetchItemByRID(QStringLiteral("{7824df00-2fd6-47a4-8319-52659dc82005}"), calendar2()); QVERIFY(item.isValid()); // A spied monitor auto monitor = createMonitor(); QSignalSpy spyMoved(monitor.data(), &Akonadi::MonitorInterface::itemMoved); MonitorSpy monitorSpy(monitor.data()); auto job = storage->moveItem(item, calendar1(), nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spyMoved.isEmpty()); QCOMPARE(spyMoved.size(), 1); auto movedItem = spyMoved.takeFirst().at(0).value(); QCOMPARE(movedItem.id(), item.id()); } void AkonadiStorageTestBase::shouldMoveItems() { // GIVEN auto storage = createStorage(); Akonadi::Item item = fetchItemByRID(QStringLiteral("{1d33862f-f274-4c67-ab6c-362d56521ff4}"), calendar2()); QVERIFY(item.isValid()); Akonadi::Item::List list; list << item; // A spied monitor auto monitor = createMonitor(); QSignalSpy spyMoved(monitor.data(), &Akonadi::MonitorInterface::itemMoved); MonitorSpy monitorSpy(monitor.data()); auto job = storage->moveItems(list, calendar1(), nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spyMoved.isEmpty()); QCOMPARE(spyMoved.size(), 1); auto movedItem = spyMoved.takeFirst().at(0).value(); QCOMPARE(movedItem.id(), item.id()); } void AkonadiStorageTestBase::shouldDeleteItem() { //GIVEN auto storage = createStorage(); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemRemoved); MonitorSpy monitorSpy(monitor.data()); // An existing item (if we trust the test data) Akonadi::Item item = fetchItemByRID(QStringLiteral("{0aa4dc30-a2c2-4e08-8241-033b3344debc}"), calendar1()); QVERIFY(item.isValid()); //When - auto job = storage->removeItem(item); + auto job = storage->removeItem(item, nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item.id()); } void AkonadiStorageTestBase::shouldDeleteItems() { //GIVEN auto storage = createStorage(); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemRemoved); MonitorSpy monitorSpy(monitor.data()); // An existing item (if we trust the test data) Akonadi::Item item = fetchItemByRID(QStringLiteral("{6c7bf5b9-4136-4203-9f45-54e32ea0eacb}"), calendar1()); QVERIFY(item.isValid()); Akonadi::Item item2 = fetchItemByRID(QStringLiteral("{83cf0b15-8d61-436b-97ae-4bd88fb2fef9}"), calendar1()); QVERIFY(item2.isValid()); Akonadi::Item::List list; list << item << item2; //When auto job = storage->removeItems(list, nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 2); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item.id()); notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item2.id()); } void AkonadiStorageTestBase::shouldUpdateCollection() { // GIVEN // A storage implementation auto storage = createStorage(); // An existing collection Akonadi::Collection collection = calendar2(); // A spied monitor auto monitor = createMonitor(); QSignalSpy changeSpy(monitor.data(), &Akonadi::MonitorInterface::collectionChanged); QSignalSpy selectionSpy(monitor.data(), &Akonadi::MonitorInterface::collectionSelectionChanged); MonitorSpy monitorSpy(monitor.data()); // WHEN auto attr = new Akonadi::EntityDisplayAttribute; attr->setDisplayName(QStringLiteral("Foo")); collection.addAttribute(attr); auto job = storage->updateCollection(collection, nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!changeSpy.isEmpty()); // THEN QCOMPARE(changeSpy.size(), 1); QCOMPARE(selectionSpy.size(), 0); auto notifiedCollection = changeSpy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); QVERIFY(notifiedCollection.hasAttribute()); QCOMPARE(notifiedCollection.attribute()->displayName(), attr->displayName()); } void AkonadiStorageTestBase::shouldNotifyCollectionTimestampChanges() { // GIVEN // A storage implementation auto storage = createStorage(); // An existing collection Akonadi::Collection collection = calendar2(); // A spied monitor auto monitor = createMonitor(); QSignalSpy changeSpy(monitor.data(), &Akonadi::MonitorInterface::collectionChanged); MonitorSpy monitorSpy(monitor.data()); // WHEN collection.attribute(Akonadi::Collection::AddIfMissing)->refreshTimestamp(); auto job = storage->updateCollection(collection, nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!changeSpy.isEmpty()); // THEN QCOMPARE(changeSpy.size(), 1); auto notifiedCollection = changeSpy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); QVERIFY(notifiedCollection.hasAttribute()); } void AkonadiStorageTestBase::shouldNotifyCollectionSelectionChanges() { // GIVEN // A storage implementation auto storage = createStorage(); // An existing collection Akonadi::Collection collection = calendar2(); // A spied monitor auto monitor = createMonitor(); QSignalSpy changeSpy(monitor.data(), &Akonadi::MonitorInterface::collectionChanged); QSignalSpy selectionSpy(monitor.data(), &Akonadi::MonitorInterface::collectionSelectionChanged); MonitorSpy monitorSpy(monitor.data()); // WHEN auto attr = new Akonadi::ApplicationSelectedAttribute; attr->setSelected(false); collection.addAttribute(attr); auto job = storage->updateCollection(collection, nullptr); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!changeSpy.isEmpty()); // THEN QCOMPARE(changeSpy.size(), 1); QCOMPARE(selectionSpy.size(), 1); auto notifiedCollection = changeSpy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); QVERIFY(notifiedCollection.hasAttribute()); QVERIFY(!notifiedCollection.attribute()->isSelected()); notifiedCollection = selectionSpy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); QVERIFY(notifiedCollection.hasAttribute()); QVERIFY(!notifiedCollection.attribute()->isSelected()); } Akonadi::Item AkonadiStorageTestBase::fetchItemByRID(const QString &remoteId, const Akonadi::Collection &collection) { Akonadi::Item item; item.setRemoteId(remoteId); auto job = createStorage()->fetchItem(item, nullptr); job->setCollection(collection); if (!job->kjob()->exec()) { qWarning() << job->kjob()->errorString(); return Akonadi::Item(); } if (job->items().count() != 1) { qWarning() << "Received unexpected amount of items: " << job->items().count(); return Akonadi::Item(); } return job->items().at(0); } Akonadi::Collection AkonadiStorageTestBase::fetchCollectionByRID(const QString &remoteId) { Akonadi::Collection collection; collection.setRemoteId(remoteId); auto job = createStorage()->fetchCollections(collection, Akonadi::StorageInterface::Base); job->setResource(QStringLiteral("akonadi_knut_resource_0")); if (!job->kjob()->exec()) { qWarning() << job->kjob()->errorString() << remoteId; return Akonadi::Collection(); } if (job->collections().count() != 1) { qWarning() << "Received unexpected amount of collections: " << job->collections().count(); return Akonadi::Collection(); } return job->collections().at(0); } Akonadi::Collection AkonadiStorageTestBase::calendar1() { return fetchCollectionByRID(QStringLiteral("{cdc229c7-a9b5-4d37-989d-a28e372be2a9}")); } Akonadi::Collection AkonadiStorageTestBase::calendar2() { return fetchCollectionByRID(QStringLiteral("{e682b8b5-b67c-4538-8689-6166f64177f0}")); } Akonadi::Collection AkonadiStorageTestBase::emails() { return fetchCollectionByRID(QStringLiteral("{14096930-7bfe-46ca-8fba-7c04d3b62ec8}")); } diff --git a/tests/units/akonadi/akonadicontextrepositorytest.cpp b/tests/units/akonadi/akonadicontextrepositorytest.cpp index c8f9b678..24c6ccdb 100644 --- a/tests/units/akonadi/akonadicontextrepositorytest.cpp +++ b/tests/units/akonadi/akonadicontextrepositorytest.cpp @@ -1,338 +1,339 @@ /* This file is part of Zanshin Copyright 2014 Franck Arrecot Copyright 2014 Rémi Benoit This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "utils/mockobject.h" #include "testlib/akonadifakejobs.h" #include "testlib/akonadifakemonitor.h" #include "akonadi/akonadicontextrepository.h" #include "akonadi/akonadiserializerinterface.h" #include "akonadi/akonadistorageinterface.h" using namespace mockitopp; using namespace mockitopp::matcher; Q_DECLARE_METATYPE(Testlib::AkonadiFakeItemFetchJob*) class AkonadiContextRepositoryTest : public QObject { Q_OBJECT private slots: void shouldCreateContext() { // GIVEN auto source = Domain::DataSource::Ptr::create(); source->setName(QStringLiteral("Source1")); // A Context and its corresponding item not existing in akonadi Akonadi::Item contextItem; Akonadi::Collection collection(23); auto context = Domain::Context::Ptr::create(); // A mock creating job auto itemCreateJob = new FakeJob(this); // Storage mock returning the context-item creation job Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::createItem).when(contextItem, collection) .thenReturn(itemCreateJob); // Serializer mock Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromContext).when(context).thenReturn(contextItem); serializerMock(&Akonadi::SerializerInterface::createCollectionFromDataSource).when(source).thenReturn(collection); // WHEN QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->create(context, source)->exec(); //THEN QVERIFY(storageMock(&Akonadi::StorageInterface::createItem).when(contextItem, collection).exactly(1)); } void shouldUpdateContext() { // GIVEN Akonadi::Item contextItem; contextItem.setId(42); auto context = Domain::Context::Ptr::create(); // A mock job auto itemModifyJob = new FakeJob(this); Utils::MockObject storageMock; Utils::MockObject serializerMock; QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), serializerMock.getInstance())); // Storage mock returning the item modify job storageMock(&Akonadi::StorageInterface::updateItem).when(contextItem, repository.get()) .thenReturn(itemModifyJob); // Serializer mock serializerMock(&Akonadi::SerializerInterface::createItemFromContext).when(context).thenReturn(contextItem); // WHEN repository->update(context)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromContext).when(context).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(contextItem, repository.get()).exactly(1)); } void shouldRemoveContext() { // GIVEN Akonadi::Item contextItem; contextItem.setId(42); auto context = Domain::Context::Ptr::create(); // A mock job auto contextItemDeleteJob = new FakeJob(this); - // Storage mock returning the mock job Utils::MockObject storageMock; - storageMock(&Akonadi::StorageInterface::removeItem).when(contextItem) + Utils::MockObject serializerMock; + QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), + serializerMock.getInstance())); + + // Storage mock returning the mock job + storageMock(&Akonadi::StorageInterface::removeItem).when(contextItem, repository.get()) .thenReturn(contextItemDeleteJob); // Serializer mock - Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromContext).when(context).thenReturn(contextItem); // WHEN - QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), - serializerMock.getInstance())); repository->remove(context)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromContext).when(context).exactly(1)); - QVERIFY(storageMock(&Akonadi::StorageInterface::removeItem).when(contextItem).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::removeItem).when(contextItem, repository.get()).exactly(1)); } void shouldAssociateATaskToAContext_data() { QTest::addColumn("associatedContextItem"); QTest::addColumn("item"); QTest::addColumn("context"); QTest::addColumn("task"); QTest::addColumn("itemFetchJob"); QTest::addColumn("execJob"); Akonadi::Collection col(40); Akonadi::Item item(42); item.setParentCollection(col); Domain::Task::Ptr task(new Domain::Task); Akonadi::Item associatedContextItem(43); auto associatedContext = Domain::Context::Ptr::create(); auto itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setItems(Akonadi::Item::List() << item); QTest::newRow("nominal case") << associatedContextItem << item << associatedContext << task << itemFetchJob << true; itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setExpectedError(KJob::KilledJobError); QTest::newRow("task job error, cannot find task") << associatedContextItem << item << associatedContext << task << itemFetchJob << false; } void shouldAssociateATaskToAContext() { // GIVEN QFETCH(Akonadi::Item,associatedContextItem); QFETCH(Akonadi::Item,item); QFETCH(Domain::Context::Ptr,context); QFETCH(Domain::Task::Ptr,task); QFETCH(Testlib::AkonadiFakeItemFetchJob*,itemFetchJob); QFETCH(bool,execJob); // A mock update job auto itemModifyJob = new FakeJob(this); Utils::MockObject storageMock; Utils::MockObject serializerMock; QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), serializerMock.getInstance())); // Storage mock returning the create job storageMock(&Akonadi::StorageInterface::fetchItem).when(item, repository.get()) .thenReturn(itemFetchJob); storageMock(&Akonadi::StorageInterface::updateItem).when(item, repository.get()) .thenReturn(itemModifyJob); // Serializer mock returning the item for the task serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task) .thenReturn(item); serializerMock(&Akonadi::SerializerInterface::addContextToTask).when(context, item) .thenReturn(); // WHEN auto associateJob = repository->associate(context, task); if (execJob) associateJob->exec(); // THEN QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(item, repository.get()).exactly(1)); if (execJob) { QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).exactly(1)); QVERIFY(serializerMock(&Akonadi::SerializerInterface::addContextToTask).when(context, item).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, repository.get()).exactly(1)); } } void shoudDissociateTaskFromContext_data() { QTest::addColumn("item"); QTest::addColumn("context"); QTest::addColumn("task"); QTest::addColumn("itemFetchJob"); QTest::addColumn("execJob"); Akonadi::Item item(42); Domain::Task::Ptr task(new Domain::Task); auto associatedContext = Domain::Context::Ptr::create(); auto itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setItems(Akonadi::Item::List() << item); QTest::newRow("nominal case") << item << associatedContext << task << itemFetchJob << true; itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setExpectedError(KJob::KilledJobError); QTest::newRow("task job error, cannot find task") << item << associatedContext << task << itemFetchJob << false; } void shoudDissociateTaskFromContext() { QFETCH(Akonadi::Item,item); QFETCH(Domain::Context::Ptr,context); QFETCH(Domain::Task::Ptr,task); QFETCH(Testlib::AkonadiFakeItemFetchJob*,itemFetchJob); QFETCH(bool,execJob); // A mock update job auto itemModifyJob = new FakeJob(this); Utils::MockObject storageMock; Utils::MockObject serializerMock; QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), serializerMock.getInstance())); // Storage mock returning the create job storageMock(&Akonadi::StorageInterface::fetchItem).when(item, repository.get()) .thenReturn(itemFetchJob); storageMock(&Akonadi::StorageInterface::updateItem).when(item, repository.get()) .thenReturn(itemModifyJob); // Serializer mock returning the item for the task serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task) .thenReturn(item); serializerMock(&Akonadi::SerializerInterface::removeContextFromTask).when(context, item) .thenReturn(); // WHEN auto dissociateJob = repository->dissociate(context, task); if (execJob) dissociateJob->exec(); // THEN QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(item, repository.get()).exactly(1)); if (execJob) { QVERIFY(serializerMock(&Akonadi::SerializerInterface::removeContextFromTask).when(context, item).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, repository.get()).exactly(1)); } } void shouldDissociateTaskFromAllContext_data() { QTest::addColumn("item"); QTest::addColumn("task"); QTest::addColumn("itemFetchJob"); QTest::addColumn("execJob"); Akonadi::Item item(42); Domain::Task::Ptr task(new Domain::Task); auto itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setItems(Akonadi::Item::List() << item); QTest::newRow("nominal case") << item << task << itemFetchJob << true; itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setExpectedError(KJob::KilledJobError); QTest::newRow("task job error, cannot find task") << item << task << itemFetchJob << false; } void shouldDissociateTaskFromAllContext() { QFETCH(Akonadi::Item,item); QFETCH(Domain::Task::Ptr,task); QFETCH(Testlib::AkonadiFakeItemFetchJob*,itemFetchJob); QFETCH(bool,execJob); // A mock update job auto itemModifyJob = new FakeJob(this); Utils::MockObject storageMock; Utils::MockObject serializerMock; QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), serializerMock.getInstance())); // Storage mock returning the create job storageMock(&Akonadi::StorageInterface::fetchItem).when(item, repository.get()) .thenReturn(itemFetchJob); storageMock(&Akonadi::StorageInterface::updateItem).when(item, repository.get()) .thenReturn(itemModifyJob); // Serializer mock returning the item for the task serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task) .thenReturn(item); // WHEN auto dissociateJob = repository->dissociateAll(task); if (execJob) dissociateJob->exec(); // THEN QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(item, repository.get()).exactly(1)); if (execJob) { QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, repository.get()).exactly(1)); } else { delete dissociateJob; } // Give a chance to itemFetchJob to delete itself // in case of an error (since it uses deleteLater() internally) QTest::qWait(10); } }; ZANSHIN_TEST_MAIN(AkonadiContextRepositoryTest) #include "akonadicontextrepositorytest.moc" diff --git a/tests/units/akonadi/akonadiprojectrepositorytest.cpp b/tests/units/akonadi/akonadiprojectrepositorytest.cpp index 53af324d..4129edf5 100644 --- a/tests/units/akonadi/akonadiprojectrepositorytest.cpp +++ b/tests/units/akonadi/akonadiprojectrepositorytest.cpp @@ -1,359 +1,360 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "utils/mockobject.h" #include "testlib/akonadifakejobs.h" #include "testlib/akonadifakemonitor.h" #include "akonadi/akonadiprojectrepository.h" #include "akonadi/akonadiserializerinterface.h" #include "akonadi/akonadistorageinterface.h" using namespace mockitopp; Q_DECLARE_METATYPE(Testlib::AkonadiFakeItemFetchJob*) class AkonadiProjectRepositoryTest : public QObject { Q_OBJECT private slots: void shouldCreateProjectInDataSource() { // GIVEN // A project and its corresponding item already not existing in storage Akonadi::Item item; auto project = Domain::Project::Ptr::create(); // A data source and its corresponding collection existing in storage Akonadi::Collection collection(42); auto source = Domain::DataSource::Ptr::create(); // A mock create job auto itemCreateJob = new FakeJob(this); // Storage mock returning the create job Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::createItem).when(item, collection) .thenReturn(itemCreateJob); // Serializer mock Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(project).thenReturn(item); serializerMock(&Akonadi::SerializerInterface::createCollectionFromDataSource).when(source).thenReturn(collection); // WHEN QScopedPointer repository(new Akonadi::ProjectRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->create(project, source)->exec(); // THEN QVERIFY(storageMock(&Akonadi::StorageInterface::createItem).when(item, collection).exactly(1)); } void shouldUpdateExistingProject() { // GIVEN // A project and its corresponding item already existing in storage Akonadi::Item item(42); Domain::Project::Ptr project(new Domain::Project); // A mock modify job auto itemModifyJob = new FakeJob(this); Utils::MockObject storageMock; Utils::MockObject serializerMock; QScopedPointer repository(new Akonadi::ProjectRepository(storageMock.getInstance(), serializerMock.getInstance())); // Storage mock returning the create job storageMock(&Akonadi::StorageInterface::updateItem).when(item, repository.get()) .thenReturn(itemModifyJob); // Serializer mock returning the item for the project serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(project).thenReturn(item); // WHEN repository->update(project)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(project).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, repository.get()).exactly(1)); } void shouldRemoveExistingProject() { // GIVEN // A project and its corresponding item already existing in storage Akonadi::Item item(42); auto project = Domain::Project::Ptr::create(); // A mock remove job auto itemRemoveJob = new FakeJob(this); - // Storage mock returning the create job Utils::MockObject storageMock; - storageMock(&Akonadi::StorageInterface::removeItem).when(item) + Utils::MockObject serializerMock; + QScopedPointer repository(new Akonadi::ProjectRepository(storageMock.getInstance(), + serializerMock.getInstance())); + + // Storage mock returning the create job + storageMock(&Akonadi::StorageInterface::removeItem).when(item, repository.get()) .thenReturn(itemRemoveJob); // Serializer mock returning the item for the project - Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(project).thenReturn(item); // WHEN - QScopedPointer repository(new Akonadi::ProjectRepository(storageMock.getInstance(), - serializerMock.getInstance())); repository->remove(project)->exec(); // THEN - QVERIFY(storageMock(&Akonadi::StorageInterface::removeItem).when(item).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::removeItem).when(item, repository.get()).exactly(1)); } void shouldAssociateATaskToAProject_data() { QTest::addColumn("childItem"); QTest::addColumn("parentItem"); QTest::addColumn("child"); QTest::addColumn("parent"); QTest::addColumn("itemFetchJob1"); QTest::addColumn("itemFetchJob2"); QTest::addColumn("itemFetchJob3"); QTest::addColumn("execJob"); QTest::addColumn("execParentJob"); QTest::addColumn("list"); Akonadi::Collection col(40); Akonadi::Item childItem(42); childItem.setParentCollection(col); Domain::Task::Ptr childTask(new Domain::Task); Akonadi::Item parentItem(41); parentItem.setParentCollection(col); auto parent = Domain::Project::Ptr::create(); auto itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setItems(Akonadi::Item::List() << childItem); auto itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob2->setItems(Akonadi::Item::List() << parentItem); auto itemFetchJob3 = new Testlib::AkonadiFakeItemFetchJob(this); Akonadi::Item::List list; QTest::newRow("nominal case (task)") << childItem << parentItem << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << true << true << list; itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setExpectedError(KJob::KilledJobError); QTest::newRow("child job error with empty list") << childItem << parentItem << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << false << false << list; itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setExpectedError(KJob::KilledJobError); itemFetchJob1->setItems(Akonadi::Item::List() << childItem); QTest::newRow("child job error with item (task)") << childItem << parentItem << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << false << false << list; itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setItems(Akonadi::Item::List() << childItem); itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob2->setExpectedError(KJob::KilledJobError); QTest::newRow("parent job error with empty list (task)") << childItem << parentItem << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << true << false << list; itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setItems(Akonadi::Item::List() << childItem); itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob2->setExpectedError(KJob::KilledJobError); itemFetchJob2->setItems(Akonadi::Item::List() << parentItem); QTest::newRow("parent job error with item (task)") << childItem << parentItem << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << true << false << list; itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setItems(Akonadi::Item::List() << childItem); itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this); Akonadi::Collection col2(39); Akonadi::Item parentItem2(41); parentItem2.setParentCollection(col2); itemFetchJob2->setItems(Akonadi::Item::List() << parentItem2); itemFetchJob3 = new Testlib::AkonadiFakeItemFetchJob(this); QTest::newRow("update and move item (task)") << childItem << parentItem2 << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << true << true << list; itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setItems(Akonadi::Item::List() << childItem); itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob2->setItems(Akonadi::Item::List() << parentItem2); itemFetchJob3 = new Testlib::AkonadiFakeItemFetchJob(this); Akonadi::Item childItem2(43); Akonadi::Item::List list2; list2 << childItem2; itemFetchJob3->setItems(list2); QTest::newRow("update and move item and his child (task)") << childItem << parentItem2 << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << true << true << list2; } void shouldAssociateATaskToAProject() { // GIVEN QFETCH(Akonadi::Item, childItem); QFETCH(Akonadi::Item, parentItem); QFETCH(Domain::Task::Ptr, child); QFETCH(Domain::Project::Ptr, parent); QFETCH(Testlib::AkonadiFakeItemFetchJob*, itemFetchJob1); QFETCH(Testlib::AkonadiFakeItemFetchJob*, itemFetchJob2); QFETCH(Testlib::AkonadiFakeItemFetchJob*, itemFetchJob3); QFETCH(bool, execJob); QFETCH(bool, execParentJob); QFETCH(Akonadi::Item::List, list); // A mock create job auto itemModifyJob = new FakeJob(this); auto transactionJob = new FakeJob(this); auto itemsMoveJob = new FakeJob(this); Akonadi::Item::List movedList; movedList << childItem << list; Utils::MockObject storageMock; Utils::MockObject serializerMock; QScopedPointer repository(new Akonadi::ProjectRepository(storageMock.getInstance(), serializerMock.getInstance())); // Storage mock returning the create job storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem, repository.get()) .thenReturn(itemFetchJob1); storageMock(&Akonadi::StorageInterface::fetchItem).when(parentItem, repository.get()) .thenReturn(itemFetchJob2); if (parentItem.parentCollection().id() != childItem.parentCollection().id()) { storageMock(&Akonadi::StorageInterface::fetchItems).when(childItem.parentCollection(), repository.get()) .thenReturn(itemFetchJob3); storageMock(&Akonadi::StorageInterface::createTransaction).when().thenReturn(transactionJob); storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, transactionJob) .thenReturn(itemModifyJob); storageMock(&Akonadi::StorageInterface::moveItems).when(movedList, parentItem.parentCollection(), transactionJob) .thenReturn(itemsMoveJob); } else { storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, repository.get()) .thenReturn(itemModifyJob); } // Serializer mock returning the item for the task serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).thenReturn(childItem); serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(parent).thenReturn(parentItem); serializerMock(&Akonadi::SerializerInterface::updateItemProject).when(childItem, parent).thenReturn(); if (execParentJob) serializerMock(&Akonadi::SerializerInterface::filterDescendantItems).when(list, childItem).thenReturn(list); // WHEN auto associateJob = repository->associate(parent, child); if (execJob) associateJob->exec(); // THEN QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem, repository.get()).exactly(1)); if (execJob) { QVERIFY(serializerMock(&Akonadi::SerializerInterface::updateItemProject).when(childItem, parent).exactly(1)); QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(parent).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(parentItem, repository.get()).exactly(1)); if (execParentJob) { if (parentItem.parentCollection().id() != childItem.parentCollection().id()) { QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItems).when(childItem.parentCollection(), repository.get()).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::createTransaction).when().thenReturn(transactionJob).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, transactionJob).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::moveItems).when(movedList, parentItem.parentCollection(), transactionJob).exactly(1)); } else { QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, repository.get()).exactly(1)); } } } } void shouldDissociateATaskFromItsProject_data() { QTest::addColumn("child"); QTest::addColumn("childItem"); QTest::addColumn("itemFetchJob"); QTest::addColumn("fetchJobFailed"); Domain::Task::Ptr taskChild(new Domain::Task); Akonadi::Item childItem(42); auto itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setItems(Akonadi::Item::List() << childItem); QTest::newRow("task nominal case") << taskChild << childItem << itemFetchJob << false; itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setExpectedError(KJob::KilledJobError); QTest::newRow("task job error with empty list") << taskChild << childItem << itemFetchJob << true; itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setExpectedError(KJob::KilledJobError); itemFetchJob->setItems(Akonadi::Item::List() << childItem); QTest::newRow("task job error with item") << taskChild << childItem << itemFetchJob << true; } void shouldDissociateATaskFromItsProject() { // GIVEN QFETCH(Domain::Task::Ptr, child); QFETCH(Akonadi::Item, childItem); QFETCH(Testlib::AkonadiFakeItemFetchJob*, itemFetchJob); QFETCH(bool, fetchJobFailed); auto itemModifyJob = new FakeJob(this); Utils::MockObject storageMock; Utils::MockObject serializerMock; QScopedPointer repository(new Akonadi::ProjectRepository(storageMock.getInstance(), serializerMock.getInstance())); // Storage mock returning the delete job storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, repository.get()) .thenReturn(itemModifyJob); storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem, repository.get()) .thenReturn(itemFetchJob); // Serializer mock returning the item for the task serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).thenReturn(childItem); serializerMock(&Akonadi::SerializerInterface::removeItemParent).when(childItem).thenReturn(); // WHEN repository->dissociate(child)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem, repository.get()).exactly(1)); if (!fetchJobFailed) { QVERIFY(serializerMock(&Akonadi::SerializerInterface::removeItemParent).when(childItem).exactly(1));; QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, repository.get()).exactly(1)); } // Give a chance to job to delete themselves // in case of an error (since they use deleteLater() internally) QTest::qWait(10); } }; ZANSHIN_TEST_MAIN(AkonadiProjectRepositoryTest) #include "akonadiprojectrepositorytest.moc"