diff --git a/src/akonadi/akonadicachingstorage.h b/src/akonadi/akonadicachingstorage.h index fcb5f095..85de4673 100644 --- a/src/akonadi/akonadicachingstorage.h +++ b/src/akonadi/akonadicachingstorage.h @@ -1,71 +1,71 @@ /* 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 = Q_NULLPTR) override; + KJob *updateItem(Item item, QObject *parent = nullptr) override; KJob *removeItem(Akonadi::Item item) override; - KJob *removeItems(Item::List items, QObject *parent = Q_NULLPTR) override; - KJob *moveItem(Item item, Collection collection, QObject *parent = Q_NULLPTR) override; - KJob *moveItems(Item::List item, Collection collection, QObject *parent = Q_NULLPTR) override; + KJob *removeItems(Item::List items, QObject *parent = nullptr) override; + KJob *moveItem(Item item, Collection collection, QObject *parent = nullptr) override; + KJob *moveItems(Item::List item, Collection collection, QObject *parent = nullptr) override; - KJob *createCollection(Collection collection, QObject *parent = Q_NULLPTR) override; - KJob *updateCollection(Collection collection, QObject *parent = Q_NULLPTR) override; - KJob *removeCollection(Collection collection, QObject *parent = Q_NULLPTR) override; + KJob *createCollection(Collection collection, QObject *parent = nullptr) override; + KJob *updateCollection(Collection collection, QObject *parent = nullptr) override; + KJob *removeCollection(Collection collection, QObject *parent = nullptr) override; KJob *createTransaction() override; KJob *createTag(Akonadi::Tag tag) override; KJob *updateTag(Akonadi::Tag tag) override; KJob *removeTag(Akonadi::Tag tag) override; CollectionFetchJobInterface *fetchCollections(Akonadi::Collection collection, FetchDepth depth) override; ItemFetchJobInterface *fetchItems(Akonadi::Collection collection) override; ItemFetchJobInterface *fetchItem(Akonadi::Item item) override; ItemFetchJobInterface *fetchTagItems(Akonadi::Tag tag) override; TagFetchJobInterface *fetchTags() override; private: Cache::Ptr m_cache; StorageInterface::Ptr m_storage; }; } #endif // AKONADI_CACHING_STORAGE_H diff --git a/src/akonadi/akonadilivequeryintegrator.h b/src/akonadi/akonadilivequeryintegrator.h index 77f8a9e7..fb8ac611 100644 --- a/src/akonadi/akonadilivequeryintegrator.h +++ b/src/akonadi/akonadilivequeryintegrator.h @@ -1,351 +1,351 @@ /* 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 AKONADI_LIVEQUERYINTEGRATOR_H #define AKONADI_LIVEQUERYINTEGRATOR_H #include #include #include #include #include #include #include "akonadi/akonadimonitorinterface.h" #include "akonadi/akonadiserializerinterface.h" #include "domain/livequery.h" namespace Akonadi { class LiveQueryIntegrator : public QObject { Q_OBJECT // Helper type trait to extract parameter and return types from // a function object // Lambda or functors (via method of const method) template struct UnaryFunctionTraits : public UnaryFunctionTraits {}; // Traits definition template struct UnaryFunctionTraits { typedef Return ReturnType; typedef Arg ArgType; }; // Function pointers template struct UnaryFunctionTraits : public UnaryFunctionTraits {}; // Method template struct UnaryFunctionTraits : public UnaryFunctionTraits {}; // Const method template struct UnaryFunctionTraits : public UnaryFunctionTraits {}; // std::function object template struct UnaryFunctionTraits> : public UnaryFunctionTraits {}; // const reference to std::function object template struct UnaryFunctionTraits &> : public UnaryFunctionTraits {}; public: typedef QSharedPointer Ptr; typedef std::function CollectionRemoveHandler; typedef std::function ItemRemoveHandler; typedef std::function TagRemoveHandler; LiveQueryIntegrator(const SerializerInterface::Ptr &serializer, const MonitorInterface::Ptr &monitor, - QObject *parent = Q_NULLPTR); + QObject *parent = nullptr); template void bind(const QByteArray &debugName, QSharedPointer> &output, FetchFunction fetch, PredicateFunction predicate, ExtraArgs... extra) { typedef UnaryFunctionTraits FetchTraits; typedef UnaryFunctionTraits AddTraits; typedef UnaryFunctionTraits PredicateTraits; typedef typename std::decay::type InputType; // typically Akonadi::Item static_assert(std::is_same::value, "Fetch function must return void"); static_assert(std::is_same::value, "Fetch add function must return void"); static_assert(std::is_same::value, "Predicate function must return bool"); typedef typename std::decay::type AddInputType; static_assert(std::is_same::value, "Fetch add and predicate functions must have the same input type"); if (output) return; using namespace std::placeholders; auto query = Domain::LiveQuery::Ptr::create(); query->setDebugName(debugName); query->setFetchFunction(fetch); query->setPredicateFunction(predicate); query->setConvertFunction(std::bind(&LiveQueryIntegrator::create, this, _1, extra...)); query->setUpdateFunction(std::bind(&LiveQueryIntegrator::update, this, _1, _2, extra...)); query->setRepresentsFunction(std::bind(&LiveQueryIntegrator::represents, this, _1, _2)); inputQueries() << query; output = query; } template void bindRelationship(const QByteArray &debugName, QSharedPointer> &output, FetchFunction fetch, CompareFunction compare, PredicateFunction predicate, ExtraArgs... extra) { typedef UnaryFunctionTraits FetchTraits; typedef UnaryFunctionTraits AddTraits; typedef UnaryFunctionTraits PredicateTraits; typedef typename std::decay::type InputType; // typically Akonadi::Item static_assert(std::is_same::value, "Fetch function must return void"); static_assert(std::is_same::value, "Fetch add function must return void"); static_assert(std::is_same::value, "Predicate function must return bool"); typedef typename std::decay::type AddInputType; static_assert(std::is_same::value, "Fetch add and predicate functions must have the same input type"); if (output) return; using namespace std::placeholders; auto query = Domain::LiveRelationshipQuery::Ptr::create(); query->setDebugName(debugName); query->setFetchFunction(fetch); query->setCompareFunction(compare); query->setPredicateFunction(predicate); query->setConvertFunction(std::bind(&LiveQueryIntegrator::create, this, _1, extra...)); query->setRepresentsFunction(std::bind(&LiveQueryIntegrator::represents, this, _1, _2)); inputQueries() << query; output = query; } void addRemoveHandler(const CollectionRemoveHandler &handler); void addRemoveHandler(const ItemRemoveHandler &handler); void addRemoveHandler(const TagRemoveHandler &handler); private slots: void onCollectionSelectionChanged(); void onCollectionAdded(const Akonadi::Collection &collection); void onCollectionRemoved(const Akonadi::Collection &collection); void onCollectionChanged(const Akonadi::Collection &collection); void onItemAdded(const Akonadi::Item &item); void onItemRemoved(const Akonadi::Item &item); void onItemChanged(const Akonadi::Item &item); void onTagAdded(const Akonadi::Tag &tag); void onTagRemoved(const Akonadi::Tag &tag); void onTagChanged(const Akonadi::Tag &tag); private: void cleanupQueries(); template OutputType create(const InputType &input, ExtraArgs... extra); template void update(const InputType &input, OutputType &output, ExtraArgs... extra); template bool represents(const InputType &input, const OutputType &output); template typename Domain::LiveQueryInput::WeakList &inputQueries(); Domain::LiveQueryInput::WeakList m_collectionInputQueries; Domain::LiveQueryInput::WeakList m_itemInputQueries; Domain::LiveQueryInput::WeakList m_tagInputQueries; QList m_collectionRemoveHandlers; QList m_itemRemoveHandlers; QList m_tagRemoveHandlers; SerializerInterface::Ptr m_serializer; MonitorInterface::Ptr m_monitor; }; template<> inline Domain::Context::Ptr LiveQueryIntegrator::create(const Tag &input) { return m_serializer->createContextFromTag(input); } template<> inline void LiveQueryIntegrator::update(const Tag &input, Domain::Context::Ptr &output) { m_serializer->updateContextFromTag(output, input); } template<> inline bool LiveQueryIntegrator::represents(const Tag &input, const Domain::Context::Ptr &output) { return m_serializer->isContextTag(output, input); } template<> inline Domain::DataSource::Ptr LiveQueryIntegrator::create(const Collection &input) { return m_serializer->createDataSourceFromCollection(input, SerializerInterface::BaseName); } template<> inline void LiveQueryIntegrator::update(const Collection &input, Domain::DataSource::Ptr &output) { m_serializer->updateDataSourceFromCollection(output, input, SerializerInterface::BaseName); } template<> inline Domain::DataSource::Ptr LiveQueryIntegrator::create(const Collection &input, SerializerInterface::DataSourceNameScheme nameScheme) { return m_serializer->createDataSourceFromCollection(input, nameScheme); } template<> inline void LiveQueryIntegrator::update(const Collection &input, Domain::DataSource::Ptr &output, SerializerInterface::DataSourceNameScheme nameScheme) { m_serializer->updateDataSourceFromCollection(output, input, nameScheme); } template<> inline bool LiveQueryIntegrator::represents(const Collection &input, const Domain::DataSource::Ptr &output) { return m_serializer->representsCollection(output, input); } template<> inline Domain::DataSource::Ptr LiveQueryIntegrator::create(const Item &input) { return m_serializer->createDataSourceFromCollection(input.parentCollection(), SerializerInterface::BaseName); } template<> inline void LiveQueryIntegrator::update(const Item &input, Domain::DataSource::Ptr &output) { m_serializer->updateDataSourceFromCollection(output, input.parentCollection(), SerializerInterface::BaseName); } template<> inline bool LiveQueryIntegrator::represents(const Item &input, const Domain::DataSource::Ptr &output) { return m_serializer->representsCollection(output, input.parentCollection()); } template<> inline Domain::Project::Ptr LiveQueryIntegrator::create(const Item &input) { return m_serializer->createProjectFromItem(input); } template<> inline void LiveQueryIntegrator::update(const Item &input, Domain::Project::Ptr &output) { m_serializer->updateProjectFromItem(output, input); } template<> inline bool LiveQueryIntegrator::represents(const Item &input, const Domain::Project::Ptr &output) { return m_serializer->representsItem(output, input); } template<> inline Domain::Task::Ptr LiveQueryIntegrator::create(const Item &input) { return m_serializer->createTaskFromItem(input); } template<> inline void LiveQueryIntegrator::update(const Item &input, Domain::Task::Ptr &output) { m_serializer->updateTaskFromItem(output, input); } template<> inline bool LiveQueryIntegrator::represents(const Item &input, const Domain::Task::Ptr &output) { return m_serializer->representsItem(output, input); } template<> inline typename Domain::LiveQueryInput::WeakList &LiveQueryIntegrator::inputQueries() { return m_collectionInputQueries; } template<> inline typename Domain::LiveQueryInput::WeakList &LiveQueryIntegrator::inputQueries() { return m_itemInputQueries; } template<> inline typename Domain::LiveQueryInput::WeakList &LiveQueryIntegrator::inputQueries() { return m_tagInputQueries; } } #endif // AKONADI_LIVEQUERYINTEGRATOR_H diff --git a/src/akonadi/akonadimonitorinterface.h b/src/akonadi/akonadimonitorinterface.h index 226cdc03..11459969 100644 --- a/src/akonadi/akonadimonitorinterface.h +++ b/src/akonadi/akonadimonitorinterface.h @@ -1,63 +1,63 @@ /* 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_MONITORINTERFACE_H #define AKONADI_MONITORINTERFACE_H #include #include namespace Akonadi { class Collection; class Item; class Tag; class MonitorInterface : public QObject { Q_OBJECT public: typedef QSharedPointer Ptr; - explicit MonitorInterface(QObject *parent = Q_NULLPTR); + explicit MonitorInterface(QObject *parent = nullptr); virtual ~MonitorInterface(); signals: void collectionAdded(const Akonadi::Collection &collection); void collectionRemoved(const Akonadi::Collection &collection); void collectionChanged(const Akonadi::Collection &collection); void collectionSelectionChanged(const Akonadi::Collection &collection); void itemAdded(const Akonadi::Item &item); void itemRemoved(const Akonadi::Item &item); void itemChanged(const Akonadi::Item &items); void itemMoved(const Akonadi::Item &item); void tagAdded(const Akonadi::Tag &tag); void tagRemoved(const Akonadi::Tag &tag); void tagChanged(const Akonadi::Tag &tag); }; } #endif // AKONADI_MONITORINTERFACE_H diff --git a/src/akonadi/akonadistorage.cpp b/src/akonadi/akonadistorage.cpp index 7b29de02..5715f421 100644 --- a/src/akonadi/akonadistorage.cpp +++ b/src/akonadi/akonadistorage.cpp @@ -1,305 +1,305 @@ /* 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 #include #include #include #include #include "akonadi/akonadicollectionfetchjobinterface.h" #include "akonadi/akonadiitemfetchjobinterface.h" #include "akonadi/akonaditagfetchjobinterface.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 = Q_NULLPTR) + 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.intersect(allowedMimeTypes).isEmpty(); }), 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); } }; class TagJob : public TagFetchJob, public TagFetchJobInterface { Q_OBJECT public: using TagFetchJob::TagFetchJob; Tag::List tags() const override { return TagFetchJob::tags(); } }; 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) { return new ItemDeleteJob(item); } 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(); } KJob *Storage::createTag(Tag tag) { return new TagCreateJob(tag); } KJob *Storage::updateTag(Tag tag) { return new TagModifyJob(tag); } KJob *Storage::removeTag(Tag tag) { return new Akonadi::TagDeleteJob(tag); } 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) { auto job = new ItemJob(collection); configureItemFetchJob(job); return job; } ItemFetchJobInterface *Storage::fetchItem(Akonadi::Item item) { auto job = new ItemJob(item); configureItemFetchJob(job); return job; } ItemFetchJobInterface *Storage::fetchTagItems(Tag tag) { auto job = new ItemJob(tag); configureItemFetchJob(job); return job; } TagFetchJobInterface *Storage::fetchTags() { return new TagJob; } 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(true); scope.tagFetchScope().setFetchIdOnly(false); scope.setAncestorRetrieval(ItemFetchScope::All); job->setFetchScope(scope); } #include "akonadistorage.moc" diff --git a/src/akonadi/akonadistorage.h b/src/akonadi/akonadistorage.h index 6bf6e62d..4f8fe3ab 100644 --- a/src/akonadi/akonadistorage.h +++ b/src/akonadi/akonadistorage.h @@ -1,72 +1,72 @@ /* 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 = Q_NULLPTR) override; + KJob *updateItem(Item item, QObject *parent = nullptr) override; KJob *removeItem(Akonadi::Item item) override; - KJob *removeItems(Item::List items, QObject *parent = Q_NULLPTR) override; - KJob *moveItem(Item item, Collection collection, QObject *parent = Q_NULLPTR) override; - KJob *moveItems(Item::List item, Collection collection, QObject *parent = Q_NULLPTR) override; + KJob *removeItems(Item::List items, QObject *parent = nullptr) override; + KJob *moveItem(Item item, Collection collection, QObject *parent = nullptr) override; + KJob *moveItems(Item::List item, Collection collection, QObject *parent = nullptr) override; - KJob *createCollection(Collection collection, QObject *parent = Q_NULLPTR) override; - KJob *updateCollection(Collection collection, QObject *parent = Q_NULLPTR) override; - KJob *removeCollection(Collection collection, QObject *parent = Q_NULLPTR) override; + KJob *createCollection(Collection collection, QObject *parent = nullptr) override; + KJob *updateCollection(Collection collection, QObject *parent = nullptr) override; + KJob *removeCollection(Collection collection, QObject *parent = nullptr) override; KJob *createTransaction() override; KJob *createTag(Akonadi::Tag tag) override; KJob *updateTag(Akonadi::Tag tag) override; KJob *removeTag(Akonadi::Tag tag) override; CollectionFetchJobInterface *fetchCollections(Akonadi::Collection collection, FetchDepth depth) override; ItemFetchJobInterface *fetchItems(Akonadi::Collection collection) override; ItemFetchJobInterface *fetchItem(Akonadi::Item item) override; ItemFetchJobInterface *fetchTagItems(Akonadi::Tag tag) override; TagFetchJobInterface *fetchTags() override; 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 45e09e31..fa1ea47a 100644 --- a/src/akonadi/akonadistorageinterface.h +++ b/src/akonadi/akonadistorageinterface.h @@ -1,85 +1,85 @@ /* 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 #include class KJob; class QObject; class QByteArray; namespace Akonadi { class Collection; class CollectionFetchJobInterface; class ItemFetchJobInterface; class TagFetchJobInterface; 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 = Q_NULLPTR) = 0; + virtual KJob *updateItem(Akonadi::Item item, QObject *parent = nullptr) = 0; virtual KJob *removeItem(Akonadi::Item item) = 0; - virtual KJob *removeItems(Item::List items, QObject *parent = Q_NULLPTR) = 0; - virtual KJob *moveItem(Item item, Collection collection, QObject *parent = Q_NULLPTR) = 0; - virtual KJob *moveItems(Item::List item, Collection collection, QObject *parent = Q_NULLPTR) = 0; + virtual KJob *removeItems(Item::List items, QObject *parent = nullptr) = 0; + virtual KJob *moveItem(Item item, Collection collection, QObject *parent = nullptr) = 0; + virtual KJob *moveItems(Item::List item, Collection collection, QObject *parent = nullptr) = 0; - virtual KJob *createCollection(Collection collection, QObject *parent = Q_NULLPTR) = 0; - virtual KJob *updateCollection(Collection collection, QObject *parent = Q_NULLPTR) = 0; - virtual KJob *removeCollection(Collection collection, QObject *parent = Q_NULLPTR) = 0; + virtual KJob *createCollection(Collection collection, QObject *parent = nullptr) = 0; + virtual KJob *updateCollection(Collection collection, QObject *parent = nullptr) = 0; + virtual KJob *removeCollection(Collection collection, QObject *parent = nullptr) = 0; virtual KJob *createTransaction() = 0; virtual KJob *createTag(Akonadi::Tag tag) = 0; virtual KJob *updateTag(Akonadi::Tag tag) = 0; virtual KJob *removeTag(Akonadi::Tag tag) = 0; virtual CollectionFetchJobInterface *fetchCollections(Akonadi::Collection collection, FetchDepth depth) = 0; virtual ItemFetchJobInterface *fetchItems(Akonadi::Collection collection) = 0; virtual ItemFetchJobInterface *fetchItem(Akonadi::Item item) = 0; virtual ItemFetchJobInterface *fetchTagItems(Akonadi::Tag tag) = 0; virtual TagFetchJobInterface *fetchTags() = 0; }; } #endif // AKONADI_STORAGEINTERFACE_H diff --git a/src/domain/context.h b/src/domain/context.h index e6e88720..03cd6dc0 100644 --- a/src/domain/context.h +++ b/src/domain/context.h @@ -1,62 +1,62 @@ /* 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 DOMAIN_CONTEXT_H #define DOMAIN_CONTEXT_H #include #include #include namespace Domain { class Context : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) public: typedef QSharedPointer Ptr; typedef QList List; - explicit Context(QObject *parent = Q_NULLPTR); + explicit Context(QObject *parent = nullptr); virtual ~Context(); QString name() const; public slots: void setName(const QString &name); signals: void nameChanged(const QString &name); private: QString m_name; }; } Q_DECLARE_METATYPE(Domain::Context::Ptr) #endif // DOMAIN_CONTEXT_H diff --git a/src/domain/datasource.h b/src/domain/datasource.h index ac2b7d8f..05a8d19d 100644 --- a/src/domain/datasource.h +++ b/src/domain/datasource.h @@ -1,88 +1,88 @@ /* 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 DOMAIN_DATASOURCE_H #define DOMAIN_DATASOURCE_H #include #include #include namespace Domain { // cppcheck somehow doesn't see the ctor in here // cppcheck-suppress noConstructor class DataSource : public QObject { Q_OBJECT Q_ENUMS(ContentType) Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QString iconName READ iconName WRITE setIconName NOTIFY iconNameChanged) Q_PROPERTY(Domain::DataSource::ContentTypes contentTypes READ contentTypes WRITE setContentTypes NOTIFY contentTypesChanged) Q_PROPERTY(bool selected READ isSelected WRITE setSelected NOTIFY selectedChanged) public: typedef QSharedPointer Ptr; typedef QList List; enum ContentType { NoContent = 0, Tasks, Notes }; Q_DECLARE_FLAGS(ContentTypes, ContentType) - explicit DataSource(QObject *parent = Q_NULLPTR); + explicit DataSource(QObject *parent = nullptr); virtual ~DataSource(); QString name() const; QString iconName() const; ContentTypes contentTypes() const; bool isSelected() const; public slots: void setName(const QString &name); void setIconName(const QString &iconName); void setContentTypes(Domain::DataSource::ContentTypes types); void setSelected(bool selected); signals: void nameChanged(const QString &name); void iconNameChanged(const QString &iconName); void contentTypesChanged(Domain::DataSource::ContentTypes types); void selectedChanged(bool selected); private: QString m_name; QString m_iconName; ContentTypes m_contentTypes; bool m_selected; }; } Q_DECLARE_METATYPE(Domain::DataSource::Ptr) Q_DECLARE_METATYPE(Domain::DataSource::ContentTypes) Q_DECLARE_OPERATORS_FOR_FLAGS(Domain::DataSource::ContentTypes) #endif // DOMAIN_DATASOURCE_H diff --git a/src/domain/livequery.h b/src/domain/livequery.h index 77e95857..12303034 100644 --- a/src/domain/livequery.h +++ b/src/domain/livequery.h @@ -1,434 +1,434 @@ /* 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 DOMAIN_LIVEQUERY_H #define DOMAIN_LIVEQUERY_H #include "queryresult.h" namespace Domain { template class LiveQueryInput { public: typedef QSharedPointer> Ptr; typedef QWeakPointer> WeakPtr; typedef QList List; typedef QList WeakList; typedef std::function AddFunction; typedef std::function FetchFunction; typedef std::function PredicateFunction; virtual ~LiveQueryInput() {} virtual void reset() = 0; virtual void onAdded(const InputType &input) = 0; virtual void onChanged(const InputType &input) = 0; virtual void onRemoved(const InputType &input) = 0; }; template class LiveQueryOutput { public: typedef QSharedPointer> Ptr; typedef QList List; typedef QueryResult Result; virtual ~LiveQueryOutput() {} virtual typename Result::Ptr result() = 0; virtual void reset() = 0; }; template class LiveQuery : public LiveQueryInput, public LiveQueryOutput { public: typedef QSharedPointer> Ptr; typedef QList List; typedef QueryResultProvider Provider; typedef QueryResult Result; typedef typename LiveQueryInput::AddFunction AddFunction; typedef typename LiveQueryInput::FetchFunction FetchFunction; typedef typename LiveQueryInput::PredicateFunction PredicateFunction; typedef std::function ConvertFunction; typedef std::function UpdateFunction; typedef std::function RepresentsFunction; LiveQuery() = default; LiveQuery(const LiveQuery &other) = default; LiveQuery &operator=(const LiveQuery &other) = default; ~LiveQuery() { clear(); } typename Result::Ptr result() override { typename Provider::Ptr provider(m_provider.toStrongRef()); if (provider) return Result::create(provider); provider = Provider::Ptr::create(); m_provider = provider.toWeakRef(); doFetch(); return Result::create(provider); } void setFetchFunction(const FetchFunction &fetch) { m_fetch = fetch; } void setPredicateFunction(const PredicateFunction &predicate) { m_predicate = predicate; } void setConvertFunction(const ConvertFunction &convert) { m_convert = convert; } void setUpdateFunction(const UpdateFunction &update) { m_update = update; } void setDebugName(const QByteArray &name) { m_debugName = name; } void setRepresentsFunction(const RepresentsFunction &represents) { m_represents = represents; } void reset() override { clear(); doFetch(); } void onAdded(const InputType &input) override { typename Provider::Ptr provider(m_provider.toStrongRef()); if (!provider) return; if (m_predicate(input)) addToProvider(provider, input); } void onChanged(const InputType &input) override { typename Provider::Ptr provider(m_provider.toStrongRef()); if (!provider) return; if (!m_predicate(input)) { for (int i = 0; i < provider->data().size(); i++) { auto output = provider->data().at(i); if (m_represents(input, output)) { provider->removeAt(i); i--; } } } else { bool found = false; for (int i = 0; i < provider->data().size(); i++) { auto output = provider->data().at(i); if (m_represents(input, output)) { m_update(input, output); provider->replace(i, output); found = true; } } if (!found) addToProvider(provider, input); } } void onRemoved(const InputType &input) override { typename Provider::Ptr provider(m_provider.toStrongRef()); if (!provider) return; for (int i = 0; i < provider->data().size(); i++) { auto output = provider->data().at(i); if (m_represents(input, output)) { provider->removeAt(i); i--; } } } private: template bool isValidOutput(const T &/*output*/) { return true; } template bool isValidOutput(const QSharedPointer &output) { return !output.isNull(); } template bool isValidOutput(T *output) { - return output != Q_NULLPTR; + return output != nullptr; } void addToProvider(const typename Provider::Ptr &provider, const InputType &input) { auto output = m_convert(input); if (isValidOutput(output)) provider->append(output); } void doFetch() { typename Provider::Ptr provider(m_provider.toStrongRef()); if (!provider) return; auto addFunction = [this, provider] (const InputType &input) { if (m_predicate(input)) addToProvider(provider, input); }; m_fetch(addFunction); } void clear() { typename Provider::Ptr provider(m_provider.toStrongRef()); if (!provider) return; while (!provider->data().isEmpty()) provider->removeFirst(); } FetchFunction m_fetch; PredicateFunction m_predicate; ConvertFunction m_convert; UpdateFunction m_update; RepresentsFunction m_represents; QByteArray m_debugName; typename Provider::WeakPtr m_provider; }; // A query that stores an intermediate list of results (from the fetch), to react on changes on any item in that list // and then filters that list with the predicate for the final result // When one of the intermediary items changes, a full fetch is done again. template class LiveRelationshipQuery : public LiveQueryInput, public LiveQueryOutput { public: typedef QSharedPointer> Ptr; typedef QList List; typedef QueryResultProvider Provider; typedef QueryResult Result; typedef typename LiveQueryInput::AddFunction AddFunction; typedef typename LiveQueryInput::FetchFunction FetchFunction; typedef typename LiveQueryInput::PredicateFunction PredicateFunction; typedef std::function ConvertFunction; typedef std::function RepresentsFunction; typedef std::function CompareFunction; LiveRelationshipQuery() = default; LiveRelationshipQuery(const LiveRelationshipQuery &other) = default; LiveRelationshipQuery &operator=(const LiveRelationshipQuery &other) = default; ~LiveRelationshipQuery() { clear(); } typename Result::Ptr result() override { typename Provider::Ptr provider(m_provider.toStrongRef()); if (provider) return Result::create(provider); provider = Provider::Ptr::create(); m_provider = provider.toWeakRef(); doFetch(); return Result::create(provider); } void setFetchFunction(const FetchFunction &fetch) { m_fetch = fetch; } void setPredicateFunction(const PredicateFunction &predicate) { m_predicate = predicate; } void setCompareFunction(const CompareFunction &compare) { m_compare = compare; } void setConvertFunction(const ConvertFunction &convert) { m_convert = convert; } void setDebugName(const QByteArray &name) { m_debugName = name; } void setRepresentsFunction(const RepresentsFunction &represents) { m_represents = represents; } void reset() override { clear(); doFetch(); } void onAdded(const InputType &input) override { typename Provider::Ptr provider(m_provider.toStrongRef()); if (!provider) return; m_intermediaryResults.append(input); if (m_predicate(input)) addToProvider(provider, input); } void onChanged(const InputType &input) override { Q_ASSERT(m_compare); const bool found = std::any_of(m_intermediaryResults.constBegin(), m_intermediaryResults.constEnd(), [&input, this](const InputType &existing) { return m_compare(input, existing); }); if (found) reset(); } void onRemoved(const InputType &input) override { onChanged(input); } private: template bool isValidOutput(const T &/*output*/) { return true; } template bool isValidOutput(const QSharedPointer &output) { return !output.isNull(); } template bool isValidOutput(T *output) { - return output != Q_NULLPTR; + return output != nullptr; } void addToProvider(const typename Provider::Ptr &provider, const InputType &input) { auto output = m_convert(input); if (isValidOutput(output)) provider->append(output); } void doFetch() { auto addFunction = [this] (const InputType &input) { onAdded(input); }; m_fetch(addFunction); } void clear() { m_intermediaryResults.clear(); typename Provider::Ptr provider(m_provider.toStrongRef()); if (!provider) return; while (!provider->data().isEmpty()) provider->removeFirst(); } FetchFunction m_fetch; PredicateFunction m_predicate; ConvertFunction m_convert; CompareFunction m_compare; RepresentsFunction m_represents; QByteArray m_debugName; typename Provider::WeakPtr m_provider; QList m_intermediaryResults; }; } #endif // DOMAIN_LIVEQUERY_H diff --git a/src/domain/project.h b/src/domain/project.h index 029f2960..9ce91672 100644 --- a/src/domain/project.h +++ b/src/domain/project.h @@ -1,62 +1,62 @@ /* 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 DOMAIN_PROJECT_H #define DOMAIN_PROJECT_H #include #include #include namespace Domain { class Project : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) public: typedef QSharedPointer Ptr; typedef QList List; - explicit Project(QObject *parent = Q_NULLPTR); + explicit Project(QObject *parent = nullptr); virtual ~Project(); QString name() const; public slots: void setName(const QString &name); signals: void nameChanged(const QString &name); private: QString m_name; }; } Q_DECLARE_METATYPE(Domain::Project::Ptr) #endif // DOMAIN_PROJECT_H diff --git a/src/domain/task.h b/src/domain/task.h index 3ba2aeff..301eee92 100644 --- a/src/domain/task.h +++ b/src/domain/task.h @@ -1,153 +1,153 @@ /* 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 DOMAIN_TASK_H #define DOMAIN_TASK_H #include #include #include #include #include namespace Domain { class Task : public QObject { Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) Q_PROPERTY(bool done READ isDone WRITE setDone NOTIFY doneChanged) Q_PROPERTY(QDate startDate READ startDate WRITE setStartDate NOTIFY startDateChanged) Q_PROPERTY(QDate dueDate READ dueDate WRITE setDueDate NOTIFY dueDateChanged) Q_PROPERTY(Domain::Task::Recurrence recurrence READ recurrence WRITE setRecurrence NOTIFY recurrenceChanged) Q_PROPERTY(Domain::Task::Attachments attachements READ attachments WRITE setAttachments NOTIFY attachmentsChanged) public: typedef QSharedPointer Ptr; typedef QList List; enum Recurrence { NoRecurrence = 0, RecursDaily, RecursWeekly, RecursMonthly // for now only monthly on the same day (say 11th day of the month) }; Q_ENUM(Recurrence) class Attachment { public: Attachment(); explicit Attachment(const QByteArray &data); explicit Attachment(const QUrl &uri); Attachment(const Attachment &other); ~Attachment(); Attachment &operator=(const Attachment &other); bool operator==(const Attachment &other) const; bool isValid() const; bool isUri() const; QUrl uri() const; void setUri(const QUrl &uri); QByteArray data() const; void setData(const QByteArray &data); QString label() const; void setLabel(const QString &label); QString mimeType() const; void setMimeType(const QString &mimeType); QString iconName() const; void setIconName(const QString &iconName); private: QUrl m_uri; QByteArray m_data; QString m_label; QString m_mimeType; QString m_iconName; }; typedef QList Attachments; - explicit Task(QObject *parent = Q_NULLPTR); + explicit Task(QObject *parent = nullptr); virtual ~Task(); QString text() const; QString title() const; bool isRunning() const; bool isDone() const; QDate startDate() const; QDate dueDate() const; QDate doneDate() const; Recurrence recurrence() const; Attachments attachments() const; public slots: void setText(const QString &text); void setTitle(const QString &title); void setRunning(bool running); void setDone(bool done); void setDoneDate(const QDate &doneDate); void setStartDate(const QDate &startDate); void setDueDate(const QDate &dueDate); void setRecurrence(Domain::Task::Recurrence recurrence); void setAttachments(const Domain::Task::Attachments &attachments); signals: void textChanged(const QString &text); void titleChanged(const QString &title); void runningChanged(bool isRunning); void doneChanged(bool isDone); void doneDateChanged(const QDate &doneDate); void startDateChanged(const QDate &startDate); void dueDateChanged(const QDate &dueDate); void recurrenceChanged(Domain::Task::Recurrence recurrence); void attachmentsChanged(const Domain::Task::Attachments &attachments); private: QString m_text; QString m_title; bool m_running; bool m_done; QDate m_startDate; QDate m_dueDate; QDate m_doneDate; Recurrence m_recurrence; Attachments m_attachments; }; } Q_DECLARE_METATYPE(Domain::Task::Ptr) Q_DECLARE_METATYPE(Domain::Task::List) Q_DECLARE_METATYPE(Domain::Task::Attachment) Q_DECLARE_METATYPE(Domain::Task::Attachments) #endif // DOMAIN_TASK_H diff --git a/src/presentation/applicationmodel.cpp b/src/presentation/applicationmodel.cpp index 1dfa9c61..7ba0989b 100644 --- a/src/presentation/applicationmodel.cpp +++ b/src/presentation/applicationmodel.cpp @@ -1,133 +1,133 @@ /* 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 "applicationmodel.h" #include "presentation/availablepagesmodel.h" #include "presentation/availablesourcesmodel.h" #include "presentation/editormodel.h" #include "presentation/errorhandler.h" #include "presentation/pagemodel.h" #include "presentation/runningtaskmodel.h" #include "utils/dependencymanager.h" #include "utils/jobhandler.h" using namespace Presentation; ApplicationModel::ApplicationModel(QObject *parent) : QObject(parent), - m_errorHandler(Q_NULLPTR) + m_errorHandler(nullptr) { MetaTypes::registerAll(); } ApplicationModel::~ApplicationModel() { Utils::JobHandler::clear(); } QObject *ApplicationModel::availableSources() { if (!m_availableSources) { auto model = Utils::DependencyManager::globalInstance().create(); model->setErrorHandler(errorHandler()); m_availableSources = model; } return m_availableSources.data(); } QObject *ApplicationModel::availablePages() { if (!m_availablePages) { auto model = Utils::DependencyManager::globalInstance().create(); model->setErrorHandler(errorHandler()); m_availablePages = model; } return m_availablePages.data(); } QObject *ApplicationModel::currentPage() { return m_currentPage.data(); } QObject *ApplicationModel::editor() { if (!m_editor) { auto model = Utils::DependencyManager::globalInstance().create(); model->setErrorHandler(errorHandler()); m_editor = model; } return m_editor.data(); } RunningTaskModelInterface *ApplicationModel::runningTaskModel() { if (!m_runningTaskModel) { auto model = Utils::DependencyManager::globalInstance().create(); m_runningTaskModel = model; m_runningTaskModel->setErrorHandler(errorHandler()); } return m_runningTaskModel.data(); } ErrorHandler *ApplicationModel::errorHandler() const { return m_errorHandler; } void ApplicationModel::setCurrentPage(QObject *page) { if (page == m_currentPage) return; m_currentPage = QObjectPtr(page); if (m_currentPage) { - m_currentPage->setParent(Q_NULLPTR); + m_currentPage->setParent(nullptr); auto pageModel = m_currentPage.staticCast(); Q_ASSERT(pageModel); pageModel->setErrorHandler(errorHandler()); } emit currentPageChanged(page); } void ApplicationModel::setErrorHandler(ErrorHandler *errorHandler) { m_errorHandler = errorHandler; if (m_availableSources) m_availableSources.staticCast()->setErrorHandler(errorHandler); if (m_availablePages) m_availablePages.staticCast()->setErrorHandler(errorHandler); if (m_editor) m_editor.staticCast()->setErrorHandler(errorHandler); if (m_runningTaskModel) m_runningTaskModel.staticCast()->setErrorHandler(errorHandler); if (m_currentPage) m_currentPage.staticCast()->setErrorHandler(errorHandler); } diff --git a/src/presentation/applicationmodel.h b/src/presentation/applicationmodel.h index 5b96ec56..325f0b46 100644 --- a/src/presentation/applicationmodel.h +++ b/src/presentation/applicationmodel.h @@ -1,83 +1,83 @@ /* 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 PRESENTATION_APPLICATIONMODEL_H #define PRESENTATION_APPLICATIONMODEL_H #include #include "domain/datasourcerepository.h" #include "domain/datasourcequeries.h" #include "domain/taskrepository.h" #include "presentation/metatypes.h" #include "presentation/runningtaskmodelinterface.h" namespace Presentation { class ErrorHandler; class ApplicationModel : public QObject { Q_OBJECT Q_PROPERTY(QObject* availableSources READ availableSources) Q_PROPERTY(QObject* availablePages READ availablePages) Q_PROPERTY(QObject* currentPage READ currentPage WRITE setCurrentPage NOTIFY currentPageChanged) Q_PROPERTY(QObject* editor READ editor) Q_PROPERTY(RunningTaskModelInterface* runningTaskModel READ runningTaskModel) Q_PROPERTY(Presentation::ErrorHandler* errorHandler READ errorHandler WRITE setErrorHandler) public: typedef QSharedPointer Ptr; - explicit ApplicationModel(QObject *parent = Q_NULLPTR); + explicit ApplicationModel(QObject *parent = nullptr); ~ApplicationModel(); QObject *availableSources(); QObject *availablePages(); QObject *currentPage(); QObject *editor(); Presentation::RunningTaskModelInterface *runningTaskModel(); ErrorHandler *errorHandler() const; public slots: void setCurrentPage(QObject *page); void setErrorHandler(ErrorHandler *errorHandler); signals: void currentPageChanged(QObject *page); private: QObjectPtr m_availableSources; QObjectPtr m_availablePages; QObjectPtr m_currentPage; QObjectPtr m_editor; RunningTaskModelInterface::Ptr m_runningTaskModel; ErrorHandler *m_errorHandler; }; } #endif // PRESENTATION_APPLICATIONMODEL_H diff --git a/src/presentation/availablepagesmodel.cpp b/src/presentation/availablepagesmodel.cpp index 8529b6a4..b09c6feb 100644 --- a/src/presentation/availablepagesmodel.cpp +++ b/src/presentation/availablepagesmodel.cpp @@ -1,317 +1,317 @@ /* 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 "availablepagesmodel.h" #include #include #include #include "domain/contextqueries.h" #include "domain/contextrepository.h" #include "domain/projectqueries.h" #include "domain/projectrepository.h" #include "domain/taskrepository.h" #include "presentation/availablepagessortfilterproxymodel.h" #include "presentation/contextpagemodel.h" #include "presentation/inboxpagemodel.h" #include "presentation/metatypes.h" #include "presentation/projectpagemodel.h" #include "presentation/querytreemodel.h" #include "presentation/workdaypagemodel.h" #include "utils/jobhandler.h" #include "utils/datetime.h" using namespace Presentation; AvailablePagesModel::AvailablePagesModel(const Domain::DataSourceQueries::Ptr &dataSourceQueries, const Domain::ProjectQueries::Ptr &projectQueries, const Domain::ProjectRepository::Ptr &projectRepository, const Domain::ContextQueries::Ptr &contextQueries, const Domain::ContextRepository::Ptr &contextRepository, const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent) : QObject(parent), - m_pageListModel(Q_NULLPTR), - m_sortProxyModel(Q_NULLPTR), + m_pageListModel(nullptr), + m_sortProxyModel(nullptr), m_dataSourceQueries(dataSourceQueries), m_projectQueries(projectQueries), m_projectRepository(projectRepository), m_contextQueries(contextQueries), m_contextRepository(contextRepository), m_taskQueries(taskQueries), m_taskRepository(taskRepository) { } QAbstractItemModel *AvailablePagesModel::pageListModel() { if (!m_pageListModel) m_pageListModel = createPageListModel(); if (!m_sortProxyModel) { m_sortProxyModel = new AvailablePagesSortFilterProxyModel(this); m_sortProxyModel->setSourceModel(m_pageListModel); } return m_sortProxyModel; } QObject *AvailablePagesModel::createPageForIndex(const QModelIndex &index) { QObjectPtr object = index.data(QueryTreeModelBase::ObjectRole).value(); if (object == m_inboxObject) { auto inboxPageModel = new InboxPageModel(m_taskQueries, m_taskRepository, this); inboxPageModel->setErrorHandler(errorHandler()); return inboxPageModel; } else if (object == m_workdayObject) { auto workdayPageModel = new WorkdayPageModel(m_taskQueries, m_taskRepository, this); workdayPageModel->setErrorHandler(errorHandler()); return workdayPageModel; } else if (auto project = object.objectCast()) { auto projectPageModel = new ProjectPageModel(project, m_projectQueries, m_projectRepository, m_taskQueries, m_taskRepository, this); projectPageModel->setErrorHandler(errorHandler()); return projectPageModel; } else if (auto context = object.objectCast()) { auto contextPageModel = new ContextPageModel(context, m_contextQueries, m_contextRepository, m_taskQueries, m_taskRepository, this); contextPageModel->setErrorHandler(errorHandler()); return contextPageModel; } - return Q_NULLPTR; + return nullptr; } void AvailablePagesModel::addProject(const QString &name, const Domain::DataSource::Ptr &source) { auto project = Domain::Project::Ptr::create(); project->setName(name); const auto job = m_projectRepository->create(project, source); installHandler(job, i18n("Cannot add project %1 in dataSource %2", name, source->name())); } void AvailablePagesModel::addContext(const QString &name) { auto context = Domain::Context::Ptr::create(); context->setName(name); const auto job = m_contextRepository->create(context); installHandler(job, i18n("Cannot add context %1", name)); } void AvailablePagesModel::removeItem(const QModelIndex &index) { QObjectPtr object = index.data(QueryTreeModelBase::ObjectRole).value(); if (auto project = object.objectCast()) { const auto job = m_projectRepository->remove(project); installHandler(job, i18n("Cannot remove project %1", project->name())); } else if (auto context = object.objectCast()) { const auto job = m_contextRepository->remove(context); installHandler(job, i18n("Cannot remove context %1", context->name())); } else { Q_ASSERT(false); } } QAbstractItemModel *AvailablePagesModel::createPageListModel() { m_inboxObject = QObjectPtr::create(); m_inboxObject->setProperty("name", i18n("Inbox")); m_workdayObject = QObjectPtr::create(); m_workdayObject->setProperty("name", i18n("Workday")); m_projectsObject = QObjectPtr::create(); m_projectsObject->setProperty("name", i18n("Projects")); m_contextsObject = QObjectPtr::create(); m_contextsObject->setProperty("name", i18n("Contexts")); m_rootsProvider = Domain::QueryResultProvider::Ptr::create(); m_rootsProvider->append(m_inboxObject); m_rootsProvider->append(m_workdayObject); m_rootsProvider->append(m_projectsObject); m_rootsProvider->append(m_contextsObject); auto query = [this](const QObjectPtr &object) -> Domain::QueryResultInterface::Ptr { if (!object) return Domain::QueryResult::create(m_rootsProvider); else if (object == m_projectsObject) return Domain::QueryResult::copy(m_dataSourceQueries->findAllSelected()); else if (object == m_contextsObject) return Domain::QueryResult::copy(m_contextQueries->findAll()); else if (const auto source = object.objectCast()) return Domain::QueryResult::copy(m_dataSourceQueries->findProjects(source)); else return Domain::QueryResult::Ptr(); }; auto flags = [this](const QObjectPtr &object) { const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDropEnabled; const Qt::ItemFlags immutableNodeFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; const Qt::ItemFlags structureNodeFlags = Qt::NoItemFlags; return object.objectCast() ? defaultFlags : object.objectCast() ? defaultFlags : object == m_inboxObject ? immutableNodeFlags : object == m_workdayObject ? immutableNodeFlags : structureNodeFlags; }; auto data = [this](const QObjectPtr &object, int role, int) -> QVariant { if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::DecorationRole && role != QueryTreeModelBase::IconNameRole) { return QVariant(); } if (role == Qt::EditRole && (object == m_inboxObject || object == m_workdayObject || object == m_projectsObject || object == m_contextsObject || object.objectCast())) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { return object->property("name").toString(); } else if (role == Qt::DecorationRole || role == QueryTreeModelBase::IconNameRole) { const QString iconName = object == m_inboxObject ? QStringLiteral("mail-folder-inbox") : (object == m_workdayObject) ? QStringLiteral("go-jump-today") : (object == m_projectsObject) ? QStringLiteral("folder") : (object == m_contextsObject) ? QStringLiteral("folder") : object.objectCast() ? QStringLiteral("folder") : object.objectCast() ? QStringLiteral("view-pim-notes") : QStringLiteral("view-pim-tasks"); if (role == Qt::DecorationRole) return QVariant::fromValue(QIcon::fromTheme(iconName)); else return iconName; } else { return QVariant(); } }; auto setData = [this](const QObjectPtr &object, const QVariant &value, int role) { if (role != Qt::EditRole) { return false; } if (object == m_inboxObject || object == m_workdayObject || object == m_projectsObject || object == m_contextsObject || object.objectCast()) { return false; } if (auto project = object.objectCast()) { const auto currentName = project->name(); project->setName(value.toString()); const auto job = m_projectRepository->update(project); installHandler(job, i18n("Cannot modify project %1", currentName)); } else if (auto context = object.objectCast()) { const auto currentName = context->name(); context->setName(value.toString()); const auto job = m_contextRepository->update(context); installHandler(job, i18n("Cannot modify context %1", currentName)); } else { Q_ASSERT(false); } return true; }; auto drop = [this](const QMimeData *mimeData, Qt::DropAction, const QObjectPtr &object) { if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))) return false; auto droppedTasks = mimeData->property("objects").value(); if (droppedTasks.isEmpty()) return false; if (auto project = object.objectCast()) { foreach (const auto &task, droppedTasks) { const auto job = m_projectRepository->associate(project, task); installHandler(job, i18n("Cannot add %1 to project %2", task->title(), project->name())); } return true; } else if (auto context = object.objectCast()) { foreach (const auto &task, droppedTasks) { const auto job = m_contextRepository->associate(context, task); installHandler(job, i18n("Cannot add %1 to context %2", task->title(), context->name())); } return true; } else if (object == m_inboxObject) { foreach (const auto &task, droppedTasks) { const auto job = m_projectRepository->dissociate(task); installHandler(job, i18n("Cannot move %1 to Inbox", task->title())); Utils::JobHandler::install(job, [this, task] { const auto dissociateJob = m_taskRepository->dissociateAll(task); installHandler(dissociateJob, i18n("Cannot move task %1 to Inbox", task->title())); }); } return true; } else if (object == m_workdayObject) { foreach (const auto &task, droppedTasks) { task->setStartDate(Utils::DateTime::currentDate()); const auto job = m_taskRepository->update(task); installHandler(job, i18n("Cannot update task %1 to Workday", task->title())); } return true; } return false; }; auto drag = [](const QObjectPtrList &) -> QMimeData* { - return Q_NULLPTR; + return nullptr; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, nullptr, this); } diff --git a/src/presentation/availablepagesmodel.h b/src/presentation/availablepagesmodel.h index eef8c0dc..334c71b5 100644 --- a/src/presentation/availablepagesmodel.h +++ b/src/presentation/availablepagesmodel.h @@ -1,93 +1,93 @@ /* 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 PRESENTATION_AVAILABLETASKPAGESMODEL_H #define PRESENTATION_AVAILABLETASKPAGESMODEL_H #include "presentation/errorhandlingmodelbase.h" #include "domain/contextqueries.h" #include "domain/contextrepository.h" #include "domain/datasourcequeries.h" #include "domain/projectqueries.h" #include "domain/projectrepository.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" #include "presentation/metatypes.h" class QModelIndex; namespace Presentation { class AvailablePagesSortFilterProxyModel; class AvailablePagesModel : public QObject, public ErrorHandlingModelBase { Q_OBJECT Q_PROPERTY(QAbstractItemModel* pageListModel READ pageListModel) public: explicit AvailablePagesModel(const Domain::DataSourceQueries::Ptr &dataSourceQueries, const Domain::ProjectQueries::Ptr &projectQueries, const Domain::ProjectRepository::Ptr &projectRepository, const Domain::ContextQueries::Ptr &contextQueries, const Domain::ContextRepository::Ptr &contextRepository, const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, - QObject *parent = Q_NULLPTR); + QObject *parent = nullptr); QAbstractItemModel *pageListModel(); Q_SCRIPTABLE QObject *createPageForIndex(const QModelIndex &index); void addProject(const QString &name, const Domain::DataSource::Ptr &source); void addContext(const QString &name); void removeItem(const QModelIndex &index); private: QAbstractItemModel *createPageListModel(); QAbstractItemModel *m_pageListModel; Presentation::AvailablePagesSortFilterProxyModel *m_sortProxyModel; Domain::DataSourceQueries::Ptr m_dataSourceQueries; Domain::ProjectQueries::Ptr m_projectQueries; Domain::ProjectRepository::Ptr m_projectRepository; Domain::ContextQueries::Ptr m_contextQueries; Domain::ContextRepository::Ptr m_contextRepository; Domain::TaskQueries::Ptr m_taskQueries; Domain::TaskRepository::Ptr m_taskRepository; Domain::QueryResultProvider::Ptr m_rootsProvider; QObjectPtr m_inboxObject; QObjectPtr m_workdayObject; QObjectPtr m_projectsObject; QObjectPtr m_contextsObject; }; } #endif // PRESENTATION_AVAILABLETASKPAGESMODEL_H diff --git a/src/presentation/availablesourcesmodel.cpp b/src/presentation/availablesourcesmodel.cpp index 1787ba48..1141590e 100644 --- a/src/presentation/availablesourcesmodel.cpp +++ b/src/presentation/availablesourcesmodel.cpp @@ -1,156 +1,156 @@ /* 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 "availablesourcesmodel.h" #include #include #include "domain/datasourcequeries.h" #include "domain/datasourcerepository.h" #include "presentation/querytreemodel.h" using namespace Presentation; AvailableSourcesModel::AvailableSourcesModel(const Domain::DataSourceQueries::Ptr &dataSourceQueries, const Domain::DataSourceRepository::Ptr &dataSourceRepository, QObject *parent) : QObject(parent), - m_sourceListModel(Q_NULLPTR), + m_sourceListModel(nullptr), m_dataSourceQueries(dataSourceQueries), m_dataSourceRepository(dataSourceRepository) { } QAbstractItemModel *AvailableSourcesModel::sourceListModel() { if (!m_sourceListModel) m_sourceListModel = createSourceListModel(); return m_sourceListModel; } void AvailableSourcesModel::showConfigDialog() { m_dataSourceRepository->showConfigDialog(); } QAbstractItemModel *AvailableSourcesModel::createSourceListModel() { auto query = [this] (const Domain::DataSource::Ptr &source) { if (!source) return m_dataSourceQueries->findTopLevel(); else return m_dataSourceQueries->findChildren(source); }; auto flags = [] (const Domain::DataSource::Ptr &source) -> Qt::ItemFlags { const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; if (source->contentTypes() != Domain::DataSource::NoContent) return defaultFlags | Qt::ItemIsUserCheckable; else return defaultFlags; }; auto data = [this] (const Domain::DataSource::Ptr &source, int role, int) -> QVariant { if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::DecorationRole && role != Qt::CheckStateRole && role != QueryTreeModelBase::IconNameRole && role != QueryTreeModelBase::IsDefaultRole) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { return source->name(); } else if (role == Qt::DecorationRole || role == QueryTreeModelBase::IconNameRole) { const QString iconName = source->iconName().isEmpty() ? QStringLiteral("folder") : source->iconName(); if (role == Qt::DecorationRole) return QVariant::fromValue(QIcon::fromTheme(iconName)); else return iconName; } else if (role == Qt::CheckStateRole) { if (source->contentTypes() != Domain::DataSource::NoContent) return source->isSelected() ? Qt::Checked : Qt::Unchecked; else return QVariant(); } else if (role == QueryTreeModelBase::IsDefaultRole) { return m_dataSourceQueries->isDefaultSource(source); } else { return QVariant(); } }; auto setData = [this] (const Domain::DataSource::Ptr &source, const QVariant &value, int role) { if (role != Qt::CheckStateRole) return false; if (source->contentTypes() == Domain::DataSource::NoContent) return false; source->setSelected(value.toInt() == Qt::Checked); const auto job = m_dataSourceRepository->update(source); installHandler(job, i18n("Cannot modify source %1", source->name())); return true; }; auto drop = [] (const QMimeData *mimeData, Qt::DropAction, const Domain::DataSource::Ptr &source) { Q_UNUSED(mimeData) Q_UNUSED(source) return false; }; auto drag = [](const Domain::DataSource::List &) -> QMimeData* { - return Q_NULLPTR; + return nullptr; }; connect(m_dataSourceQueries->notifier(), &Domain::DataSourceQueriesNotifier::defaultSourceChanged, this, &AvailableSourcesModel::onDefaultSourceChanged); return new QueryTreeModel(query, flags, data, setData, drop, drag, nullptr, this); } void AvailableSourcesModel::setDefaultItem(const QModelIndex &index) { auto source = index.data(QueryTreeModelBase::ObjectRole).value(); Q_ASSERT(source); m_dataSourceQueries->setDefaultSource(source); } void AvailableSourcesModel::onDefaultSourceChanged() { emitDefaultSourceChanged(QModelIndex()); } void AvailableSourcesModel::emitDefaultSourceChanged(const QModelIndex &root) { const auto rowCount = m_sourceListModel->rowCount(root); for (int row = 0; row < rowCount; row++) { const auto index = m_sourceListModel->index(row, 0, root); emit m_sourceListModel->dataChanged(index, index); emitDefaultSourceChanged(index); } } diff --git a/src/presentation/availablesourcesmodel.h b/src/presentation/availablesourcesmodel.h index 025aa847..13f0dc06 100644 --- a/src/presentation/availablesourcesmodel.h +++ b/src/presentation/availablesourcesmodel.h @@ -1,72 +1,72 @@ /* 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 PRESENTATION_AVAILABLESOURCESMODEL_H #define PRESENTATION_AVAILABLESOURCESMODEL_H #include #include "domain/datasourcequeries.h" #include "domain/datasourcerepository.h" #include "presentation/metatypes.h" #include "presentation/errorhandlingmodelbase.h" class QModelIndex; namespace Presentation { class AvailableSourcesModel : public QObject, public ErrorHandlingModelBase { Q_OBJECT Q_PROPERTY(QAbstractItemModel* sourceListModel READ sourceListModel) public: explicit AvailableSourcesModel(const Domain::DataSourceQueries::Ptr &dataSourceQueries, const Domain::DataSourceRepository::Ptr &dataSourceRepository, - QObject *parent = Q_NULLPTR); + QObject *parent = nullptr); QAbstractItemModel *sourceListModel(); public slots: void setDefaultItem(const QModelIndex &index); void showConfigDialog(); private slots: void onDefaultSourceChanged(); private: void emitDefaultSourceChanged(const QModelIndex &root); QAbstractItemModel *createSourceListModel(); QAbstractItemModel *m_sourceListModel; Domain::DataSourceQueries::Ptr m_dataSourceQueries; Domain::DataSourceRepository::Ptr m_dataSourceRepository; }; } #endif // PRESENTATION_AVAILABLESOURCESMODEL_H diff --git a/src/presentation/contextpagemodel.cpp b/src/presentation/contextpagemodel.cpp index 91102dea..56c765d1 100644 --- a/src/presentation/contextpagemodel.cpp +++ b/src/presentation/contextpagemodel.cpp @@ -1,208 +1,208 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens 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 "contextpagemodel.h" #include #include #include "domain/task.h" #include "domain/contextqueries.h" #include "domain/contextrepository.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" #include "presentation/querytreemodel.h" using namespace Presentation; ContextPageModel::ContextPageModel(const Domain::Context::Ptr &context, const Domain::ContextQueries::Ptr &contextQueries, const Domain::ContextRepository::Ptr &contextRepository, const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent) : PageModel(parent), m_context(context), m_contextQueries(contextQueries), m_contextRepository(contextRepository), m_taskQueries(taskQueries), m_taskRepository(taskRepository) { } Domain::Context::Ptr ContextPageModel::context() const { return m_context; } Domain::Task::Ptr ContextPageModel::addItem(const QString &title, const QModelIndex &parentIndex) { const auto parentData = parentIndex.data(QueryTreeModelBase::ObjectRole); const auto parentTask = parentData.value(); auto task = Domain::Task::Ptr::create(); task->setTitle(title); const auto job = parentTask ? m_taskRepository->createChild(task, parentTask) : m_taskRepository->createInContext(task, m_context); installHandler(job, i18n("Cannot add task %1 in context %2", title, m_context->name())); return task; } void ContextPageModel::removeItem(const QModelIndex &index) { QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto task = data.value(); const auto job = index.parent().isValid() ? m_taskRepository->dissociate(task) : m_contextRepository->dissociate(m_context, task); installHandler(job, i18n("Cannot remove task %1 from context %2", task->title(), m_context->name())); } void ContextPageModel::promoteItem(const QModelIndex &index) { QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto task = data.value(); Q_ASSERT(task); const auto job = m_taskRepository->promoteToProject(task); installHandler(job, i18n("Cannot promote task %1 to be a project", task->title())); } QAbstractItemModel *ContextPageModel::createCentralListModel() { auto query = [this] (const Domain::Task::Ptr &task) -> Domain::QueryResultInterface::Ptr { if (!task) return m_contextQueries->findTopLevelTasks(m_context); else return m_taskQueries->findChildren(task); }; auto flags = [] (const Domain::Task::Ptr &task) { Q_UNUSED(task); return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled; }; using AdditionalInfo = Domain::QueryResult::Ptr; // later on we'll want a struct with the context query as well auto data = [] (const Domain::Task::Ptr &task, int role, const AdditionalInfo &projectQueryResult) -> QVariant { switch (role) { case Qt::DisplayRole: case Qt::EditRole: return task->title(); case Qt::CheckStateRole: return task->isDone() ? Qt::Checked : Qt::Unchecked; case Presentation::QueryTreeModelBase::AdditionalInfoRole: if (projectQueryResult && !projectQueryResult->data().isEmpty()) { Domain::Project::Ptr project = projectQueryResult->data().at(0); return i18n("Project: %1", project->name()); } return i18n("Inbox"); default: break; } return QVariant(); }; auto fetchAdditionalInfo = [this](const QModelIndex &index, const Domain::Task::Ptr &task) -> AdditionalInfo { AdditionalInfo projectQueryResult = m_taskQueries->findProject(task); if (projectQueryResult) { QPersistentModelIndex persistentIndex(index); projectQueryResult->addPostInsertHandler([persistentIndex](const Domain::Project::Ptr &, int) { // When a project was found (inserted into the result), update the rendering of the item auto model = const_cast(persistentIndex.model()); model->dataChanged(persistentIndex, persistentIndex); }); } return projectQueryResult; }; auto setData = [this] (const Domain::Task::Ptr &task, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) return false; const auto currentTitle = task->title(); if (role == Qt::EditRole) task->setTitle(value.toString()); else task->setDone(value.toInt() == Qt::Checked); const auto job = m_taskRepository->update(task); installHandler(job, i18n("Cannot modify task %1 in context %2", currentTitle, m_context->name())); return true; }; auto drop = [this] (const QMimeData *mimeData, Qt::DropAction, const Domain::Task::Ptr &parentTask) { if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))) return false; auto droppedTasks = mimeData->property("objects").value(); if (droppedTasks.isEmpty()) return false; using namespace std::placeholders; auto associate = std::function(); auto dissociate = std::function(); auto parentTitle = QString(); if (parentTask) { associate = std::bind(&Domain::TaskRepository::associate, m_taskRepository, parentTask, _1); - dissociate = [] (Domain::Task::Ptr) -> KJob* { return Q_NULLPTR; }; + dissociate = [] (Domain::Task::Ptr) -> KJob* { return nullptr; }; parentTitle = parentTask->title(); } else { associate = std::bind(&Domain::ContextRepository::associate, m_contextRepository, m_context, _1); dissociate = std::bind(&Domain::TaskRepository::dissociate, m_taskRepository, _1); parentTitle = m_context->name(); } foreach(const Domain::Task::Ptr &childTask, droppedTasks) { auto job = associate(childTask); installHandler(job, i18n("Cannot move task %1 as sub-task of %2", childTask->title(), parentTitle)); job = dissociate(childTask); if (job) installHandler(job, i18n("Cannot dissociate task %1 from its parent", childTask->title())); } return true; }; auto drag = [] (const Domain::Task::List &tasks) -> QMimeData* { if (tasks.isEmpty()) - return Q_NULLPTR; + return nullptr; auto data = new QMimeData(); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(tasks)); return data; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, fetchAdditionalInfo, this); } diff --git a/src/presentation/contextpagemodel.h b/src/presentation/contextpagemodel.h index 2f281c28..42056944 100644 --- a/src/presentation/contextpagemodel.h +++ b/src/presentation/contextpagemodel.h @@ -1,65 +1,65 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens 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. */ #ifndef CONTEXTPAGEMODEL_H #define CONTEXTPAGEMODEL_H #include "presentation/pagemodel.h" #include "domain/contextqueries.h" #include "domain/contextrepository.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" namespace Presentation { class ContextPageModel : public PageModel { Q_OBJECT public: explicit ContextPageModel(const Domain::Context::Ptr &context, const Domain::ContextQueries::Ptr &contextQueries, const Domain::ContextRepository::Ptr &contextRepository, const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, - QObject *parent = Q_NULLPTR); + QObject *parent = nullptr); Domain::Context::Ptr context() const; public slots: Domain::Task::Ptr addItem(const QString &title, const QModelIndex &parentIndex = QModelIndex()) override; void removeItem(const QModelIndex &index) override; void promoteItem(const QModelIndex &index) override; private: QAbstractItemModel *createCentralListModel() override; Domain::Context::Ptr m_context; Domain::ContextQueries::Ptr m_contextQueries; Domain::ContextRepository::Ptr m_contextRepository; Domain::TaskQueries::Ptr m_taskQueries; Domain::TaskRepository::Ptr m_taskRepository; }; } #endif // CONTEXTPAGEMODEL_H diff --git a/src/presentation/editormodel.cpp b/src/presentation/editormodel.cpp index a3d9e865..7997c219 100644 --- a/src/presentation/editormodel.cpp +++ b/src/presentation/editormodel.cpp @@ -1,463 +1,463 @@ /* 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 "editormodel.h" #include #include #include #include #include #include #include #include #include #include "domain/task.h" #include "errorhandler.h" namespace Presentation { class AttachmentModel : public QAbstractListModel { Q_OBJECT public: explicit AttachmentModel(QObject *parent = nullptr) : QAbstractListModel(parent) { } void setTask(const Domain::Task::Ptr &task) { if (m_task == task) return; beginResetModel(); if (m_task) { disconnect(m_task.data(), &Domain::Task::attachmentsChanged, this, &AttachmentModel::triggerReset); } m_task = task; if (m_task) { connect(m_task.data(), &Domain::Task::attachmentsChanged, this, &AttachmentModel::triggerReset); } endResetModel(); } int rowCount(const QModelIndex &parent) const override { if (parent.isValid() || !m_task) return 0; return m_task->attachments().size(); } QVariant data(const QModelIndex &index, int role) const override { if (!index.isValid()) return QVariant(); auto attachment = m_task->attachments().at(index.row()); switch (role) { case Qt::DisplayRole: return attachment.label(); case Qt::DecorationRole: return QVariant::fromValue(QIcon::fromTheme(attachment.iconName())); default: return QVariant(); } } private slots: void triggerReset() { beginResetModel(); endResetModel(); } private: Domain::Task::Ptr m_task; }; } using namespace Presentation; static int s_autoSaveDelay = 500; EditorModel::EditorModel(QObject *parent) : QObject(parent), m_done(false), m_recurrence(Domain::Task::NoRecurrence), m_attachmentModel(new AttachmentModel(this)), m_saveTimer(new QTimer(this)), m_saveNeeded(false), m_editingInProgress(false) { m_saveTimer->setSingleShot(true); connect(m_saveTimer, &QTimer::timeout, this, &EditorModel::save); } EditorModel::~EditorModel() { save(); } Domain::Task::Ptr EditorModel::task() const { return m_task; } void EditorModel::setTask(const Domain::Task::Ptr &task) { if (m_task == task) return; save(); m_text = QString(); m_title = QString(); m_done = false; m_start = QDate(); m_due = QDate(); m_recurrence = Domain::Task::NoRecurrence; m_attachmentModel->setTask(Domain::Task::Ptr()); if (m_task) - disconnect(m_task.data(), Q_NULLPTR, this, Q_NULLPTR); + disconnect(m_task.data(), nullptr, this, nullptr); m_task = task; if (m_task) { m_text = m_task->text(); m_title = m_task->title(); m_done = m_task->isDone(); m_start = m_task->startDate(); m_due = m_task->dueDate(); m_recurrence = m_task->recurrence(); m_attachmentModel->setTask(m_task); connect(m_task.data(), &Domain::Task::textChanged, this, &EditorModel::onTextChanged); connect(m_task.data(), &Domain::Task::titleChanged, this, &EditorModel::onTitleChanged); connect(m_task.data(), &Domain::Task::doneChanged, this, &EditorModel::onDoneChanged); connect(m_task.data(), &Domain::Task::startDateChanged, this, &EditorModel::onStartDateChanged); connect(m_task.data(), &Domain::Task::dueDateChanged, this, &EditorModel::onDueDateChanged); connect(m_task.data(), &Domain::Task::recurrenceChanged, this, &EditorModel::onRecurrenceChanged); } emit textChanged(m_text); emit titleChanged(m_title); emit doneChanged(m_done); emit startDateChanged(m_start); emit dueDateChanged(m_due); emit recurrenceChanged(m_recurrence); emit taskChanged(m_task); } bool EditorModel::hasSaveFunction() const { return bool(m_saveFunction); } void EditorModel::setSaveFunction(const SaveFunction &function) { m_saveFunction = function; } QString EditorModel::text() const { return m_text; } QString EditorModel::title() const { return m_title; } bool EditorModel::isDone() const { return m_done; } QDate EditorModel::startDate() const { return m_start; } QDate EditorModel::dueDate() const { return m_due; } Domain::Task::Recurrence EditorModel::recurrence() const { return m_recurrence; } QAbstractItemModel *EditorModel::attachmentModel() const { return m_attachmentModel; } int EditorModel::autoSaveDelay() { return s_autoSaveDelay; } void EditorModel::setAutoSaveDelay(int delay) { s_autoSaveDelay = delay; } bool EditorModel::editingInProgress() const { return m_editingInProgress; } void EditorModel::setText(const QString &text) { if (m_text == text) return; applyNewText(text); setSaveNeeded(true); } void EditorModel::setTitle(const QString &title) { if (m_title == title) return; applyNewTitle(title); setSaveNeeded(true); } void EditorModel::setDone(bool done) { if (m_done == done) return; applyNewDone(done); setSaveNeeded(true); } void EditorModel::setStartDate(const QDate &start) { if (m_start == start) return; applyNewStartDate(start); setSaveNeeded(true); } void EditorModel::setDueDate(const QDate &due) { if (m_due == due) return; applyNewDueDate(due); setSaveNeeded(true); } void EditorModel::setRecurrence(Domain::Task::Recurrence recurrence) { if (m_recurrence == recurrence) return; applyNewRecurrence(recurrence); setSaveNeeded(true); } void EditorModel::addAttachment(const QString &fileName) { if (!m_task) return; QMimeDatabase mimeDb; auto mimeType = mimeDb.mimeTypeForFile(fileName); auto attachment = Domain::Task::Attachment(); attachment.setLabel(QFileInfo(fileName).fileName()); attachment.setMimeType(mimeType.name()); attachment.setIconName(mimeType.iconName()); QFile file(fileName); if (!file.open(QFile::ReadOnly)) { // TODO: Might be worth extending error handling // to deal with job-less errors later on qWarning() << "Couldn't open" << fileName; return; } attachment.setData(file.readAll()); file.close(); auto attachments = m_task->attachments(); attachments.append(attachment); m_task->setAttachments(attachments); setSaveNeeded(true); } void EditorModel::removeAttachment(const QModelIndex &index) { if (!m_task) return; auto attachments = m_task->attachments(); attachments.removeAt(index.row()); m_task->setAttachments(attachments); setSaveNeeded(true); } void EditorModel::openAttachment(const QModelIndex &index) { Q_ASSERT(m_task); auto attachment = m_task->attachments().at(index.row()); auto uri = attachment.uri(); if (!attachment.isUri()) { auto tempFile = new QTemporaryFile(QDir::tempPath() + QStringLiteral("/zanshin_attachment_XXXXXX"), this); tempFile->open(); tempFile->setPermissions(QFile::ReadUser); tempFile->write(attachment.data()); tempFile->close(); uri = QUrl::fromLocalFile(tempFile->fileName()); } QDesktopServices::openUrl(uri); } void EditorModel::setEditingInProgress(bool editing) { m_editingInProgress = editing; } void EditorModel::onTextChanged(const QString &text) { if (!m_editingInProgress) applyNewText(text); } void EditorModel::onTitleChanged(const QString &title) { if (!m_editingInProgress) applyNewTitle(title); } void EditorModel::onDoneChanged(bool done) { if (!m_editingInProgress) applyNewDone(done); } void EditorModel::onStartDateChanged(const QDate &start) { if (!m_editingInProgress) applyNewStartDate(start); } void EditorModel::onDueDateChanged(const QDate &due) { if (!m_editingInProgress) applyNewDueDate(due); } void EditorModel::onRecurrenceChanged(Domain::Task::Recurrence recurrence) { if (!m_editingInProgress) applyNewRecurrence(recurrence); } void EditorModel::save() { if (!isSaveNeeded()) return; Q_ASSERT(m_task); const auto currentTitle = m_task->title(); m_task->setTitle(m_title); m_task->setText(m_text); m_task->setDone(m_done); m_task->setStartDate(m_start); m_task->setDueDate(m_due); m_task->setRecurrence(m_recurrence); const auto job = m_saveFunction(m_task); installHandler(job, i18n("Cannot modify task %1", currentTitle)); setSaveNeeded(false); } void EditorModel::setSaveNeeded(bool needed) { if (needed) m_saveTimer->start(autoSaveDelay()); else m_saveTimer->stop(); m_saveNeeded = needed; } bool EditorModel::isSaveNeeded() const { return m_saveNeeded; } void EditorModel::applyNewText(const QString &text) { m_text = text; emit textChanged(m_text); } void EditorModel::applyNewTitle(const QString &title) { m_title = title; emit titleChanged(m_title); } void EditorModel::applyNewDone(bool done) { m_done = done; emit doneChanged(m_done); } void EditorModel::applyNewStartDate(const QDate &start) { m_start = start; emit startDateChanged(m_start); } void EditorModel::applyNewDueDate(const QDate &due) { m_due = due; emit dueDateChanged(m_due); } void EditorModel::applyNewRecurrence(Domain::Task::Recurrence recurrence) { m_recurrence = recurrence; emit recurrenceChanged(m_recurrence); } #include "editormodel.moc" diff --git a/src/presentation/editormodel.h b/src/presentation/editormodel.h index 0a1b2615..fe28eb50 100644 --- a/src/presentation/editormodel.h +++ b/src/presentation/editormodel.h @@ -1,143 +1,143 @@ /* 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 PRESENTATION_EDITORMODEL_H #define PRESENTATION_EDITORMODEL_H #include #include #include #include "domain/task.h" #include "presentation/errorhandlingmodelbase.h" class QAbstractItemModel; class QTimer; namespace Presentation { class AttachmentModel; class EditorModel : public QObject, public ErrorHandlingModelBase { Q_OBJECT Q_PROPERTY(Domain::Task::Ptr task READ task WRITE setTask NOTIFY taskChanged) Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(bool done READ isDone WRITE setDone NOTIFY doneChanged) Q_PROPERTY(QDate startDate READ startDate WRITE setStartDate NOTIFY startDateChanged) Q_PROPERTY(QDate dueDate READ dueDate WRITE setDueDate NOTIFY dueDateChanged) Q_PROPERTY(Domain::Task::Recurrence recurrence READ recurrence WRITE setRecurrence NOTIFY recurrenceChanged) Q_PROPERTY(QAbstractItemModel* attachmentModel READ attachmentModel CONSTANT) Q_PROPERTY(bool editingInProgress READ editingInProgress WRITE setEditingInProgress) public: typedef std::function SaveFunction; - explicit EditorModel(QObject *parent = Q_NULLPTR); + explicit EditorModel(QObject *parent = nullptr); ~EditorModel(); Domain::Task::Ptr task() const; void setTask(const Domain::Task::Ptr &task); bool hasSaveFunction() const; void setSaveFunction(const SaveFunction &function); QString text() const; QString title() const; bool isDone() const; QDate startDate() const; QDate dueDate() const; Domain::Task::Recurrence recurrence() const; QAbstractItemModel *attachmentModel() const; static int autoSaveDelay(); static void setAutoSaveDelay(int delay); bool editingInProgress() const; public slots: void setText(const QString &text); void setTitle(const QString &title); void setDone(bool done); void setStartDate(const QDate &start); void setDueDate(const QDate &due); void setRecurrence(Domain::Task::Recurrence recurrence); void addAttachment(const QString &fileName); void removeAttachment(const QModelIndex &index); void openAttachment(const QModelIndex &index); void setEditingInProgress(bool editingInProgress); signals: void taskChanged(const Domain::Task::Ptr &task); void textChanged(const QString &text); void titleChanged(const QString &title); void doneChanged(bool done); void startDateChanged(const QDate &date); void dueDateChanged(const QDate &due); void recurrenceChanged(Domain::Task::Recurrence recurrence); private slots: void onTextChanged(const QString &text); void onTitleChanged(const QString &title); void onDoneChanged(bool done); void onStartDateChanged(const QDate &start); void onDueDateChanged(const QDate &due); void onRecurrenceChanged(Domain::Task::Recurrence recurrence); void save(); private: void setSaveNeeded(bool needed); bool isSaveNeeded() const; void applyNewText(const QString &text); void applyNewTitle(const QString &title); void applyNewDone(bool done); void applyNewStartDate(const QDate &start); void applyNewDueDate(const QDate &due); void applyNewRecurrence(Domain::Task::Recurrence recurrence); Domain::Task::Ptr m_task; SaveFunction m_saveFunction; QString m_text; QString m_title; bool m_done; QDate m_start; QDate m_due; Domain::Task::Recurrence m_recurrence; AttachmentModel *m_attachmentModel; QTimer *m_saveTimer; bool m_saveNeeded; bool m_editingInProgress; }; } #endif // PRESENTATION_EDITORMODEL_H diff --git a/src/presentation/errorhandlingmodelbase.cpp b/src/presentation/errorhandlingmodelbase.cpp index a70c327d..d1aafd42 100644 --- a/src/presentation/errorhandlingmodelbase.cpp +++ b/src/presentation/errorhandlingmodelbase.cpp @@ -1,51 +1,51 @@ /* This file is part of Zanshin Copyright 2014 Mario Bensi 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 "errorhandlingmodelbase.h" #include "presentation/errorhandler.h" using namespace Presentation; ErrorHandlingModelBase::ErrorHandlingModelBase() - : m_errorHandler(Q_NULLPTR) + : m_errorHandler(nullptr) { } ErrorHandler *ErrorHandlingModelBase::errorHandler() const { return m_errorHandler; } void ErrorHandlingModelBase::setErrorHandler(ErrorHandler *errorHandler) { m_errorHandler = errorHandler; } void ErrorHandlingModelBase::installHandler(KJob *job, const QString &message) { if (!m_errorHandler) return; m_errorHandler->installHandler(job, message); } diff --git a/src/presentation/inboxpagemodel.cpp b/src/presentation/inboxpagemodel.cpp index 6da645f0..becb2515 100644 --- a/src/presentation/inboxpagemodel.cpp +++ b/src/presentation/inboxpagemodel.cpp @@ -1,179 +1,179 @@ /* 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 "inboxpagemodel.h" #include #include #include "presentation/querytreemodel.h" using namespace Presentation; InboxPageModel::InboxPageModel(const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent) : PageModel(parent), m_taskQueries(taskQueries), m_taskRepository(taskRepository) { } Domain::Task::Ptr InboxPageModel::addItem(const QString &title, const QModelIndex &parentIndex) { const auto parentData = parentIndex.data(QueryTreeModelBase::ObjectRole); const auto parentTask = parentData.value(); auto task = Domain::Task::Ptr::create(); task->setTitle(title); const auto job = parentTask ? m_taskRepository->createChild(task, parentTask) : m_taskRepository->create(task); installHandler(job, i18n("Cannot add task %1 in Inbox", title)); return task; } void InboxPageModel::removeItem(const QModelIndex &index) { QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto task = data.value(); Q_ASSERT(task); const auto job = m_taskRepository->remove(task); installHandler(job, i18n("Cannot remove task %1 from Inbox", task->title())); } void InboxPageModel::promoteItem(const QModelIndex &index) { QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto task = data.value(); Q_ASSERT(task); const auto job = m_taskRepository->promoteToProject(task); installHandler(job, i18n("Cannot promote task %1 to be a project", task->title())); } QAbstractItemModel *InboxPageModel::createCentralListModel() { using AdditionalInfo = Domain::QueryResult::Ptr; auto query = [this](const Domain::Task::Ptr &task) -> Domain::QueryResultInterface::Ptr { if (!task) return m_taskQueries->findInboxTopLevel(); else return m_taskQueries->findChildren(task); }; auto flags = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled; }; auto data = [](const Domain::Task::Ptr &task, int role, const AdditionalInfo &dataSourceQueryResult) -> QVariant { switch (role) { case Qt::DisplayRole: case Qt::EditRole: return task->title(); case Qt::CheckStateRole: return task->isDone() ? Qt::Checked : Qt::Unchecked; case Presentation::QueryTreeModelBase::AdditionalInfoRole: if (dataSourceQueryResult && !dataSourceQueryResult->data().isEmpty()) { Domain::DataSource::Ptr dataSource = dataSourceQueryResult->data().at(0); return dataSource->name(); } return QString(); default: break; } return QVariant(); }; auto fetchAdditionalInfo = [this](const QModelIndex &index, const Domain::Task::Ptr &task) -> AdditionalInfo { if (index.parent().isValid()) // children are in the same collection as their parent, so the same datasource return nullptr; AdditionalInfo datasourceQueryResult = m_taskQueries->findDataSource(task); if (datasourceQueryResult) { QPersistentModelIndex persistentIndex(index); datasourceQueryResult->addPostInsertHandler([persistentIndex](const Domain::DataSource::Ptr &, int) { // When a datasource was found (inserted into the result), update the rendering of the item auto model = const_cast(persistentIndex.model()); model->dataChanged(persistentIndex, persistentIndex); }); } return datasourceQueryResult; }; auto setData = [this](const Domain::Task::Ptr &task, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) { return false; } const auto currentTitle = task->title(); if (role == Qt::EditRole) task->setTitle(value.toString()); else task->setDone(value.toInt() == Qt::Checked); const auto job = m_taskRepository->update(task); installHandler(job, i18n("Cannot modify task %1 in Inbox", currentTitle)); return true; }; auto drop = [this](const QMimeData *mimeData, Qt::DropAction, const Domain::Task::Ptr &parentTask) { if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))) return false; auto droppedTasks = mimeData->property("objects").value(); if (droppedTasks.isEmpty()) return false; foreach(const auto &childTask, droppedTasks) { if (parentTask) { const auto job = m_taskRepository->associate(parentTask, childTask); installHandler(job, i18n("Cannot move task %1 as sub-task of %2", childTask->title(), parentTask->title())); } else { const auto job = m_taskRepository->dissociate(childTask); installHandler(job, i18n("Cannot deparent task %1 from its parent", childTask->title())); } } return true; }; auto drag = [](const Domain::Task::List &tasks) -> QMimeData* { if (tasks.isEmpty()) - return Q_NULLPTR; + return nullptr; auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(tasks)); return data; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, fetchAdditionalInfo, this); } diff --git a/src/presentation/inboxpagemodel.h b/src/presentation/inboxpagemodel.h index 04b439db..37f3ca7e 100644 --- a/src/presentation/inboxpagemodel.h +++ b/src/presentation/inboxpagemodel.h @@ -1,56 +1,56 @@ /* 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 PRESENTATION_INBOXPAGEMODEL_H #define PRESENTATION_INBOXPAGEMODEL_H #include "presentation/pagemodel.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" namespace Presentation { class InboxPageModel : public PageModel { Q_OBJECT public: explicit InboxPageModel(const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, - QObject *parent = Q_NULLPTR); + QObject *parent = nullptr); Domain::Task::Ptr addItem(const QString &title, const QModelIndex &parentIndex = QModelIndex()) override; void removeItem(const QModelIndex &index) override; void promoteItem(const QModelIndex &index) override; private: QAbstractItemModel *createCentralListModel() override; Domain::TaskQueries::Ptr m_taskQueries; Domain::TaskRepository::Ptr m_taskRepository; }; } #endif // PRESENTATION_INBOXPAGEMODEL_H diff --git a/src/presentation/pagemodel.cpp b/src/presentation/pagemodel.cpp index b6231397..1609225a 100644 --- a/src/presentation/pagemodel.cpp +++ b/src/presentation/pagemodel.cpp @@ -1,40 +1,40 @@ /* 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 "pagemodel.h" using namespace Presentation; PageModel::PageModel(QObject *parent) : QObject(parent), - m_centralListModel(Q_NULLPTR) + m_centralListModel(nullptr) { } QAbstractItemModel *PageModel::centralListModel() { if (!m_centralListModel) m_centralListModel = createCentralListModel(); return m_centralListModel; } diff --git a/src/presentation/pagemodel.h b/src/presentation/pagemodel.h index 7974ea05..1eb5eaeb 100644 --- a/src/presentation/pagemodel.h +++ b/src/presentation/pagemodel.h @@ -1,61 +1,61 @@ /* 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 PRESENTATION_PAGEMODEL_H #define PRESENTATION_PAGEMODEL_H #include #include #include "domain/task.h" #include "presentation/metatypes.h" #include "presentation/errorhandlingmodelbase.h" namespace Presentation { class PageModel : public QObject, public ErrorHandlingModelBase { Q_OBJECT Q_PROPERTY(QAbstractItemModel* centralListModel READ centralListModel) public: - explicit PageModel(QObject *parent = Q_NULLPTR); + explicit PageModel(QObject *parent = nullptr); QAbstractItemModel *centralListModel(); public slots: virtual Domain::Task::Ptr addItem(const QString &title, const QModelIndex &parentIndex = QModelIndex()) = 0; virtual void removeItem(const QModelIndex &index) = 0; virtual void promoteItem(const QModelIndex &index) = 0; private: virtual QAbstractItemModel *createCentralListModel() = 0; QAbstractItemModel *m_centralListModel; }; } #endif // PRESENTATION_PAGEMODEL_H diff --git a/src/presentation/projectpagemodel.cpp b/src/presentation/projectpagemodel.cpp index f561669d..cd833c06 100644 --- a/src/presentation/projectpagemodel.cpp +++ b/src/presentation/projectpagemodel.cpp @@ -1,175 +1,175 @@ /* 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 "projectpagemodel.h" #include #include #include "presentation/querytreemodel.h" using namespace Presentation; ProjectPageModel::ProjectPageModel(const Domain::Project::Ptr &project, const Domain::ProjectQueries::Ptr &projectQueries, const Domain::ProjectRepository::Ptr &projectRepository, const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent) : PageModel(parent), m_projectQueries(projectQueries), m_projectRepository(projectRepository), m_project(project), m_taskQueries(taskQueries), m_taskRepository(taskRepository) { } Domain::Project::Ptr ProjectPageModel::project() const { return m_project; } Domain::Task::Ptr ProjectPageModel::addItem(const QString &title, const QModelIndex &parentIndex) { const auto parentData = parentIndex.data(QueryTreeModelBase::ObjectRole); const auto parentTask = parentData.value(); auto task = Domain::Task::Ptr::create(); task->setTitle(title); const auto job = parentTask ? m_taskRepository->createChild(task, parentTask) : m_taskRepository->createInProject(task, m_project); installHandler(job, i18n("Cannot add task %1 in project %2", title, m_project->name())); return task; } void ProjectPageModel::removeItem(const QModelIndex &index) { QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto task = data.value(); Q_ASSERT(task); const auto job = m_taskRepository->remove(task); installHandler(job, i18n("Cannot remove task %1 from project %2", task->title(), m_project->name())); } void ProjectPageModel::promoteItem(const QModelIndex &index) { QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto task = data.value(); Q_ASSERT(task); const auto job = m_taskRepository->promoteToProject(task); installHandler(job, i18n("Cannot promote task %1 to be a project", task->title())); } QAbstractItemModel *ProjectPageModel::createCentralListModel() { auto query = [this](const Domain::Task::Ptr &task) -> Domain::QueryResultInterface::Ptr { if (!task) return m_projectQueries->findTopLevel(m_project); else return m_taskQueries->findChildren(task); }; auto flags = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled; }; auto data = [](const Domain::Task::Ptr &task, int role, int) -> QVariant { if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { return task->title(); } else { return task->isDone() ? Qt::Checked : Qt::Unchecked; } }; auto setData = [this](const Domain::Task::Ptr &task, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) { return false; } const auto currentTitle = task->title(); if (role == Qt::EditRole) task->setTitle(value.toString()); else task->setDone(value.toInt() == Qt::Checked); const auto job = m_taskRepository->update(task); installHandler(job, i18n("Cannot modify task %1 in project %2", currentTitle, m_project->name())); return true; }; auto drop = [this](const QMimeData *mimeData, Qt::DropAction, const Domain::Task::Ptr &parentTask) { if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))) return false; auto droppedTasks = mimeData->property("objects").value(); if (droppedTasks.isEmpty()) return false; using namespace std::placeholders; auto associate = std::function(); auto parentTitle = QString(); if (parentTask) { associate = std::bind(&Domain::TaskRepository::associate, m_taskRepository, parentTask, _1); parentTitle = parentTask->title(); } else { associate = std::bind(&Domain::ProjectRepository::associate, m_projectRepository, m_project, _1); parentTitle = m_project->name(); } foreach(const Domain::Task::Ptr &childTask, droppedTasks) { const auto job = associate(childTask); installHandler(job, i18n("Cannot move task %1 as a sub-task of %2", childTask->title(), parentTitle)); } return true; }; auto drag = [](const Domain::Task::List &tasks) -> QMimeData* { if (tasks.isEmpty()) - return Q_NULLPTR; + return nullptr; auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(tasks)); return data; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, nullptr, this); } diff --git a/src/presentation/projectpagemodel.h b/src/presentation/projectpagemodel.h index bad9c4dd..56d97a1a 100644 --- a/src/presentation/projectpagemodel.h +++ b/src/presentation/projectpagemodel.h @@ -1,67 +1,67 @@ /* 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 PRESENTATION_PROJECTPAGEMODEL_H #define PRESENTATION_PROJECTPAGEMODEL_H #include "presentation/pagemodel.h" #include "domain/projectqueries.h" #include "domain/projectrepository.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" namespace Presentation { class ProjectPageModel : public PageModel { Q_OBJECT public: explicit ProjectPageModel(const Domain::Project::Ptr &project, const Domain::ProjectQueries::Ptr &projectQueries, const Domain::ProjectRepository::Ptr &projectRepository, const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, - QObject *parent = Q_NULLPTR); + QObject *parent = nullptr); Domain::Project::Ptr project() const; Domain::Task::Ptr addItem(const QString &title, const QModelIndex &parentIndex = QModelIndex()) override; void removeItem(const QModelIndex &index) override; void promoteItem(const QModelIndex &index) override; private: QAbstractItemModel *createCentralListModel() override; Domain::ProjectQueries::Ptr m_projectQueries; Domain::ProjectRepository::Ptr m_projectRepository; Domain::Project::Ptr m_project; Domain::TaskQueries::Ptr m_taskQueries; Domain::TaskRepository::Ptr m_taskRepository; }; } #endif // PRESENTATION_PROJECTPAGEMODEL_H diff --git a/src/presentation/querytreemodel.h b/src/presentation/querytreemodel.h index 68382b41..9d8a48db 100644 --- a/src/presentation/querytreemodel.h +++ b/src/presentation/querytreemodel.h @@ -1,122 +1,122 @@ /* This file is part of Zanshin Copyright 2014 Mario Bensi 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 PRESENTATION_QUERYTREEMODEL_H #define PRESENTATION_QUERYTREEMODEL_H #include "querytreenode.h" #include #include namespace Presentation { template class QueryTreeModel : public QueryTreeModelBase { public: typedef typename QueryTreeNode::QueryGenerator QueryGenerator; typedef typename QueryTreeNode::FlagsFunction FlagsFunction; typedef typename QueryTreeNode::DataFunction DataFunction; typedef typename QueryTreeNode::SetDataFunction SetDataFunction; typedef typename QueryTreeNode::DropFunction DropFunction; typedef std::function &)> DragFunction; using FetchAdditionalInfoFunction = std::function; using NodeType = QueryTreeNode; explicit QueryTreeModel(const QueryGenerator &queryGenerator, const FlagsFunction &flagsFunction, const DataFunction &dataFunction, const SetDataFunction &setDataFunction, - QObject *parent = Q_NULLPTR) - : QueryTreeModelBase(new QueryTreeNode(ItemType(), Q_NULLPTR, this, + QObject *parent = nullptr) + : QueryTreeModelBase(new QueryTreeNode(ItemType(), nullptr, this, queryGenerator, flagsFunction, dataFunction, setDataFunction), parent) { } explicit QueryTreeModel(const QueryGenerator &queryGenerator, const FlagsFunction &flagsFunction, const DataFunction &dataFunction, const SetDataFunction &setDataFunction, const DropFunction &dropFunction, const DragFunction &dragFunction, const FetchAdditionalInfoFunction &fetchAdditionalInfoFunction, - QObject *parent = Q_NULLPTR) - : QueryTreeModelBase(new QueryTreeNode(ItemType(), Q_NULLPTR, this, + QObject *parent = nullptr) + : QueryTreeModelBase(new QueryTreeNode(ItemType(), nullptr, this, queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction), parent), m_dragFunction(dragFunction), m_fetchAdditionalInfoFunction(fetchAdditionalInfoFunction) { } protected: QMimeData *createMimeData(const QModelIndexList &indexes) const override { if (m_dragFunction) { QList items; std::transform(indexes.begin(), indexes.end(), std::back_inserter(items), [this](const QModelIndex &index) { return itemAtIndex(index); }); return m_dragFunction(items); } else { - return Q_NULLPTR; + return nullptr; } } void fetchAdditionalInfo(const QModelIndex &index) override { if (m_fetchAdditionalInfoFunction) { auto theNode = node(index); if (!theNode->hasAdditionalInfo()) theNode->setAdditionalInfo(m_fetchAdditionalInfoFunction(index, theNode->item())); } } ItemType itemAtIndex(const QModelIndex &index) const { return node(index)->item(); } NodeType *node(const QModelIndex &index) const { return static_cast(nodeFromIndex(index)); } private: DragFunction m_dragFunction; FetchAdditionalInfoFunction m_fetchAdditionalInfoFunction; }; } #endif // PRESENTATION_QUERYTREEMODEL_H diff --git a/src/presentation/querytreemodelbase.cpp b/src/presentation/querytreemodelbase.cpp index e1b30239..b2315140 100644 --- a/src/presentation/querytreemodelbase.cpp +++ b/src/presentation/querytreemodelbase.cpp @@ -1,269 +1,269 @@ /* This file is part of Zanshin Copyright 2014 Mario Bensi 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 "querytreemodelbase.h" #include #include #include using namespace Presentation; QueryTreeNodeBase::QueryTreeNodeBase(QueryTreeNodeBase *parent, QueryTreeModelBase *model) : m_parent(parent), m_model(model) { } QueryTreeNodeBase::~QueryTreeNodeBase() { qDeleteAll(m_childNode); } int QueryTreeNodeBase::row() { return m_parent ? m_parent->m_childNode.indexOf(this) : -1; } QueryTreeNodeBase *QueryTreeNodeBase::parent() const { return m_parent; } QueryTreeNodeBase *QueryTreeNodeBase::child(int row) const { if (row >= 0 && row < m_childNode.size()) return m_childNode.value(row); else - return Q_NULLPTR; + return nullptr; } void QueryTreeNodeBase::insertChild(int row, QueryTreeNodeBase *node) { m_childNode.insert(row, node); } void QueryTreeNodeBase::appendChild(QueryTreeNodeBase *node) { m_childNode.append(node); } void QueryTreeNodeBase::removeChildAt(int row) { delete m_childNode.takeAt(row); } int QueryTreeNodeBase::childCount() const { return m_childNode.size(); } QModelIndex QueryTreeNodeBase::index(int row, int column, const QModelIndex &parent) const { return m_model->index(row, column, parent); } QModelIndex QueryTreeNodeBase::createIndex(int row, int column, void *data) const { return m_model->createIndex(row, column, data); } void QueryTreeNodeBase::beginInsertRows(const QModelIndex &parent, int first, int last) { m_model->beginInsertRows(parent, first, last); } void QueryTreeNodeBase::endInsertRows() { m_model->endInsertRows(); } void QueryTreeNodeBase::beginRemoveRows(const QModelIndex &parent, int first, int last) { m_model->beginRemoveRows(parent, first, last); } void QueryTreeNodeBase::endRemoveRows() { m_model->endRemoveRows(); } void QueryTreeNodeBase::emitDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { emit m_model->dataChanged(topLeft, bottomRight); } QueryTreeModelBase::QueryTreeModelBase(QueryTreeNodeBase *rootNode, QObject *parent) : QAbstractItemModel(parent), m_rootIndexFlag(Qt::ItemIsDropEnabled), m_rootNode(rootNode) { auto roles = roleNames(); roles.insert(ObjectRole, "object"); roles.insert(IconNameRole, "icon"); roles.insert(IsDefaultRole, "default"); setRoleNames(roles); } QueryTreeModelBase::~QueryTreeModelBase() { delete m_rootNode; } Qt::ItemFlags QueryTreeModelBase::flags(const QModelIndex &index) const { if (!isModelIndexValid(index)) return m_rootIndexFlag; return nodeFromIndex(index)->flags(); } QModelIndex QueryTreeModelBase::index(int row, int column, const QModelIndex &parent) const { if (row < 0 || column != 0) return QModelIndex(); const QueryTreeNodeBase *parentNode = nodeFromIndex(parent); if (row < parentNode->childCount()) { QueryTreeNodeBase *node = parentNode->child(row); return createIndex(row, column, node); } else { return QModelIndex(); } } QModelIndex QueryTreeModelBase::parent(const QModelIndex &index) const { QueryTreeNodeBase *node = nodeFromIndex(index); if (!node->parent() || node->parent() == m_rootNode) return QModelIndex(); else return createIndex(node->parent()->row(), 0, node->parent()); } int QueryTreeModelBase::rowCount(const QModelIndex &index) const { return nodeFromIndex(index)->childCount(); } int QueryTreeModelBase::columnCount(const QModelIndex &) const { return 1; } QVariant QueryTreeModelBase::data(const QModelIndex &index, int role) const { if (!isModelIndexValid(index)) { return QVariant(); } const_cast(this)->fetchAdditionalInfo(index); return nodeFromIndex(index)->data(role); } bool QueryTreeModelBase::setData(const QModelIndex &index, const QVariant &value, int role) { if (!isModelIndexValid(index)) { return false; } return nodeFromIndex(index)->setData(value, role); } bool QueryTreeModelBase::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(row); Q_UNUSED(column); // If that's not holding that mime type we can't do the cycle checking // this is relevant only for internal drag and drop anyway if (data->hasFormat(QStringLiteral("application/x-zanshin-indexes"))) { const auto indexes = data->property("indexes").value(); foreach (const auto &index, indexes) { auto p = parent; while (p.isValid()) { if (p == index) // Oops, we found a cycle (one of the indexes is parent of the drop point) return false; p = p.parent(); } } } return nodeFromIndex(parent)->dropMimeData(data, action); } QMimeData *QueryTreeModelBase::mimeData(const QModelIndexList &indexes) const { if (indexes.isEmpty()) - return Q_NULLPTR; + return nullptr; auto data = createMimeData(indexes); data->setData(QStringLiteral("application/x-zanshin-indexes"), "indexes"); data->setProperty("indexes", QVariant::fromValue(indexes)); return data; } QStringList QueryTreeModelBase::mimeTypes() const { return QAbstractItemModel::mimeTypes() << QStringLiteral("application/x-zanshin-object") << QStringLiteral("application/x-zanshin-indexes"); } Qt::DropActions QueryTreeModelBase::supportedDragActions() const { return Qt::MoveAction; } Qt::DropActions QueryTreeModelBase::supportedDropActions() const { return Qt::MoveAction; } QueryTreeNodeBase *QueryTreeModelBase::nodeFromIndex(const QModelIndex &index) const { return index.isValid() ? static_cast(index.internalPointer()) : m_rootNode; } void QueryTreeModelBase::setRootIndexFlag(Qt::ItemFlags flags) { m_rootIndexFlag = flags; } bool QueryTreeModelBase::isModelIndexValid(const QModelIndex &index) const { bool valid = index.isValid() && index.column() == 0 && index.row() >= 0; if (!valid) return false; const QueryTreeNodeBase *parentNode = nodeFromIndex(index.parent()); const int count = parentNode->childCount(); return index.row() < count; } diff --git a/src/presentation/querytreemodelbase.h b/src/presentation/querytreemodelbase.h index c2b9fae7..a741e3a6 100644 --- a/src/presentation/querytreemodelbase.h +++ b/src/presentation/querytreemodelbase.h @@ -1,114 +1,114 @@ /* This file is part of Zanshin Copyright 2014 Mario Bensi 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 PRESENTATION_QUERYTREEMODELBASE_H #define PRESENTATION_QUERYTREEMODELBASE_H #include namespace Presentation { class QueryTreeModelBase; class QueryTreeNodeBase { public: QueryTreeNodeBase(QueryTreeNodeBase *parent, QueryTreeModelBase *model); virtual ~QueryTreeNodeBase(); virtual Qt::ItemFlags flags() const = 0; virtual QVariant data(int role) const = 0; virtual bool setData(const QVariant &value, int role) = 0; virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action) = 0; int row(); QueryTreeNodeBase *parent() const; QueryTreeNodeBase *child(int row) const; void insertChild(int row, QueryTreeNodeBase *node); void appendChild(QueryTreeNodeBase *node); void removeChildAt(int row); int childCount() const; protected: QModelIndex index(int row, int column, const QModelIndex &parent) const; QModelIndex createIndex(int row, int column, void *data) const; void beginInsertRows(const QModelIndex &parent, int first, int last); void endInsertRows(); void beginRemoveRows(const QModelIndex &parent, int first, int last); void endRemoveRows(); void emitDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); private: QueryTreeNodeBase *m_parent; QList m_childNode; QueryTreeModelBase *m_model; }; class QueryTreeModelBase : public QAbstractItemModel { Q_OBJECT public: enum { ObjectRole = Qt::UserRole + 1, IconNameRole, IsDefaultRole, AdditionalInfoRole, UserRole }; ~QueryTreeModelBase(); Qt::ItemFlags flags(const QModelIndex &index) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; QMimeData *mimeData(const QModelIndexList &indexes) const override; QStringList mimeTypes() const override; Qt::DropActions supportedDragActions() const override; Qt::DropActions supportedDropActions() const override; protected: explicit QueryTreeModelBase(QueryTreeNodeBase *rootNode, - QObject *parent = Q_NULLPTR); + QObject *parent = nullptr); virtual QMimeData *createMimeData(const QModelIndexList &indexes) const = 0; virtual void fetchAdditionalInfo(const QModelIndex &) {} QueryTreeNodeBase *nodeFromIndex(const QModelIndex &index) const; void setRootIndexFlag(Qt::ItemFlags flags); private: friend class QueryTreeNodeBase; bool isModelIndexValid(const QModelIndex &index) const; Qt::ItemFlags m_rootIndexFlag; QueryTreeNodeBase *m_rootNode; }; } #endif // PRESENTATION_QUERYTREEMODELBASE_H diff --git a/src/presentation/taskfilterproxymodel.h b/src/presentation/taskfilterproxymodel.h index acfdf1dd..08e64534 100644 --- a/src/presentation/taskfilterproxymodel.h +++ b/src/presentation/taskfilterproxymodel.h @@ -1,63 +1,63 @@ /* 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 PRESENTATION_TASKFILTERPROXYMODEL_H #define PRESENTATION_TASKFILTERPROXYMODEL_H #include namespace Presentation { class TaskFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT Q_ENUMS(SortType) public: enum SortType { TitleSort = 0, DateSort }; - explicit TaskFilterProxyModel(QObject *parent = Q_NULLPTR); + explicit TaskFilterProxyModel(QObject *parent = nullptr); SortType sortType() const; void setSortType(SortType type); void setSortOrder(Qt::SortOrder order); bool showFutureTasks() const; void setShowFutureTasks(bool show); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; private: SortType m_sortType; bool m_showFuture; }; } #endif // PRESENTATION_TASKFILTERPROXYMODEL_H diff --git a/src/presentation/workdaypagemodel.h b/src/presentation/workdaypagemodel.h index c3430d94..41b250ed 100644 --- a/src/presentation/workdaypagemodel.h +++ b/src/presentation/workdaypagemodel.h @@ -1,56 +1,56 @@ /* This file is part of Zanshin Copyright 2015 Theo Vaucher 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 PRESENTATION_WORKDAYPAGEMODEL_H #define PRESENTATION_WORKDAYPAGEMODEL_H #include "presentation/pagemodel.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" namespace Presentation { class WorkdayPageModel : public PageModel { Q_OBJECT public: explicit WorkdayPageModel(const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, - QObject *parent = Q_NULLPTR); + QObject *parent = nullptr); Domain::Task::Ptr addItem(const QString &title, const QModelIndex &parentIndex = QModelIndex()) override; void removeItem(const QModelIndex &index) override; void promoteItem(const QModelIndex &index) override; private: QAbstractItemModel *createCentralListModel() override; Domain::TaskQueries::Ptr m_taskQueries; Domain::TaskRepository::Ptr m_taskRepository; }; } #endif // PRESENTATION_WORKDAYPAGEMODEL_H diff --git a/src/scripting/scripthandler.h b/src/scripting/scripthandler.h index 9a01fc67..8682e05c 100644 --- a/src/scripting/scripthandler.h +++ b/src/scripting/scripthandler.h @@ -1,55 +1,55 @@ /* This file is part of Zanshin Copyright 2015 Theo Vaucher 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 SCRIPTING_SCRIPTHANDLER_H #define SCRIPTING_SCRIPTHANDLER_H #include #include #include "domain/taskrepository.h" namespace Scripting { class ScriptHandler : public QObject { Q_OBJECT public: - explicit ScriptHandler(const Domain::TaskRepository::Ptr &taskRepository, QObject *parent = Q_NULLPTR); + explicit ScriptHandler(const Domain::TaskRepository::Ptr &taskRepository, QObject *parent = nullptr); QJSValue evaluateFile(const QString &filename); QJSValue evaluateString(const QString &string); QJSEngine *engine(); private: Domain::TaskRepository::Ptr m_taskRepository; QJSEngine *m_engine; }; } #endif // SCRIPTING_SCRIPTHANDLER_H diff --git a/src/scripting/taskaction.h b/src/scripting/taskaction.h index 3b4596f7..a45831ea 100644 --- a/src/scripting/taskaction.h +++ b/src/scripting/taskaction.h @@ -1,50 +1,50 @@ /* This file is part of Zanshin Copyright 2015 Theo Vaucher 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 SCRIPTING_TASKACTION_H #define SCRIPTING_TASKACTION_H #include #include "domain/taskrepository.h" #include "presentation/errorhandlingmodelbase.h" namespace Scripting { class TaskAction : public QObject, public Presentation::ErrorHandlingModelBase { Q_OBJECT public: - explicit TaskAction(const Domain::TaskRepository::Ptr &taskRepository, QObject *parent = Q_NULLPTR); + explicit TaskAction(const Domain::TaskRepository::Ptr &taskRepository, QObject *parent = nullptr); public slots: private: Domain::TaskRepository::Ptr m_taskRepository; }; } #endif // SCRIPTING_TASKACTION_H diff --git a/src/utils/compositejob.h b/src/utils/compositejob.h index 73719ab7..639850e3 100644 --- a/src/utils/compositejob.h +++ b/src/utils/compositejob.h @@ -1,51 +1,51 @@ /* This file is part of Zanshin Copyright 2014 Mario Bensi 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 COMPOSITE_JOB #define COMPOSITE_JOB #include "kcompositejob.h" #include "jobhandler.h" namespace Utils { class CompositeJob : public KCompositeJob { Q_OBJECT public: - explicit CompositeJob(QObject *parent = Q_NULLPTR); + explicit CompositeJob(QObject *parent = nullptr); using KCompositeJob::addSubjob; virtual void start() override; virtual bool install(KJob *job, const JobHandler::ResultHandlerWithJob &handler); virtual bool install(KJob *job, const JobHandler::ResultHandler &handler); void emitError(const QString &errorText); private slots: virtual void slotResult(KJob *job) override; }; } #endif diff --git a/src/utils/dependencymanager.h b/src/utils/dependencymanager.h index 5b7aa52a..6000b4ee 100644 --- a/src/utils/dependencymanager.h +++ b/src/utils/dependencymanager.h @@ -1,559 +1,559 @@ /* 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 UTILS_DEPENDENCYMANAGER_H #define UTILS_DEPENDENCYMANAGER_H #include #include #include #include #ifdef Q_COMPILER_LAMBDA #include #endif namespace Utils { class DependencyManager; namespace Internal { template class Provider { public: #ifdef Q_COMPILER_LAMBDA typedef std::function FactoryType; typedef std::function(FactoryType, DependencyManager*)> PolicyType; #else typedef Iface *(*FactoryType)(DependencyManager*); typedef QSharedPointer (*PolicyType)(FactoryType, DependencyManager*); #endif Provider() - : m_factory(Q_NULLPTR), - m_policy(Q_NULLPTR) + : m_factory(nullptr), + m_policy(nullptr) { } Provider(FactoryType factory, PolicyType policy) : m_factory(factory), m_policy(policy) { } Provider(const Provider &other) : m_factory(other.m_factory), m_policy(other.m_policy) { } ~Provider() { } Provider &operator=(const Provider &other) { Provider tmp(other); std::swap(m_factory, tmp.m_factory); std::swap(m_policy, tmp.m_policy); return *this; } QSharedPointer operator()(DependencyManager *deps) const { - Q_ASSERT(m_factory != Q_NULLPTR); + Q_ASSERT(m_factory != nullptr); return m_policy(m_factory, deps); } private: FactoryType m_factory; PolicyType m_policy; }; template class Supplier { public: static void setProvider(DependencyManager *manager, const Provider &provider) { s_providers.insert(manager, provider); } static QSharedPointer create(DependencyManager *manager) { return s_providers.value(manager)(manager); } static int providersCount() { return s_providers.size(); } static void removeProvider(DependencyManager *manager) { s_providers.remove(manager); } private: Supplier(); static QHash< DependencyManager*, Provider > s_providers; }; template QHash< DependencyManager*, Provider > Supplier::s_providers; struct MultipleInstancesPolicy { template static typename Provider::PolicyType function(Iface *) { return create; } template static QSharedPointer create(typename Provider::FactoryType factory, DependencyManager *deps) { return QSharedPointer(factory(deps)); } }; struct UniqueInstancePolicy { template static typename Provider::PolicyType function(Iface *) { return create; } template static QSharedPointer create(typename Provider::FactoryType factory, DependencyManager *deps) { static QWeakPointer weakRef; QSharedPointer instance = weakRef.toStrongRef(); if (!instance) { instance = QSharedPointer(factory(deps)); weakRef = instance; } return instance; } }; } class DependencyManager { public: typedef Internal::MultipleInstancesPolicy MultipleInstances; typedef Internal::UniqueInstancePolicy UniqueInstance; static DependencyManager &globalInstance(); DependencyManager(); DependencyManager(const DependencyManager &other); ~DependencyManager(); DependencyManager &operator=(const DependencyManager &other); template void add(const typename Internal::Provider::FactoryType &factory) { - Iface * const val = Q_NULLPTR; + Iface * const val = nullptr; Internal::Provider provider(factory, InstancePolicy::function(val)); Internal::Supplier::setProvider(this, provider); m_cleanupFunctions << Internal::Supplier::removeProvider; } template void add(const typename Internal::Provider::FactoryType &factory) { add(factory); } template void add() { add(DependencyManager::FactoryHelper::create); } template void add() { add(); } template QSharedPointer create() { return Internal::Supplier::create(this); } private: QList m_cleanupFunctions; template class FactoryHelper { public: static Iface *create(DependencyManager *) { return new Impl; } }; #ifdef Q_COMPILER_VARIADIC_TEMPLATES template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl((manager->create())...); } }; #else template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create(), manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create()); } }; template class FactoryHelper { public: static Iface *create(DependencyManager *manager) { return new Impl(manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create(), manager->create()); } }; #endif }; } #endif // UTILS_DEPENDENCYMANAGER_H diff --git a/src/widgets/applicationcomponents.cpp b/src/widgets/applicationcomponents.cpp index 7e8d9c72..d476a418 100644 --- a/src/widgets/applicationcomponents.cpp +++ b/src/widgets/applicationcomponents.cpp @@ -1,305 +1,305 @@ /* 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 "applicationcomponents.h" #include #include #include #include #include #include #include #include #include "availablepagesview.h" #include "availablesourcesview.h" #include "editorview.h" #include "pageview.h" #include "pageviewerrorhandler.h" #include "quickselectdialog.h" #include "runningtaskwidget.h" #include "presentation/runningtaskmodelinterface.h" using namespace Widgets; ApplicationComponents::ApplicationComponents(QWidget *parent) : QObject(parent), m_parent(parent), - m_availableSourcesView(Q_NULLPTR), - m_availablePagesView(Q_NULLPTR), - m_pageView(Q_NULLPTR), - m_editorView(Q_NULLPTR), + m_availableSourcesView(nullptr), + m_availablePagesView(nullptr), + m_pageView(nullptr), + m_editorView(nullptr), m_errorHandler(new PageViewErrorHandler) { m_quickSelectDialogFactory = [] (QWidget *parent) { return QuickSelectDialogPtr(new QuickSelectDialog(parent)); }; auto moveItemAction = new QAction(this); moveItemAction->setObjectName(QStringLiteral("moveItemAction")); moveItemAction->setText(i18n("Move Task")); moveItemAction->setShortcut(Qt::Key_M); connect(moveItemAction, &QAction::triggered, this, &ApplicationComponents::onMoveItemsRequested); m_actions.insert(QStringLiteral("page_view_move"), moveItemAction); } ApplicationComponents::~ApplicationComponents() { setModel({}); } QHash ApplicationComponents::globalActions() const { auto actions = QHash(); actions.unite(availableSourcesView()->globalActions()); actions.unite(availablePagesView()->globalActions()); actions.unite(pageView()->globalActions()); actions.unite(m_actions); return actions; } QObjectPtr ApplicationComponents::model() const { return m_model; } AvailableSourcesView *ApplicationComponents::availableSourcesView() const { if (!m_availableSourcesView) { auto availableSourcesView = new AvailableSourcesView(m_parent); if (m_model) { availableSourcesView->setModel(m_model->property("availableSources").value()); } ApplicationComponents *self = const_cast(this); self->m_availableSourcesView = availableSourcesView; } return m_availableSourcesView; } AvailablePagesView *ApplicationComponents::availablePagesView() const { if (!m_availablePagesView) { auto availablePagesView = new AvailablePagesView(m_parent); if (m_model) { availablePagesView->setModel(m_model->property("availablePages").value()); auto availableSources = m_model->property("availableSources").value(); if (availableSources) availablePagesView->setProjectSourcesModel(availableSources->property("sourceListModel").value()); } ApplicationComponents *self = const_cast(this); self->m_availablePagesView = availablePagesView; connect(self->m_availablePagesView, &AvailablePagesView::currentPageChanged, self, &ApplicationComponents::onCurrentPageChanged); } return m_availablePagesView; } PageView *ApplicationComponents::pageView() const { if (!m_pageView) { auto pageView = new PageView(m_parent); if (m_model) { pageView->setModel(m_model->property("currentPage").value()); pageView->setRunningTaskModel(m_model->property("runningTaskModel").value()); connect(m_model.data(), SIGNAL(currentPageChanged(QObject*)), pageView, SLOT(setModel(QObject*))); } ApplicationComponents *self = const_cast(this); self->m_pageView = pageView; self->m_errorHandler->setPageView(pageView); connect(self->m_pageView, &PageView::currentTaskChanged, self, &ApplicationComponents::onCurrentTaskChanged); } return m_pageView; } EditorView *ApplicationComponents::editorView() const { if (!m_editorView) { auto editorView = new EditorView(m_parent); if (m_model) { editorView->setModel(m_model->property("editor").value()); } auto self = const_cast(this); self->m_editorView = editorView; } return m_editorView; } RunningTaskWidget *ApplicationComponents::runningTaskView() const { if (!m_runningTaskView) { auto runningTaskView = new RunningTaskWidget(m_parent); if (m_model) { runningTaskView->setModel(m_model->property("runningTaskModel").value()); } auto self = const_cast(this); self->m_runningTaskView = runningTaskView; } return m_runningTaskView; } ApplicationComponents::QuickSelectDialogFactory ApplicationComponents::quickSelectDialogFactory() const { return m_quickSelectDialogFactory; } void ApplicationComponents::setModel(const QObjectPtr &model) { if (m_model == model) return; if (m_model) { if (m_pageView) disconnect(m_model.data(), 0, m_pageView, 0); m_model->setProperty("errorHandler", 0); } // Delay deletion of the old model until we're out of scope auto tmp = m_model; Q_UNUSED(tmp); m_model = model; if (m_model) { m_model->setProperty("errorHandler", QVariant::fromValue(errorHandler())); } if (m_availableSourcesView) { m_availableSourcesView->setModel(m_model ? m_model->property("availableSources").value() - : Q_NULLPTR); + : nullptr); } if (m_availablePagesView) { m_availablePagesView->setModel(m_model ? m_model->property("availablePages").value() - : Q_NULLPTR); + : nullptr); m_availablePagesView->setProjectSourcesModel(m_model ? m_model->property("dataSourcesModel").value() - : Q_NULLPTR); + : nullptr); } if (m_pageView) { m_pageView->setModel(m_model ? m_model->property("currentPage").value() - : Q_NULLPTR); + : nullptr); m_pageView->setRunningTaskModel(m_model ? m_model->property("runningTaskModel").value() - : Q_NULLPTR); + : nullptr); if (m_model) { connect(m_model.data(), SIGNAL(currentPageChanged(QObject*)), m_pageView, SLOT(setModel(QObject*))); } } if (m_editorView) { m_editorView->setModel(m_model ? m_model->property("editor").value() - : Q_NULLPTR); + : nullptr); } if (m_runningTaskView) { m_runningTaskView->setModel(m_model ? m_model->property("runningTaskModel").value() - : Q_NULLPTR); + : nullptr); } else if (m_model) { runningTaskView(); // We got a model so make sure this view exists now } } void ApplicationComponents::setQuickSelectDialogFactory(const QuickSelectDialogFactory &factory) { m_quickSelectDialogFactory = factory; } void ApplicationComponents::onCurrentPageChanged(QObject *page) { if (!m_model) return; m_model->setProperty("currentPage", QVariant::fromValue(page)); QObject *editorModel = m_model->property("editor").value(); if (editorModel) editorModel->setProperty("task", QVariant::fromValue(Domain::Task::Ptr())); } void ApplicationComponents::onCurrentTaskChanged(const Domain::Task::Ptr &task) { if (!m_model) return; auto editorModel = m_model->property("editor").value(); if (editorModel) editorModel->setProperty("task", QVariant::fromValue(task)); } void ApplicationComponents::onMoveItemsRequested() { if (!m_model) return; if (m_pageView->selectedIndexes().size() == 0) return; auto pageListModel = m_availablePagesView->model()->property("pageListModel").value(); Q_ASSERT(pageListModel); QuickSelectDialogInterface::Ptr dlg = m_quickSelectDialogFactory(m_pageView); dlg->setModel(pageListModel); if (dlg->exec() == QDialog::Accepted) moveItems(dlg->selectedIndex(), m_pageView->selectedIndexes()); } Presentation::ErrorHandler *ApplicationComponents::errorHandler() const { return m_errorHandler.data(); } void ApplicationComponents::moveItems(const QModelIndex &destination, const QModelIndexList &droppedItems) { Q_ASSERT(destination.isValid()); Q_ASSERT(!droppedItems.isEmpty()); auto centralListModel = droppedItems.first().model(); auto availablePagesModel = const_cast(destination.model()); // drag const auto data = std::unique_ptr(centralListModel->mimeData(droppedItems)); // drop availablePagesModel->dropMimeData(data.get(), Qt::MoveAction, -1, -1, destination); } diff --git a/src/widgets/applicationcomponents.h b/src/widgets/applicationcomponents.h index 5294f246..0f254fcb 100644 --- a/src/widgets/applicationcomponents.h +++ b/src/widgets/applicationcomponents.h @@ -1,108 +1,108 @@ /* 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 WIDGETS_APPLICATIONCOMPONENTS_H #define WIDGETS_APPLICATIONCOMPONENTS_H #include #include #include #include #include #include "domain/task.h" #include "presentation/metatypes.h" class QAction; class QWidget; namespace Presentation { class ErrorHandler; } namespace Widgets { class AvailablePagesView; class AvailableSourcesView; class EditorView; class PageView; class PageViewErrorHandler; class RunningTaskWidget; class QuickSelectDialogInterface; class ApplicationComponents : public QObject { Q_OBJECT public: typedef QSharedPointer QuickSelectDialogPtr; typedef std::function QuickSelectDialogFactory; - explicit ApplicationComponents(QWidget *parent = Q_NULLPTR); + explicit ApplicationComponents(QWidget *parent = nullptr); ~ApplicationComponents(); QHash globalActions() const; QObjectPtr model() const; AvailableSourcesView *availableSourcesView() const; AvailablePagesView *availablePagesView() const; virtual PageView *pageView() const; EditorView *editorView() const; RunningTaskWidget *runningTaskView() const; QuickSelectDialogFactory quickSelectDialogFactory() const; public slots: virtual void setModel(const QObjectPtr &model); void setQuickSelectDialogFactory(const QuickSelectDialogFactory &factory); private slots: void onCurrentPageChanged(QObject *page); void onCurrentTaskChanged(const Domain::Task::Ptr &task); void onMoveItemsRequested(); private: Presentation::ErrorHandler *errorHandler() const; void moveItems(const QModelIndex &destination, const QModelIndexList &droppedItems); QHash m_actions; QObjectPtr m_model; QWidget *m_parent; QPointer m_availableSourcesView; QPointer m_availablePagesView; QPointer m_pageView; QPointer m_editorView; QPointer m_runningTaskView; QScopedPointer m_errorHandler; QuickSelectDialogFactory m_quickSelectDialogFactory; }; } #endif // WIDGETS_APPLICATIONCOMPONENTS_H diff --git a/src/widgets/availablepagesview.cpp b/src/widgets/availablepagesview.cpp index e2030297..5a29be71 100644 --- a/src/widgets/availablepagesview.cpp +++ b/src/widgets/availablepagesview.cpp @@ -1,338 +1,338 @@ /* 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 "availablepagesview.h" #include #include #include #include #include #include #include #include #include "presentation/metatypes.h" #include "presentation/querytreemodelbase.h" #include "widgets/messagebox.h" #include "widgets/newprojectdialog.h" #include "widgets/quickselectdialog.h" #include "domain/project.h" #include "domain/context.h" using namespace Widgets; using namespace Presentation; AvailablePagesView::AvailablePagesView(QWidget *parent) : QWidget(parent), m_addProjectAction(new QAction(this)), m_addContextAction(new QAction(this)), m_removeAction(new QAction(this)), - m_model(Q_NULLPTR), - m_sources(Q_NULLPTR), + m_model(nullptr), + m_sources(nullptr), m_pagesView(new QTreeView(this)) { m_pagesView->setObjectName(QStringLiteral("pagesView")); m_pagesView->header()->hide(); m_pagesView->setDragDropMode(QTreeView::DropOnly); auto actionBar = new QToolBar(this); actionBar->setObjectName(QStringLiteral("actionBar")); actionBar->setIconSize(QSize(16, 16)); m_addProjectAction->setObjectName(QStringLiteral("addProjectAction")); m_addProjectAction->setText(i18n("New Project")); m_addProjectAction->setIcon(QIcon::fromTheme(QStringLiteral("view-pim-tasks"))); connect(m_addProjectAction, &QAction::triggered, this, &AvailablePagesView::onAddProjectTriggered); actionBar->addAction(m_addProjectAction); m_addContextAction->setObjectName(QStringLiteral("addContextAction")); m_addContextAction->setText(i18n("New Context")); m_addContextAction->setIcon(QIcon::fromTheme(QStringLiteral("view-pim-notes"))); connect(m_addContextAction, &QAction::triggered, this, &AvailablePagesView::onAddContextTriggered); actionBar->addAction(m_addContextAction); m_removeAction->setObjectName(QStringLiteral("removeAction")); m_removeAction->setText(i18n("Remove Page")); m_removeAction->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); connect(m_removeAction, &QAction::triggered, this, &AvailablePagesView::onRemoveTriggered); actionBar->addAction(m_removeAction); auto actionBarLayout = new QHBoxLayout; actionBarLayout->setContentsMargins(0, 0, 0, 0); actionBarLayout->setAlignment(Qt::AlignRight); actionBarLayout->addWidget(actionBar); auto layout = new QVBoxLayout; layout->addWidget(m_pagesView); layout->addLayout(actionBarLayout); setLayout(layout); auto margins = layout->contentsMargins(); margins.setBottom(0); layout->setContentsMargins(margins); m_projectDialogFactory = [] (QWidget *parent) { return NewProjectDialogPtr(new NewProjectDialog(parent)); }; m_quickSelectDialogFactory = [] (QWidget *parent) { return QuickSelectDialogPtr(new QuickSelectDialog(parent)); }; m_messageBoxInterface = MessageBox::Ptr::create(); auto goPreviousAction = new QAction(this); goPreviousAction->setObjectName(QStringLiteral("goPreviousAction")); goPreviousAction->setText(i18n("Previous Page")); goPreviousAction->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); goPreviousAction->setShortcut(Qt::ALT | Qt::Key_Up); connect(goPreviousAction, &QAction::triggered, this, &AvailablePagesView::onGoPreviousTriggered); auto goNextAction = new QAction(this); goNextAction->setObjectName(QStringLiteral("goNextAction")); goNextAction->setText(i18n("Next Page")); goNextAction->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); goNextAction->setShortcut(Qt::ALT | Qt::Key_Down); connect(goNextAction, &QAction::triggered, this, &AvailablePagesView::onGoNextTriggered); auto goToAction = new QAction(this); goToAction->setObjectName(QStringLiteral("goToAction")); goToAction->setText(i18n("Go to Page...")); goToAction->setShortcut(Qt::Key_J); connect(goToAction, &QAction::triggered, this, &AvailablePagesView::onGoToTriggered); m_actions.insert(QStringLiteral("pages_project_add"), m_addProjectAction); m_actions.insert(QStringLiteral("pages_context_add"), m_addContextAction); m_actions.insert(QStringLiteral("pages_remove"), m_removeAction); m_actions.insert(QStringLiteral("pages_go_previous"), goPreviousAction); m_actions.insert(QStringLiteral("pages_go_next"), goNextAction); m_actions.insert(QStringLiteral("pages_go_to"), goToAction); } QHash AvailablePagesView::globalActions() const { return m_actions; } QObject *AvailablePagesView::model() const { return m_model; } QAbstractItemModel *AvailablePagesView::projectSourcesModel() const { return m_sources; } Domain::DataSource::Ptr AvailablePagesView::defaultProjectSource() const { return m_defaultSource; } AvailablePagesView::ProjectDialogFactory AvailablePagesView::projectDialogFactory() const { return m_projectDialogFactory; } AvailablePagesView::QuickSelectDialogFactory AvailablePagesView::quickSelectDialogFactory() const { return m_quickSelectDialogFactory; } void AvailablePagesView::setModel(QObject *model) { if (model == m_model) return; if (m_pagesView->selectionModel()) { - disconnect(m_pagesView->selectionModel(), Q_NULLPTR, this, Q_NULLPTR); + disconnect(m_pagesView->selectionModel(), nullptr, this, nullptr); } if (m_pagesView->model()) { disconnect(m_pagesView->model(), &QAbstractItemModel::rowsInserted, m_pagesView, &QTreeView::expand); disconnect(m_pagesView->model(), &QAbstractItemModel::layoutChanged, m_pagesView, &QTreeView::expandAll); disconnect(m_pagesView->model(), &QAbstractItemModel::modelReset, m_pagesView, &QTreeView::expandAll); } - m_pagesView->setModel(Q_NULLPTR); + m_pagesView->setModel(nullptr); m_model = model; setEnabled(m_model); if (!m_model) return; QVariant modelProperty = m_model->property("pageListModel"); if (modelProperty.canConvert()) { m_pagesView->setModel(modelProperty.value()); connect(m_pagesView->model(), &QAbstractItemModel::rowsInserted, m_pagesView, &QTreeView::expand); connect(m_pagesView->model(), &QAbstractItemModel::layoutChanged, m_pagesView, &QTreeView::expandAll); connect(m_pagesView->model(), &QAbstractItemModel::modelReset, m_pagesView, &QTreeView::expandAll); } connect(m_pagesView->selectionModel(), &QItemSelectionModel::currentChanged, this, &AvailablePagesView::onCurrentChanged); QMetaObject::invokeMethod(this, "onInitTimeout", Qt::QueuedConnection); } void AvailablePagesView::setProjectSourcesModel(QAbstractItemModel *sources) { m_sources = sources; } void AvailablePagesView::setDefaultProjectSource(const Domain::DataSource::Ptr &source) { m_defaultSource = source; } void AvailablePagesView::setProjectDialogFactory(const AvailablePagesView::ProjectDialogFactory &factory) { m_projectDialogFactory = factory; } void AvailablePagesView::setQuickSelectDialogFactory(const AvailablePagesView::QuickSelectDialogFactory &factory) { m_quickSelectDialogFactory = factory; } void AvailablePagesView::setMessageBoxInterface(const MessageBoxInterface::Ptr &interface) { m_messageBoxInterface = interface; } void AvailablePagesView::onCurrentChanged(const QModelIndex ¤t) { - QObject *page = Q_NULLPTR; + QObject *page = nullptr; QMetaObject::invokeMethod(m_model, "createPageForIndex", Q_RETURN_ARG(QObject*, page), Q_ARG(QModelIndex, current)); emit currentPageChanged(page); const auto object = current.data(QueryTreeModelBase::ObjectRole).value(); m_removeAction->setEnabled(object.objectCast() || object.objectCast()); } void AvailablePagesView::onAddProjectTriggered() { NewProjectDialogInterface::Ptr dialog = m_projectDialogFactory(this); dialog->setDataSourcesModel(m_sources); if (dialog->exec() == QDialog::Accepted) { m_defaultSource = dialog->dataSource(); QMetaObject::invokeMethod(m_model, "addProject", Q_ARG(QString, dialog->name()), Q_ARG(Domain::DataSource::Ptr, dialog->dataSource())); } } void AvailablePagesView::onAddContextTriggered() { const QString name = m_messageBoxInterface->askTextInput(this, i18n("Add Context"), i18n("Context name")); if (!name.isEmpty()) { QMetaObject::invokeMethod(m_model, "addContext", Q_ARG(QString, name)); } } void AvailablePagesView::onRemoveTriggered() { const QModelIndex current = m_pagesView->currentIndex(); if (!current.isValid()) return; QString title; QString text; QObjectPtr object = current.data(QueryTreeModelBase::ObjectRole).value(); if (!object) { qDebug() << "Model doesn't have ObjectRole for" << current; return; } if (Domain::Project::Ptr project = object.objectCast()) { title = i18n("Delete Project"); text = i18n("Do you really want to delete the project '%1', with all its actions?", project->name()); } else if (Domain::Context::Ptr context = object.objectCast()) { title = i18n("Delete Context"); text = i18n("Do you really want to delete the context '%1'?", context->name()); } else { qFatal("Unrecognized object type"); return; } QMessageBox::Button button = m_messageBoxInterface->askConfirmation(this, title, text); if (button != QMessageBox::Yes) { return; } QMetaObject::invokeMethod(m_model, "removeItem", Q_ARG(QModelIndex, current)); } void AvailablePagesView::onGoPreviousTriggered() { auto index = m_pagesView->indexAbove(m_pagesView->currentIndex()); while (index.isValid() && !(index.flags() & Qt::ItemIsSelectable)) { index = m_pagesView->indexAbove(index); } if (index.isValid()) m_pagesView->setCurrentIndex(index); } void AvailablePagesView::onGoNextTriggered() { auto index = m_pagesView->indexBelow(m_pagesView->currentIndex()); while (index.isValid() && !(index.flags() & Qt::ItemIsSelectable)) { index = m_pagesView->indexBelow(index); } if (index.isValid()) m_pagesView->setCurrentIndex(index); } void AvailablePagesView::onGoToTriggered() { QuickSelectDialogInterface::Ptr dialog = m_quickSelectDialogFactory(this); dialog->setModel(m_pagesView->model()); if (dialog->exec() == QDialog::Accepted && dialog->selectedIndex().isValid()) { m_pagesView->setCurrentIndex(dialog->selectedIndex()); } } void AvailablePagesView::onInitTimeout() { if (m_pagesView->model()) { m_pagesView->setCurrentIndex(m_pagesView->model()->index(0, 0)); m_pagesView->expandAll(); } } diff --git a/src/widgets/availablepagesview.h b/src/widgets/availablepagesview.h index 56edc639..728c14b1 100644 --- a/src/widgets/availablepagesview.h +++ b/src/widgets/availablepagesview.h @@ -1,104 +1,104 @@ /* 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 WIDGETS_AVAILABLEPAGESVIEW_H #define WIDGETS_AVAILABLEPAGESVIEW_H #include #include #include #include #include "domain/datasource.h" #include "messageboxinterface.h" class QAbstractItemModel; class QModelIndex; class QTreeView; namespace Widgets { class NewProjectDialogInterface; class QuickSelectDialogInterface; class AvailablePagesView : public QWidget { Q_OBJECT public: typedef QSharedPointer NewProjectDialogPtr; typedef std::function ProjectDialogFactory; typedef QSharedPointer QuickSelectDialogPtr; typedef std::function QuickSelectDialogFactory; - explicit AvailablePagesView(QWidget *parent = Q_NULLPTR); + explicit AvailablePagesView(QWidget *parent = nullptr); QHash globalActions() const; QObject *model() const; QAbstractItemModel *projectSourcesModel() const; Domain::DataSource::Ptr defaultProjectSource() const; ProjectDialogFactory projectDialogFactory() const; QuickSelectDialogFactory quickSelectDialogFactory() const; public slots: void setModel(QObject *model); void setProjectSourcesModel(QAbstractItemModel *sources); void setDefaultProjectSource(const Domain::DataSource::Ptr &source); void setProjectDialogFactory(const ProjectDialogFactory &factory); void setQuickSelectDialogFactory(const QuickSelectDialogFactory &factory); void setMessageBoxInterface(const MessageBoxInterface::Ptr &interface); signals: void currentPageChanged(QObject *page); private slots: void onCurrentChanged(const QModelIndex ¤t); void onAddProjectTriggered(); void onAddContextTriggered(); void onRemoveTriggered(); void onGoPreviousTriggered(); void onGoNextTriggered(); void onGoToTriggered(); void onInitTimeout(); private: QAction *m_addProjectAction; QAction *m_addContextAction; QAction *m_removeAction; QHash m_actions; QObject *m_model; QAbstractItemModel *m_sources; Domain::DataSource::Ptr m_defaultSource; QTreeView *m_pagesView; ProjectDialogFactory m_projectDialogFactory; QuickSelectDialogFactory m_quickSelectDialogFactory; MessageBoxInterface::Ptr m_messageBoxInterface; }; } #endif // WIDGETS_AVAILABLEPAGESVIEW_H diff --git a/src/widgets/availablesourcesview.cpp b/src/widgets/availablesourcesview.cpp index 2c9484ad..20476f55 100644 --- a/src/widgets/availablesourcesview.cpp +++ b/src/widgets/availablesourcesview.cpp @@ -1,163 +1,163 @@ /* 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 "availablesourcesview.h" #include #include #include #include #include #include #include #include #include #include "presentation/metatypes.h" #include "presentation/querytreemodelbase.h" #include "widgets/datasourcedelegate.h" using namespace Widgets; AvailableSourcesView::AvailableSourcesView(QWidget *parent) : QWidget(parent), m_defaultAction(new QAction(this)), - m_model(Q_NULLPTR), + m_model(nullptr), m_sortProxy(new QSortFilterProxyModel(this)), m_sourcesView(new QTreeView(this)) { m_sortProxy->setDynamicSortFilter(true); m_sortProxy->sort(0); m_sourcesView->setObjectName(QStringLiteral("sourcesView")); m_sourcesView->header()->hide(); m_sourcesView->setModel(m_sortProxy); connect(m_sourcesView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &AvailableSourcesView::onSelectionChanged); connect(m_sourcesView->model(), &QAbstractItemModel::rowsInserted, m_sourcesView, &QTreeView::expand); connect(m_sourcesView->model(), &QAbstractItemModel::layoutChanged, m_sourcesView, &QTreeView::expandAll); connect(m_sourcesView->model(), &QAbstractItemModel::modelReset, m_sourcesView, &QTreeView::expandAll); auto delegate = new DataSourceDelegate(m_sourcesView); m_sourcesView->setItemDelegate(delegate); auto actionBar = new QToolBar(this); actionBar->setObjectName(QStringLiteral("actionBar")); actionBar->setIconSize(QSize(16, 16)); m_defaultAction->setObjectName(QStringLiteral("defaultAction")); m_defaultAction->setText(i18n("Use as Default Source")); m_defaultAction->setIcon(QIcon::fromTheme(QStringLiteral("folder-favorites"))); connect(m_defaultAction, &QAction::triggered, this, &AvailableSourcesView::onDefaultTriggered); actionBar->addAction(m_defaultAction); auto layout = new QVBoxLayout; layout->addWidget(m_sourcesView); auto actionBarLayout = new QHBoxLayout; actionBarLayout->setContentsMargins(0, 0, 0, 0); actionBarLayout->setAlignment(Qt::AlignRight); actionBarLayout->addWidget(actionBar); layout->addLayout(actionBarLayout); setLayout(layout); auto margins = layout->contentsMargins(); margins.setBottom(0); layout->setContentsMargins(margins); auto settingsAction = new QAction(this); settingsAction->setObjectName(QStringLiteral("settingsAction")); settingsAction->setText(i18n("Configure %1...", QApplication::applicationName())); settingsAction->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); connect(settingsAction, &QAction::triggered, this, &AvailableSourcesView::onSettingsTriggered); m_actions.insert(QStringLiteral("options_configure"), settingsAction); onSelectionChanged(); } QHash AvailableSourcesView::globalActions() const { return m_actions; } QObject *AvailableSourcesView::model() const { return m_model; } void AvailableSourcesView::setModel(QObject *model) { if (model == m_model) return; - m_sortProxy->setSourceModel(Q_NULLPTR); + m_sortProxy->setSourceModel(nullptr); m_model = model; setEnabled(m_model); if (!m_model) return; setSourceModel("sourceListModel"); } void AvailableSourcesView::onSelectionChanged() { const auto selectedIndexes = m_sourcesView->selectionModel()->selectedIndexes(); auto selectedSources = Domain::DataSource::List(); std::transform(selectedIndexes.constBegin(), selectedIndexes.constEnd(), std::back_inserter(selectedSources), [] (const QModelIndex &index) { return index.data(Presentation::QueryTreeModelBase::ObjectRole) .value(); }); m_defaultAction->setEnabled(selectedSources.size() == 1 && selectedSources.first()->contentTypes() != Domain::DataSource::NoContent); } void AvailableSourcesView::onSettingsTriggered() { QMetaObject::invokeMethod(m_model, "showConfigDialog"); } void AvailableSourcesView::onDefaultTriggered() { const auto currentIndex = m_sourcesView->currentIndex(); const auto index = m_sortProxy->mapToSource(currentIndex); if (index.isValid()) QMetaObject::invokeMethod(m_model, "setDefaultItem", Q_ARG(QModelIndex, index)); } void AvailableSourcesView::setSourceModel(const QByteArray &propertyName) { QVariant modelProperty = m_model->property(propertyName); if (modelProperty.canConvert()) m_sortProxy->setSourceModel(modelProperty.value()); } diff --git a/src/widgets/availablesourcesview.h b/src/widgets/availablesourcesview.h index c2461de8..c7fa4f21 100644 --- a/src/widgets/availablesourcesview.h +++ b/src/widgets/availablesourcesview.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 WIDGETS_AVAILABLESOURCESVIEW_H #define WIDGETS_AVAILABLESOURCESVIEW_H #include #include #include "domain/datasource.h" class QSortFilterProxyModel; class QTreeView; namespace Widgets { class AvailableSourcesView : public QWidget { Q_OBJECT public: - explicit AvailableSourcesView(QWidget *parent = Q_NULLPTR); + explicit AvailableSourcesView(QWidget *parent = nullptr); QHash globalActions() const; QObject *model() const; void setSourceModel(const QByteArray &propertyName); public slots: void setModel(QObject *model); private slots: void onSelectionChanged(); void onSettingsTriggered(); void onDefaultTriggered(); private: QHash m_actions; QAction *m_defaultAction; QObject *m_model; QSortFilterProxyModel *m_sortProxy; QTreeView *m_sourcesView; }; } #endif // WIDGETS_AVAILABLESOURCESVIEW_H diff --git a/src/widgets/datasourcedelegate.h b/src/widgets/datasourcedelegate.h index e015a090..ccc65aed 100644 --- a/src/widgets/datasourcedelegate.h +++ b/src/widgets/datasourcedelegate.h @@ -1,49 +1,49 @@ /* This file is part of Zanshin Copyright 2014 Christian Mollekopf 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 DATASOURCEDELEGATE_H #define DATASOURCEDELEGATE_H #include #include "domain/datasource.h" namespace Widgets { class DataSourceDelegate : public QStyledItemDelegate { Q_OBJECT public: - explicit DataSourceDelegate(QObject *parent = Q_NULLPTR); + explicit DataSourceDelegate(QObject *parent = nullptr); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; } #endif diff --git a/src/widgets/editorview.cpp b/src/widgets/editorview.cpp index e7d7f0b4..6c392232 100644 --- a/src/widgets/editorview.cpp +++ b/src/widgets/editorview.cpp @@ -1,298 +1,298 @@ /* 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 "editorview.h" #include #include #include #include #include #include #include #include "kdateedit.h" #include "domain/task.h" #include "ui_editorview.h" using namespace Widgets; EditorView::EditorView(QWidget *parent) : QWidget(parent), - m_model(Q_NULLPTR), + m_model(nullptr), ui(new Ui::EditorView) { m_requestFileNameFunction = [](QWidget *parent) { return QFileDialog::getOpenFileName(parent, i18n("Add Attachment")); }; ui->setupUi(this); ui->startDateEdit->setMinimumContentsLength(10); ui->dueDateEdit->setMinimumContentsLength(10); ui->recurrenceCombo->addItem(i18n("Never"), QVariant::fromValue(Domain::Task::NoRecurrence)); ui->recurrenceCombo->addItem(i18n("Daily"), QVariant::fromValue(Domain::Task::RecursDaily)); ui->recurrenceCombo->addItem(i18n("Weekly"), QVariant::fromValue(Domain::Task::RecursWeekly)); ui->recurrenceCombo->addItem(i18n("Monthly"), QVariant::fromValue(Domain::Task::RecursMonthly)); ui->textEdit->installEventFilter(this); ui->startDateEdit->installEventFilter(this); ui->dueDateEdit->installEventFilter(this); ui->doneButton->installEventFilter(this); ui->recurrenceCombo->installEventFilter(this); connect(ui->textEdit, &QPlainTextEdit::textChanged, this, &EditorView::onTextEditChanged); connect(ui->startDateEdit, &KPIM::KDateEdit::dateEntered, this, &EditorView::onStartEditEntered); connect(ui->dueDateEdit, &KPIM::KDateEdit::dateEntered, this, &EditorView::onDueEditEntered); connect(ui->doneButton, &QAbstractButton::toggled, this, &EditorView::onDoneButtonChanged); connect(ui->startTodayButton, &QAbstractButton::clicked, this, &EditorView::onStartTodayClicked); connect(ui->recurrenceCombo, static_cast(&QComboBox::currentIndexChanged), this, &EditorView::onRecurrenceComboChanged); connect(ui->attachmentList, &QAbstractItemView::doubleClicked, this, &EditorView::onAttachmentDoubleClicked); connect(ui->addAttachmentButton, &QToolButton::clicked, this, &EditorView::onAddAttachmentClicked); connect(ui->removeAttachmentButton, &QToolButton::clicked, this, &EditorView::onRemoveAttachmentClicked); setEnabled(false); } EditorView::~EditorView() { delete ui; } QObject *EditorView::model() const { return m_model; } EditorView::RequestFileNameFunction EditorView::requestFileNameFunction() const { return m_requestFileNameFunction; } void EditorView::setModel(QObject *model) { if (model == m_model) return; if (m_model) { disconnect(ui->attachmentList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &EditorView::onAttachmentSelectionChanged); - ui->attachmentList->setModel(Q_NULLPTR); - disconnect(m_model, Q_NULLPTR, this, Q_NULLPTR); - disconnect(this, Q_NULLPTR, m_model, Q_NULLPTR); + ui->attachmentList->setModel(nullptr); + disconnect(m_model, nullptr, this, nullptr); + disconnect(this, nullptr, m_model, nullptr); } m_model = model; setEnabled(m_model); if (!m_model) { ui->taskGroup->setVisible(false); ui->textEdit->clear(); return; } auto attachments = m_model->property("attachmentModel").value(); ui->attachmentList->setModel(attachments); connect(ui->attachmentList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &EditorView::onAttachmentSelectionChanged); onTaskChanged(); onTextOrTitleChanged(); onStartDateChanged(); onDueDateChanged(); onDoneChanged(); onRecurrenceChanged(); onAttachmentSelectionChanged(); connect(m_model, SIGNAL(taskChanged(Domain::Task::Ptr)), this, SLOT(onTaskChanged())); connect(m_model, SIGNAL(titleChanged(QString)), this, SLOT(onTextOrTitleChanged())); connect(m_model, SIGNAL(textChanged(QString)), this, SLOT(onTextOrTitleChanged())); connect(m_model, SIGNAL(startDateChanged(QDate)), this, SLOT(onStartDateChanged())); connect(m_model, SIGNAL(dueDateChanged(QDate)), this, SLOT(onDueDateChanged())); connect(m_model, SIGNAL(doneChanged(bool)), this, SLOT(onDoneChanged())); connect(m_model, SIGNAL(recurrenceChanged(Domain::Task::Recurrence)), this, SLOT(onRecurrenceChanged())); connect(this, SIGNAL(titleChanged(QString)), m_model, SLOT(setTitle(QString))); connect(this, SIGNAL(textChanged(QString)), m_model, SLOT(setText(QString))); connect(this, SIGNAL(startDateChanged(QDate)), m_model, SLOT(setStartDate(QDate))); connect(this, SIGNAL(dueDateChanged(QDate)), m_model, SLOT(setDueDate(QDate))); connect(this, SIGNAL(doneChanged(bool)), m_model, SLOT(setDone(bool))); connect(this, SIGNAL(recurrenceChanged(Domain::Task::Recurrence)), m_model, SLOT(setRecurrence(Domain::Task::Recurrence))); } void EditorView::setRequestFileNameFunction(const EditorView::RequestFileNameFunction &function) { m_requestFileNameFunction = function; } bool EditorView::eventFilter(QObject *watched, QEvent *event) { Q_UNUSED(watched); switch (event->type()) { case QEvent::FocusIn: // We don't want to replace text being edited by the user with older text // coming from akonadi notifications (async, after some older save job) m_model->setProperty("editingInProgress", true); break; case QEvent::FocusOut: // We do react to notifications, however, when not having the focus, // for instance when changing the title using the central list. m_model->setProperty("editingInProgress", false); break; default: break; } return false; } void EditorView::onTaskChanged() { auto task = m_model->property("task").value(); setEnabled(task); } void EditorView::onTextOrTitleChanged() { const auto title = m_model->property("title").toString(); const auto text = m_model->property("text").toString(); const auto fullText = QString(title + '\n' + text); if (ui->textEdit->toPlainText() != fullText) // QPlainTextEdit doesn't do this check ui->textEdit->setPlainText(fullText); } void EditorView::onStartDateChanged() { ui->startDateEdit->setDate(m_model->property("startDate").toDate()); } void EditorView::onDueDateChanged() { ui->dueDateEdit->setDate(m_model->property("dueDate").toDate()); } void EditorView::onDoneChanged() { ui->doneButton->setChecked(m_model->property("done").toBool()); } void EditorView::onRecurrenceChanged() { const auto recurrence = m_model->property("recurrence").value(); for (int index = 0; index < ui->recurrenceCombo->count(); index++) { if (recurrence == ui->recurrenceCombo->itemData(index).value()) { ui->recurrenceCombo->setCurrentIndex(index); return; } } } void EditorView::onTextEditChanged() { const QString plainText = ui->textEdit->toPlainText(); const int index = plainText.indexOf('\n'); if (index < 0) { emit titleChanged(plainText); emit textChanged(QString()); } else { const QString title = plainText.left(index); const QString text = plainText.mid(index + 1); emit titleChanged(title); emit textChanged(text); } } void EditorView::onStartEditEntered(const QDate &start) { emit startDateChanged(start); } void EditorView::onDueEditEntered(const QDate &due) { emit dueDateChanged(due); } void EditorView::onDoneButtonChanged(bool checked) { emit doneChanged(checked); } void EditorView::onStartTodayClicked() { QDate today(QDate::currentDate()); ui->startDateEdit->setDate(today); emit startDateChanged(today); } void EditorView::onRecurrenceComboChanged(int index) { const auto recurrence = ui->recurrenceCombo->itemData(index).value(); emit recurrenceChanged(recurrence); } void EditorView::onAttachmentSelectionChanged() { if (!m_model) return; const auto selectionModel = ui->attachmentList->selectionModel(); const auto selectedIndexes = selectionModel->selectedIndexes(); ui->removeAttachmentButton->setEnabled(!selectedIndexes.isEmpty()); } void EditorView::onAddAttachmentClicked() { if (!m_model) return; auto fileName = m_requestFileNameFunction(this); if (!fileName.isEmpty()) QMetaObject::invokeMethod(m_model, "addAttachment", Q_ARG(QString, fileName)); } void EditorView::onRemoveAttachmentClicked() { if (!m_model) return; const auto selectionModel = ui->attachmentList->selectionModel(); const auto selectedIndexes = selectionModel->selectedIndexes(); if (!selectedIndexes.isEmpty()) QMetaObject::invokeMethod(m_model, "removeAttachment", Q_ARG(QModelIndex, selectedIndexes.first())); } void EditorView::onAttachmentDoubleClicked(const QModelIndex &index) { if (!m_model) return; QMetaObject::invokeMethod(m_model, "openAttachment", Q_ARG(QModelIndex, index)); } diff --git a/src/widgets/editorview.h b/src/widgets/editorview.h index 8257d539..1451e965 100644 --- a/src/widgets/editorview.h +++ b/src/widgets/editorview.h @@ -1,108 +1,108 @@ /* 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 WIDGETS_EDITORVIEW_H #define WIDGETS_EDITORVIEW_H #include #include #include #include "domain/task.h" class QAbstractButton; class QLabel; class QPlainTextEdit; class KLineEdit; namespace KPIM { class KDateEdit; } namespace Ui { class EditorView; } namespace Widgets { class EditorView : public QWidget { Q_OBJECT public: typedef std::function RequestFileNameFunction; - explicit EditorView(QWidget *parent = Q_NULLPTR); + explicit EditorView(QWidget *parent = nullptr); ~EditorView(); QObject *model() const; RequestFileNameFunction requestFileNameFunction() const; public slots: void setModel(QObject *model); void setRequestFileNameFunction(const RequestFileNameFunction &function); signals: void textChanged(const QString &text); void titleChanged(const QString &title); void startDateChanged(const QDate &start); void dueDateChanged(const QDate &due); void doneChanged(bool done); void recurrenceChanged(Domain::Task::Recurrence recurrence); protected: bool eventFilter(QObject *watched, QEvent *event) override; private slots: void onTaskChanged(); void onTextOrTitleChanged(); void onStartDateChanged(); void onDueDateChanged(); void onDoneChanged(); void onRecurrenceChanged(); void onTextEditChanged(); void onStartEditEntered(const QDate &start); void onDueEditEntered(const QDate &due); void onDoneButtonChanged(bool checked); void onStartTodayClicked(); void onRecurrenceComboChanged(int index); void onAttachmentSelectionChanged(); void onAddAttachmentClicked(); void onRemoveAttachmentClicked(); void onAttachmentDoubleClicked(const QModelIndex &index); private: QObject *m_model; RequestFileNameFunction m_requestFileNameFunction; Ui::EditorView *ui; }; } #endif // WIDGETS_EDITORVIEW_H diff --git a/src/widgets/filterwidget.h b/src/widgets/filterwidget.h index 8831ed2a..a55aeaa7 100644 --- a/src/widgets/filterwidget.h +++ b/src/widgets/filterwidget.h @@ -1,70 +1,70 @@ /* 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 WIDGETS_FILTERWIDGET_H #define WIDGETS_FILTERWIDGET_H #include class QComboBox; class QLineEdit; namespace Presentation { class TaskFilterProxyModel; } namespace Ui { class FilterWidget; } namespace Widgets { class FilterWidget : public QWidget { Q_OBJECT public: - explicit FilterWidget(QWidget *parent = Q_NULLPTR); + explicit FilterWidget(QWidget *parent = nullptr); ~FilterWidget(); Presentation::TaskFilterProxyModel *proxyModel() const; public slots: void clear(); void setShowFutureTasks(bool show); private slots: void onTextChanged(const QString &text); void onSortTypeChanged(int index); void onAscendingClicked(); void onDescendingClicked(); private: Ui::FilterWidget *ui; Presentation::TaskFilterProxyModel *m_model; }; } #endif // WIDGETS_FILTERWIDGET_H diff --git a/src/widgets/itemdelegate.h b/src/widgets/itemdelegate.h index 5a60d5d7..cf94f2e8 100644 --- a/src/widgets/itemdelegate.h +++ b/src/widgets/itemdelegate.h @@ -1,47 +1,47 @@ /* 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 WIDGETS_ITEMDELEGATE_H #define WIDGETS_ITEMDELEGATE_H #include namespace Widgets { class ItemDelegate : public QStyledItemDelegate { Q_OBJECT public: - explicit ItemDelegate(QObject *parent = Q_NULLPTR); + explicit ItemDelegate(QObject *parent = nullptr); QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; } #endif // WIDGETS_ITEMDELEGATE_H diff --git a/src/widgets/newprojectdialog.cpp b/src/widgets/newprojectdialog.cpp index 290dc801..cb068343 100644 --- a/src/widgets/newprojectdialog.cpp +++ b/src/widgets/newprojectdialog.cpp @@ -1,128 +1,128 @@ /* 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 "newprojectdialog.h" #include "ui_newprojectdialog.h" #include #include #include #include "presentation/querytreemodelbase.h" using namespace Widgets; class TaskSourceProxy : public QSortFilterProxyModel { Q_OBJECT public: - explicit TaskSourceProxy(QObject *parent = Q_NULLPTR) + explicit TaskSourceProxy(QObject *parent = nullptr) : QSortFilterProxyModel(parent) { setDynamicSortFilter(true); } protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &) const override { auto sourceIndex = sourceModel()->index(sourceRow, 0); auto source = sourceIndex.data(Presentation::QueryTreeModelBase::ObjectRole) .value(); return source && (source->contentTypes() & Domain::DataSource::Tasks); } }; NewProjectDialog::NewProjectDialog(QWidget *parent) : QDialog(parent), ui(new Ui::NewProjectDialog), m_flattenProxy(new KDescendantsProxyModel(this)) { ui->setupUi(this); connect(ui->nameEdit, &QLineEdit::textChanged, this, &NewProjectDialog::onUserInputChanged); auto taskSourceProxy = new TaskSourceProxy(this); taskSourceProxy->setSourceModel(m_flattenProxy); ui->sourceCombo->setModel(taskSourceProxy); m_flattenProxy->setDisplayAncestorData(true); connect(ui->sourceCombo, static_cast(&QComboBox::currentIndexChanged), this, &NewProjectDialog::onUserInputChanged); onUserInputChanged(); } NewProjectDialog::~NewProjectDialog() { delete ui; } int NewProjectDialog::exec() { return QDialog::exec(); } void NewProjectDialog::accept() { m_name = ui->nameEdit->text(); m_source = ui->sourceCombo->itemData(ui->sourceCombo->currentIndex(), Presentation::QueryTreeModelBase::ObjectRole) .value(); QDialog::accept(); } void NewProjectDialog::setDataSourcesModel(QAbstractItemModel *model) { m_flattenProxy->setSourceModel(model); auto proxy = ui->sourceCombo->model(); for (int row = 0; row < proxy->rowCount(); row++) { auto index = proxy->index(row, 0); if (index.data(Presentation::QueryTreeModelBase::IsDefaultRole).toBool()) { ui->sourceCombo->setCurrentIndex(row); } } } QString NewProjectDialog::name() const { return m_name; } Domain::DataSource::Ptr NewProjectDialog::dataSource() const { return m_source; } void NewProjectDialog::onUserInputChanged() { const auto text = ui->nameEdit->text(); const auto source = ui->sourceCombo->itemData(ui->sourceCombo->currentIndex(), Presentation::QueryTreeModelBase::ObjectRole) .value(); auto buttonOk = ui->buttonBox->button(QDialogButtonBox::Ok); buttonOk->setEnabled(!text.isEmpty() && source); } #include "newprojectdialog.moc" diff --git a/src/widgets/newprojectdialog.h b/src/widgets/newprojectdialog.h index c051dc6e..38ab50c7 100644 --- a/src/widgets/newprojectdialog.h +++ b/src/widgets/newprojectdialog.h @@ -1,71 +1,71 @@ /* 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 WIDGETS_NEWPROJECTDIALOG_H #define WIDGETS_NEWPROJECTDIALOG_H #include #include "widgets/newprojectdialoginterface.h" class QModelIndex; class KDescendantsProxyModel; namespace Ui { class NewProjectDialog; } namespace Widgets { class NewProjectDialog : public QDialog, public NewProjectDialogInterface { Q_OBJECT public: - explicit NewProjectDialog(QWidget *parent = Q_NULLPTR); + explicit NewProjectDialog(QWidget *parent = nullptr); ~NewProjectDialog(); int exec() override; void accept() override; void setDataSourcesModel(QAbstractItemModel *model) override; QString name() const override; Domain::DataSource::Ptr dataSource() const override; private slots: void onUserInputChanged(); private: void applyDefaultSource(const QModelIndex &root); Ui::NewProjectDialog *ui; KDescendantsProxyModel *m_flattenProxy; QString m_name; Domain::DataSource::Ptr m_source; }; } #endif // WIDGETS_NEWPROJECTDIALOG_H diff --git a/src/widgets/pageview.cpp b/src/widgets/pageview.cpp index 91a70ee0..c8f390ff 100644 --- a/src/widgets/pageview.cpp +++ b/src/widgets/pageview.cpp @@ -1,486 +1,486 @@ /* 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 "pageview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "filterwidget.h" #include "itemdelegate.h" #include "messagebox.h" #include #include "presentation/metatypes.h" #include "presentation/querytreemodelbase.h" #include "presentation/runningtaskmodelinterface.h" #include "presentation/taskfilterproxymodel.h" #include "utils/datetime.h" namespace Widgets { class PageTreeView : public QTreeView { Q_OBJECT public: using QTreeView::QTreeView; protected: void keyPressEvent(QKeyEvent *event) override { if (event->key() == Qt::Key_Escape && state() != EditingState) { selectionModel()->clear(); } QTreeView::keyPressEvent(event); } void resizeEvent(QResizeEvent *event) override { header()->resizeSection(0, event->size().width()); QTreeView::resizeEvent(event); } }; } class PassivePopup : public QFrame { Q_OBJECT public: - explicit PassivePopup(QWidget *parent = Q_NULLPTR) + explicit PassivePopup(QWidget *parent = nullptr) : QFrame(parent), m_hideTimer(new QTimer(this)), m_label(new QLabel(this)) { setWindowFlags(Qt::Tool | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint); setFrameStyle(QFrame::Box | QFrame::Plain); setLineWidth(2); setAttribute(Qt::WA_DeleteOnClose); setLayout(new QVBoxLayout); layout()->addWidget(m_label); connect(m_hideTimer, &QTimer::timeout, this, &QWidget::hide); } void setVisible(bool visible) override { if (visible) { m_hideTimer->start(2000); } QFrame::setVisible(visible); } void setText(const QString &text) { m_label->setText(text); } private: QTimer *m_hideTimer; QLabel *m_label; }; using namespace Widgets; PageView::PageView(QWidget *parent) : QWidget(parent), m_cancelAction(new QAction(this)), - m_model(Q_NULLPTR), + m_model(nullptr), m_messageWidget(new KMessageWidget(this)), m_filterWidget(new FilterWidget(this)), m_centralView(new PageTreeView(this)), m_quickAddEdit(new QLineEdit(this)), m_runningTaskModel(nullptr) { m_messageWidget->setObjectName(QStringLiteral("messageWidget")); m_messageWidget->setCloseButtonVisible(true); m_messageWidget->setMessageType(KMessageWidget::Error); m_messageWidget->setWordWrap(true); m_messageWidget->hide(); m_filterWidget->setObjectName(QStringLiteral("filterWidget")); m_filterWidget->hide(); m_centralView->setObjectName(QStringLiteral("centralView")); m_centralView->header()->hide(); m_centralView->setAlternatingRowColors(true); m_centralView->setItemDelegate(new ItemDelegate(this)); m_centralView->setDragDropMode(QTreeView::DragDrop); m_centralView->setSelectionMode(QAbstractItemView::ExtendedSelection); m_centralView->setModel(m_filterWidget->proxyModel()); m_centralView->installEventFilter(this); m_centralView->setItemsExpandable(false); m_centralView->setRootIsDecorated(false); connect(m_centralView->model(), &QAbstractItemModel::rowsInserted, m_centralView, &QTreeView::expandAll); connect(m_centralView->model(), &QAbstractItemModel::layoutChanged, m_centralView, &QTreeView::expandAll); connect(m_centralView->model(), &QAbstractItemModel::modelReset, m_centralView, &QTreeView::expandAll); m_centralView->setStyleSheet(QStringLiteral("QTreeView::branch { border-image: url(none.png); }")); m_quickAddEdit->setObjectName(QStringLiteral("quickAddEdit")); m_quickAddEdit->setPlaceholderText(i18n("Type and press enter to add a task")); connect(m_quickAddEdit, &QLineEdit::returnPressed, this, &PageView::onReturnPressed); auto layout = new QVBoxLayout; layout->setContentsMargins(0, 0, 0, 3); layout->addWidget(m_messageWidget); layout->addWidget(m_filterWidget); layout->addWidget(m_centralView); layout->addWidget(m_quickAddEdit); setLayout(layout); m_messageBoxInterface = MessageBox::Ptr::create(); auto addItemAction = new QAction(this); addItemAction->setObjectName(QStringLiteral("addItemAction")); addItemAction->setText(i18n("New Task")); addItemAction->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); addItemAction->setShortcut(Qt::CTRL | Qt::Key_N); connect(addItemAction, &QAction::triggered, this, &PageView::onAddItemRequested); m_cancelAction->setObjectName(QStringLiteral("cancelAddItemAction")); m_cancelAction->setShortcut(Qt::Key_Escape); addAction(m_cancelAction); connect(m_cancelAction, &QAction::triggered, m_centralView, static_cast(&QWidget::setFocus)); auto removeItemAction = new QAction(this); removeItemAction->setObjectName(QStringLiteral("removeItemAction")); removeItemAction->setText(i18n("Remove Task")); removeItemAction->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); removeItemAction->setShortcut(Qt::Key_Delete); connect(removeItemAction, &QAction::triggered, this, &PageView::onRemoveItemRequested); addAction(removeItemAction); auto promoteItemAction = new QAction(this); promoteItemAction->setObjectName(QStringLiteral("promoteItemAction")); promoteItemAction->setText(i18n("Promote Task as Project")); promoteItemAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_P); connect(promoteItemAction, &QAction::triggered, this, &PageView::onPromoteItemRequested); auto filterViewAction = new QAction(this); filterViewAction->setObjectName(QStringLiteral("filterViewAction")); filterViewAction->setText(i18n("Filter...")); filterViewAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); filterViewAction->setShortcut(Qt::CTRL | Qt::Key_F); filterViewAction->setCheckable(true); connect(filterViewAction, &QAction::triggered, this, &PageView::onFilterToggled); auto futureViewAction = new QAction(this); futureViewAction->setObjectName(QStringLiteral("futureViewAction")); futureViewAction->setText(i18n("Show future tasks")); futureViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-calendar-whatsnext"))); futureViewAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_F); futureViewAction->setCheckable(true); connect(futureViewAction, &QAction::triggered, m_filterWidget, &FilterWidget::setShowFutureTasks); auto configGroup = KConfigGroup(KSharedConfig::openConfig(), "General"); if (configGroup.readEntry("ShowFuture", true)) futureViewAction->trigger(); connect(futureViewAction, &QAction::triggered, futureViewAction, [configGroup] (bool checked) mutable { configGroup.writeEntry("ShowFuture", checked); }); m_runTaskAction = new QAction(this); m_runTaskAction->setObjectName(QStringLiteral("runTaskAction")); m_runTaskAction->setShortcut(Qt::CTRL | Qt::Key_Space); m_runTaskAction->setText(i18n("Start Now")); m_runTaskAction->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); connect(m_runTaskAction, &QAction::triggered, this, &PageView::onRunTaskTriggered); updateRunTaskAction(); m_actions.insert(QStringLiteral("page_view_add"), addItemAction); m_actions.insert(QStringLiteral("page_view_remove"), removeItemAction); m_actions.insert(QStringLiteral("page_view_promote"), promoteItemAction); m_actions.insert(QStringLiteral("page_view_filter"), filterViewAction); m_actions.insert(QStringLiteral("page_view_future"), futureViewAction); m_actions.insert(QStringLiteral("page_run_task"), m_runTaskAction); } QHash PageView::globalActions() const { return m_actions; } QObject *PageView::model() const { return m_model; } Presentation::RunningTaskModelInterface *PageView::runningTaskModel() const { return m_runningTaskModel; } void PageView::setModel(QObject *model) { if (model == m_model) return; if (m_centralView->selectionModel()) { - disconnect(m_centralView->selectionModel(), Q_NULLPTR, this, Q_NULLPTR); + disconnect(m_centralView->selectionModel(), nullptr, this, nullptr); } - m_filterWidget->proxyModel()->setSourceModel(Q_NULLPTR); + m_filterWidget->proxyModel()->setSourceModel(nullptr); m_model = model; setEnabled(m_model); updateRunTaskAction(); if (!m_model) return; QVariant modelProperty = m_model->property("centralListModel"); if (modelProperty.canConvert()) m_filterWidget->proxyModel()->setSourceModel(modelProperty.value()); connect(m_centralView->selectionModel(), &QItemSelectionModel::currentChanged, this, &PageView::onCurrentChanged); } MessageBoxInterface::Ptr PageView::messageBoxInterface() const { return m_messageBoxInterface; } QModelIndexList PageView::selectedIndexes() const { using namespace std::placeholders; const auto selection = m_centralView->selectionModel()->selectedIndexes(); auto sourceIndices = QModelIndexList(); std::transform(selection.constBegin(), selection.constEnd(), std::back_inserter(sourceIndices ), std::bind(&QSortFilterProxyModel::mapToSource, m_filterWidget->proxyModel(), _1)); return sourceIndices; } void PageView::setRunningTaskModel(Presentation::RunningTaskModelInterface *model) { m_runningTaskModel = model; connect(m_runningTaskModel, SIGNAL(runningTaskChanged(Domain::Task::Ptr)), this, SLOT(onRunningTaskChanged(Domain::Task::Ptr))); } void PageView::setMessageBoxInterface(const MessageBoxInterface::Ptr &interface) { m_messageBoxInterface = interface; } void PageView::displayErrorMessage(const QString &message) { m_messageWidget->setText(message); m_messageWidget->animatedShow(); } void PageView::onReturnPressed() { if (m_quickAddEdit->text().isEmpty()) return; auto parentIndex = QModelIndex(); if (m_centralView->selectionModel()->selectedIndexes().size() == 1) parentIndex = m_centralView->selectionModel()->selectedIndexes().first(); QMetaObject::invokeMethod(m_model, "addItem", Q_ARG(QString, m_quickAddEdit->text()), Q_ARG(QModelIndex, parentIndex)); m_quickAddEdit->clear(); } void PageView::onAddItemRequested() { if (m_quickAddEdit->hasFocus()) return; const auto editTopLeft = m_quickAddEdit->geometry().topLeft(); const auto pos = mapToGlobal(editTopLeft); auto popup = new PassivePopup(m_quickAddEdit); popup->setText(i18n("Type and press enter to add an item")); popup->show(); popup->move(pos - QPoint(0, popup->height())); m_quickAddEdit->selectAll(); m_quickAddEdit->setFocus(); } void PageView::onRemoveItemRequested() { const QModelIndexList ¤tIndexes = m_centralView->selectionModel()->selectedIndexes(); if (currentIndexes.isEmpty()) return; auto indexHasChildren = [](QModelIndex index) { if (const QAbstractProxyModel *proxy = qobject_cast(index.model())) { index = proxy->mapToSource(index); } return index.model()->rowCount(index) > 0; }; QString text; if (currentIndexes.size() > 1) { const bool hasDescendants = std::any_of(currentIndexes.constBegin(), currentIndexes.constEnd(), [&](const QModelIndex ¤tIndex) { return currentIndex.isValid() && indexHasChildren(currentIndex); }); if (hasDescendants) text = i18n("Do you really want to delete the selected items and their children?"); else text = i18n("Do you really want to delete the selected items?"); } else { const QModelIndex ¤tIndex = currentIndexes.first(); if (!currentIndex.isValid()) return; if (indexHasChildren(currentIndex)) text = i18n("Do you really want to delete the selected task and all its children?"); } if (!text.isEmpty()) { QMessageBox::Button button = m_messageBoxInterface->askConfirmation(this, i18n("Delete Tasks"), text); bool canRemove = (button == QMessageBox::Yes); if (!canRemove) return; } foreach (const QModelIndex ¤tIndex, currentIndexes) { if (!currentIndex.isValid()) continue; QMetaObject::invokeMethod(m_model, "removeItem", Q_ARG(QModelIndex, currentIndex)); const auto data = currentIndex.data(Presentation::QueryTreeModelBase::ObjectRole); if (data.isValid()) { auto task = data.value(); if (task) m_runningTaskModel->taskDeleted(task); } } } void PageView::onPromoteItemRequested() { QModelIndex currentIndex = m_centralView->currentIndex(); if (!currentIndex.isValid()) return; QMetaObject::invokeMethod(m_model, "promoteItem", Q_ARG(QModelIndex, currentIndex)); } void PageView::onFilterToggled(bool show) { m_filterWidget->setVisible(show); if (show) m_filterWidget->setFocus(); else m_filterWidget->clear(); } void PageView::updateRunTaskAction() { const auto task = currentTask(); m_runTaskAction->setEnabled(task); } void PageView::onRunTaskTriggered() { auto task = currentTask(); Q_ASSERT(task); // the action is supposed to be disabled otherwise if (task->startDate().isNull()) task->setStartDate(Utils::DateTime::currentDate()); m_runningTaskModel->setRunningTask(task); } void PageView::onRunningTaskChanged(const Domain::Task::Ptr &task) { if (!task) { QWidget *toplevel = window(); toplevel->raise(); toplevel->activateWindow(); } } void PageView::onCurrentChanged(const QModelIndex ¤t) { updateRunTaskAction(); auto data = current.data(Presentation::QueryTreeModelBase::ObjectRole); if (!data.isValid()) return; auto task = currentTask(); if (!task) return; emit currentTaskChanged(task); } bool PageView::eventFilter(QObject *object, QEvent *event) { Q_ASSERT(object == m_centralView); switch(event->type()) { case QEvent::FocusIn: m_cancelAction->setEnabled(false); break; case QEvent::FocusOut: m_cancelAction->setEnabled(true); break; default: break; } return false; } Domain::Task::Ptr PageView::currentTask() const { const auto current = m_centralView->selectionModel()->currentIndex(); const auto data = current.data(Presentation::QueryTreeModelBase::ObjectRole); if (!data.isValid()) return Domain::Task::Ptr(); return data.value(); } #include "pageview.moc" diff --git a/src/widgets/pageview.h b/src/widgets/pageview.h index 00ecc251..d2de459d 100644 --- a/src/widgets/pageview.h +++ b/src/widgets/pageview.h @@ -1,105 +1,105 @@ /* 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 WIDGETS_PAGEVIEW_H #define WIDGETS_PAGEVIEW_H #include #include #include #include #include #include "domain/task.h" #include "messageboxinterface.h" class QLineEdit; class QModelIndex; class QMessageBox; class KMessageWidget; namespace Presentation { class RunningTaskModelInterface; } namespace Widgets { class FilterWidget; class PageTreeView; class PageView : public QWidget { Q_OBJECT public: - explicit PageView(QWidget *parent = Q_NULLPTR); + explicit PageView(QWidget *parent = nullptr); QHash globalActions() const; QObject *model() const; Presentation::RunningTaskModelInterface *runningTaskModel() const; MessageBoxInterface::Ptr messageBoxInterface() const; QModelIndexList selectedIndexes() const; public slots: void setModel(QObject *model); void setRunningTaskModel(Presentation::RunningTaskModelInterface *model); void setMessageBoxInterface(const MessageBoxInterface::Ptr &interface); void displayErrorMessage(const QString &message); signals: void currentTaskChanged(const Domain::Task::Ptr &task); private slots: void onReturnPressed(); void onAddItemRequested(); void onRemoveItemRequested(); void onPromoteItemRequested(); void onFilterToggled(bool show); void onCurrentChanged(const QModelIndex ¤t); void onRunTaskTriggered(); void onRunningTaskChanged(const Domain::Task::Ptr &task); private: bool eventFilter(QObject *object, QEvent *event) override; void updateRunTaskAction(); Domain::Task::Ptr currentTask() const; QHash m_actions; QAction *m_cancelAction; QAction *m_runTaskAction; QObject *m_model; KMessageWidget *m_messageWidget; FilterWidget *m_filterWidget; PageTreeView *m_centralView; QLineEdit *m_quickAddEdit; MessageBoxInterface::Ptr m_messageBoxInterface; Presentation::RunningTaskModelInterface *m_runningTaskModel; }; } #endif // WIDGETS_PAGEVIEW_H diff --git a/src/widgets/pageviewerrorhandler.cpp b/src/widgets/pageviewerrorhandler.cpp index cc082eaf..b6685106 100644 --- a/src/widgets/pageviewerrorhandler.cpp +++ b/src/widgets/pageviewerrorhandler.cpp @@ -1,49 +1,49 @@ /* This file is part of Zanshin Copyright 2016 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 "pageviewerrorhandler.h" #include "widgets/pageview.h" using namespace Widgets; PageViewErrorHandler::PageViewErrorHandler() - : m_pageView(Q_NULLPTR) + : m_pageView(nullptr) { } Widgets::PageView *PageViewErrorHandler::pageView() const { return m_pageView; } void PageViewErrorHandler::setPageView(Widgets::PageView *pageView) { m_pageView = pageView; } void Widgets::PageViewErrorHandler::doDisplayMessage(const QString &message) { if (m_pageView) m_pageView->displayErrorMessage(message); } diff --git a/src/widgets/quickselectdialog.cpp b/src/widgets/quickselectdialog.cpp index 4fa2c56b..625da602 100644 --- a/src/widgets/quickselectdialog.cpp +++ b/src/widgets/quickselectdialog.cpp @@ -1,129 +1,129 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens Copyright 2015 Franck Arrecot 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 "quickselectdialog.h" #include #include #include #include #include #include #include #include #include using namespace Widgets; QuickSelectDialog::QuickSelectDialog(QWidget *parent) : QDialog(parent), - m_model(Q_NULLPTR), + m_model(nullptr), m_filterProxyModel(new KRecursiveFilterProxyModel(this)), m_label(new QLabel(this)), m_tree(new QTreeView(this)) { setWindowTitle(i18n("Quick Select Dialog")); m_label->setText(i18n("You can start typing to filter the list of available pages")); m_filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_tree->setModel(m_filterProxyModel); m_tree->setObjectName(QStringLiteral("pagesView")); m_tree->header()->hide(); m_tree->expandAll(); m_tree->setFocus(); m_tree->setSelectionMode(QAbstractItemView::SingleSelection); m_tree->setSortingEnabled(false); m_tree->installEventFilter(this); auto buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); auto mainLayout = new QVBoxLayout(this); mainLayout->addWidget(m_label); mainLayout->addWidget(m_tree); mainLayout->addWidget(buttonBox); connect(buttonBox, &QDialogButtonBox::accepted, this, &QuickSelectDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QuickSelectDialog::reject); } int QuickSelectDialog::exec() { return QDialog::exec(); } QPersistentModelIndex QuickSelectDialog::selectedIndex() const { QModelIndex selected = m_tree->currentIndex(); return m_filterProxyModel->mapToSource(selected); } void QuickSelectDialog::applyFilterChanged(const QString &textFilter) { if (textFilter.isEmpty()) m_label->setText(i18n("You can start typing to filter the list of available pages")); else m_label->setText(i18n("Path: %1", textFilter)); m_filterProxyModel->setFilterFixedString(textFilter); m_tree->expandAll(); } bool QuickSelectDialog::eventFilter(QObject *, QEvent *ev) { if (ev->type() == QEvent::KeyPress) { auto event = static_cast(ev); auto filter = m_filterProxyModel->filterRegExp().pattern(); switch (event->key()) { case Qt::Key_Backspace: filter.chop(1); break; case Qt::Key_Delete: filter = QString(); break; default: if (event->text().contains(QRegExp("^(\\w| )+$"))) { filter += event->text(); } break; } applyFilterChanged(filter); } return false; } void QuickSelectDialog::setModel(QAbstractItemModel *model) { if (model == m_model) return; m_model = model; m_filterProxyModel->setSourceModel(m_model); m_tree->expandAll(); } diff --git a/src/widgets/quickselectdialog.h b/src/widgets/quickselectdialog.h index 4af1818b..84b5a00a 100644 --- a/src/widgets/quickselectdialog.h +++ b/src/widgets/quickselectdialog.h @@ -1,70 +1,70 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens Copyright 2015 Franck Arrecot 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 WIDGETS_QUICKSELECTDIALOG_H #define WIDGETS_QUICKSELECTDIALOG_H #include #include #include #include "presentation/metatypes.h" #include "widgets/quickselectdialoginterface.h" class QAbstractItemModel; class QLabel; class QTreeView; class KRecursiveFilterProxyModel; namespace Widgets { class QuickSelectDialog : public QDialog, public QuickSelectDialogInterface { Q_OBJECT public: - explicit QuickSelectDialog(QWidget *parent = Q_NULLPTR); + explicit QuickSelectDialog(QWidget *parent = nullptr); int exec() override; QPersistentModelIndex selectedIndex() const override; void setModel(QAbstractItemModel *model) override; private slots: void applyFilterChanged(const QString &textFilter); bool eventFilter(QObject *object, QEvent *ev) override; private: QString m_filter; QAbstractItemModel *m_model; KRecursiveFilterProxyModel *m_filterProxyModel; QLabel *m_label; QTreeView *m_tree; }; } #endif // WIDGETS_QUICKSELECTDIALOG_H diff --git a/src/widgets/runningtaskwidget.h b/src/widgets/runningtaskwidget.h index 445d5c53..af1141ac 100644 --- a/src/widgets/runningtaskwidget.h +++ b/src/widgets/runningtaskwidget.h @@ -1,78 +1,78 @@ /* This file is part of Zanshin Copyright 2016 David Faure 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 RUNNINGTASKWIDGET_H #define RUNNINGTASKWIDGET_H #include #include "domain/task.h" class QLabel; class QHBoxLayout; class QPushButton; namespace Presentation { class RunningTaskModelInterface; } namespace Widgets { class RunningTaskWidget : public QWidget { Q_OBJECT public: - explicit RunningTaskWidget(QWidget *parent = Q_NULLPTR); + explicit RunningTaskWidget(QWidget *parent = nullptr); void setModel(Presentation::RunningTaskModelInterface *model); Presentation::RunningTaskModelInterface *model() const; QString currentText() const; // for the unittest private slots: // connected to the model void onRunningTaskChanged(const Domain::Task::Ptr &task); // connected to the push buttons void onTaskRunStopped(); void onTaskRunDone(); void setCollapsed(bool b); protected: void enterEvent(QEvent *ev) override; void leaveEvent(QEvent *ev) override; private: void resize(); Presentation::RunningTaskModelInterface *m_model; QHBoxLayout *m_layout; QLabel *m_titleLabel; QPushButton *m_stopButton; QPushButton *m_doneButton; bool m_collapsed; }; } #endif // RUNNINGTASKWIDGET_H diff --git a/src/widgets/scripteditor.h b/src/widgets/scripteditor.h index a835cf30..48345af3 100644 --- a/src/widgets/scripteditor.h +++ b/src/widgets/scripteditor.h @@ -1,57 +1,57 @@ /* This file is part of Zanshin Copyright 2015 Theo Vaucher 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 WIDGETS_SCRIPTEDITOR_H #define WIDGETS_SCRIPTEDITOR_H #include #include "presentation/metatypes.h" class ScriptHandler; class QTextEdit; namespace Widgets { class ScriptEditor : public QMainWindow { Q_OBJECT public: - explicit ScriptEditor(QWidget *parent = Q_NULLPTR); + explicit ScriptEditor(QWidget *parent = nullptr); virtual ~ScriptEditor(); QObjectPtr scriptHandler() const; public slots: void setScriptHandler(const QObjectPtr &scriptHandler); private: QObjectPtr m_scriptHandler; QTextEdit *m_textEdit; }; } #endif // WIDGETS_SCRIPTEDITOR_H diff --git a/tests/features/cuke-steps.cpp b/tests/features/cuke-steps.cpp index f4f9a970..f5eb54a8 100644 --- a/tests/features/cuke-steps.cpp +++ b/tests/features/cuke-steps.cpp @@ -1,894 +1,894 @@ /* 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 #include #include #include #include #include #include #include #include #include #include "presentation/applicationmodel.h" #include "presentation/errorhandler.h" #include "presentation/querytreemodelbase.h" #include "akonadi/akonadiapplicationselectedattribute.h" #include "akonadi/akonadicache.h" #include "akonadi/akonadicachingstorage.h" #include "akonadi/akonadimonitorimpl.h" #include "akonadi/akonaditimestampattribute.h" #include "utils/dependencymanager.h" #include "utils/jobhandler.h" #include "testlib/akonadifakedata.h" #include "testlib/akonadifakedataxmlloader.h" #include "testlib/monitorspy.h" #include "testlib/testsafety.h" static int argc = 1; static char *argv0 = "cuke-steps"; static QApplication app(argc, &argv0); namespace CukeSteps { void initializeAppDependencies(); } namespace cucumber { namespace internal { template<> inline QString fromString(const std::string& s) { return QString::fromUtf8(s.data()); } } } using namespace cucumber; class FakeErrorHandler : public Presentation::ErrorHandler { public: void doDisplayMessage(const QString &) { } }; class ZanshinContext : public QObject { Q_OBJECT public: - explicit ZanshinContext(QObject *parent = Q_NULLPTR) + explicit ZanshinContext(QObject *parent = nullptr) : QObject(parent), app(), - presentation(Q_NULLPTR), - editor(Q_NULLPTR), + presentation(nullptr), + editor(nullptr), proxyModel(new QSortFilterProxyModel(this)), - m_model(Q_NULLPTR), - m_sourceModel(Q_NULLPTR), - monitorSpy(Q_NULLPTR) + m_model(nullptr), + m_sourceModel(nullptr), + monitorSpy(nullptr) { qputenv("ZANSHIN_OVERRIDE_DATE", "2015-03-10"); static bool initializedDependencies = false; if (!initializedDependencies) { CukeSteps::initializeAppDependencies(); MonitorSpy::setExpirationDelay(200); initializedDependencies = true; } Akonadi::AttributeFactory::registerAttribute(); Akonadi::AttributeFactory::registerAttribute(); const auto xmlFile = QString::fromLocal8Bit(qgetenv("ZANSHIN_USER_XMLDATA")); if (xmlFile.isEmpty()) { qDebug() << "FATAL ERROR! ZANSHIN_USER_XMLDATA WAS NOT PROVIDED\n\n"; exit(1); } auto searchCollection = Akonadi::Collection(1); searchCollection.setParentCollection(Akonadi::Collection::root()); searchCollection.setName(QStringLiteral("Search")); m_data.createCollection(searchCollection); auto loader = Testlib::AkonadiFakeDataXmlLoader(&m_data); loader.load(xmlFile); // Swap regular dependencies for the fake data ones auto &deps = Utils::DependencyManager::globalInstance(); deps.add( [this] (Utils::DependencyManager *) { return m_data.createMonitor(); } ); deps.add( [this] (Utils::DependencyManager *deps) { return new Akonadi::CachingStorage(deps->create(), Akonadi::StorageInterface::Ptr(m_data.createStorage())); } ); using namespace Presentation; proxyModel->setDynamicSortFilter(true); auto appModel = ApplicationModel::Ptr::create(); appModel->setErrorHandler(&m_errorHandler); app = appModel; auto monitor = Utils::DependencyManager::globalInstance().create(); monitorSpy = new MonitorSpy(monitor.data(), this); } ~ZanshinContext() { } // Note that setModel might invalidate the 'index' member variable, due to proxyModel->setSourceModel. void setModel(QAbstractItemModel *model) { if (m_sourceModel == model) return; m_sourceModel = model; if (!qobject_cast(model)) { proxyModel->setObjectName(QStringLiteral("m_proxyModel_in_ZanshinContext")); proxyModel->setSourceModel(model); proxyModel->setSortRole(Qt::DisplayRole); proxyModel->sort(0); m_model = proxyModel; } else { m_model = model; } } QAbstractItemModel *sourceModel() { return m_sourceModel; } QAbstractItemModel *model() { return m_model; } Domain::Task::Ptr currentTask() const { return index.data(Presentation::QueryTreeModelBase::ObjectRole) .value(); } void waitForEmptyJobQueue() { while (Utils::JobHandler::jobCount() != 0) { QTest::qWait(20); } } void waitForStableState() { waitForEmptyJobQueue(); monitorSpy->waitForStableState(); } QObjectPtr app; QList indices; QPersistentModelIndex index; QObject *presentation; QObject *editor; QList dragIndices; private: Testlib::AkonadiFakeData m_data; QSortFilterProxyModel *proxyModel; QAbstractItemModel *m_model; QAbstractItemModel *m_sourceModel; MonitorSpy *monitorSpy; FakeErrorHandler m_errorHandler; }; namespace Zanshin { QString indexString(const QModelIndex &index, int role = Qt::DisplayRole) { if (role != Qt::DisplayRole) return index.data(role).toString(); QString data = index.data(role).toString(); if (index.parent().isValid()) return indexString(index.parent(), role) + " / " + data; else return data; } QModelIndex findIndex(QAbstractItemModel *model, const QString &string, int role = Qt::DisplayRole, const QModelIndex &root = QModelIndex()) { for (int row = 0; row < model->rowCount(root); row++) { const QModelIndex index = model->index(row, 0, root); if (indexString(index, role) == string) return index; if (model->rowCount(index) > 0) { const QModelIndex found = findIndex(model, string, role, index); if (found.isValid()) return found; } } return QModelIndex(); } static void collectIndicesImpl(ZanshinContext *context, const QModelIndex &root = QModelIndex()) { QAbstractItemModel *model = context->model(); for (int row = 0; row < model->rowCount(root); row++) { const QModelIndex index = model->index(row, 0, root); context->indices << index; if (model->rowCount(index) > 0) collectIndicesImpl(context, index); } } static void collectIndices(ZanshinContext *context) { context->indices.clear(); collectIndicesImpl(context); } void dumpIndices(const QList &indices) { qDebug() << "Dumping list of size:" << indices.size(); for (int row = 0; row < indices.size(); row++) { qDebug() << row << indexString(indices.at(row)); } } inline bool verify(bool statement, const char *str, const char *file, int line) { if (statement) return true; qDebug() << "Statement" << str << "returned FALSE"; qDebug() << "Loc:" << file << line; return false; } template inline bool compare(T const &t1, T const &t2, const char *actual, const char *expected, const char *file, int line) { if (t1 == t2) return true; qDebug() << "Compared values are not the same"; qDebug() << "Actual (" << actual << ") :" << QTest::toString(t1); qDebug() << "Expected (" << expected << ") :" << QTest::toString(t2); qDebug() << "Loc:" << file << line; return false; } } // namespace Zanshin #define COMPARE(actual, expected) \ do {\ if (!Zanshin::compare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ BOOST_REQUIRE(false);\ } while (0) // Note: you should make sure that context->indices is filled in before calling this, // e.g. calling Zanshin::collectIndices(context.get()) if not already done. #define COMPARE_OR_DUMP(actual, expected) \ do {\ if (!Zanshin::compare(actual, expected, #actual, #expected, __FILE__, __LINE__)) {\ Zanshin::dumpIndices(context->indices); \ BOOST_REQUIRE(false);\ }\ } while (0) #define VERIFY(statement) \ do {\ if (!Zanshin::verify((statement), #statement, __FILE__, __LINE__))\ BOOST_REQUIRE(false);\ } while (0) // Note: you should make sure that context->indices is filled in before calling this, // e.g. calling Zanshin::collectIndices(context.get()) if not already done. #define VERIFY_OR_DUMP(statement) \ do {\ if (!Zanshin::verify((statement), #statement, __FILE__, __LINE__)) {\ Zanshin::dumpIndices(context->indices); \ BOOST_REQUIRE(false);\ }\ } while (0) #define VERIFY_OR_DO(statement, whatToDo) \ do {\ if (!Zanshin::verify((statement), #statement, __FILE__, __LINE__)) {\ whatToDo; \ BOOST_REQUIRE(false);\ }\ } while (0) GIVEN("^I display the available data sources$") { ScenarioScope context; auto availableSources = context->app->property("availableSources").value(); VERIFY(availableSources); auto sourceListModel = availableSources->property("sourceListModel").value(); VERIFY(sourceListModel); context->presentation = availableSources; context->setModel(sourceListModel); } GIVEN("^I display the available pages$") { ScenarioScope context; context->presentation = context->app->property("availablePages").value(); context->setModel(context->presentation->property("pageListModel").value()); } GIVEN("^I display the \"(.*)\" page$") { REGEX_PARAM(QString, pageName); ScenarioScope context; auto availablePages = context->app->property("availablePages").value(); VERIFY(availablePages); auto pageListModel = availablePages->property("pageListModel").value(); VERIFY(pageListModel); context->waitForEmptyJobQueue(); QModelIndex pageIndex = Zanshin::findIndex(pageListModel, pageName); VERIFY(pageIndex.isValid()); - QObject *page = Q_NULLPTR; + QObject *page = nullptr; QMetaObject::invokeMethod(availablePages, "createPageForIndex", Q_RETURN_ARG(QObject*, page), Q_ARG(QModelIndex, pageIndex)); VERIFY(page); VERIFY(context->app->setProperty("currentPage", QVariant::fromValue(page))); context->presentation = context->app->property("currentPage").value(); } GIVEN("^there is an item named \"(.+)\" in the central list$") { REGEX_PARAM(QString, itemName); ScenarioScope context; auto model = context->presentation->property("centralListModel").value(); context->setModel(model); context->waitForEmptyJobQueue(); Zanshin::collectIndices(context.get()); context->index = Zanshin::findIndex(context->model(), itemName); VERIFY_OR_DUMP(context->index.isValid()); } GIVEN("^there is an item named \"(.+)\" in the available data sources$") { REGEX_PARAM(QString, itemName); ScenarioScope context; auto availableSources = context->app->property("availableSources").value(); VERIFY(availableSources); auto model = availableSources->property("sourceListModel").value(); VERIFY(model); context->waitForEmptyJobQueue(); context->setModel(model); Zanshin::collectIndices(context.get()); context->index = Zanshin::findIndex(context->model(), itemName); VERIFY_OR_DUMP(context->index.isValid()); } GIVEN("^the central list contains items named:") { TABLE_PARAM(tableParam); ScenarioScope context; context->dragIndices.clear(); auto model = context->presentation->property("centralListModel").value(); context->waitForEmptyJobQueue(); context->setModel(model); for (const auto &row : tableParam.hashes()) { for (const auto &it : row) { const QString itemName = QString::fromUtf8(it.second.data()); QModelIndex index = Zanshin::findIndex(context->model(), itemName); VERIFY_OR_DO(index.isValid(), Zanshin::dumpIndices(context->dragIndices)); context->dragIndices << index; } } } WHEN("^I look at the central list$") { ScenarioScope context; auto model = context->presentation->property("centralListModel").value(); context->setModel(model); context->waitForStableState(); } WHEN("^I check the item$") { ScenarioScope context; VERIFY(context->model()->setData(context->index, Qt::Checked, Qt::CheckStateRole)); context->waitForStableState(); } WHEN("^I uncheck the item$") { ScenarioScope context; VERIFY(context->model()->setData(context->index, Qt::Unchecked, Qt::CheckStateRole)); context->waitForStableState(); } WHEN("^I remove the item$") { ScenarioScope context; VERIFY(QMetaObject::invokeMethod(context->presentation, "removeItem", Q_ARG(QModelIndex, context->index))); context->waitForStableState(); } WHEN("^I promote the item$") { ScenarioScope context; VERIFY(QMetaObject::invokeMethod(context->presentation, "promoteItem", Q_ARG(QModelIndex, context->index))); context->waitForStableState(); } WHEN("^I add a project named \"(.*)\" in the source named \"(.*)\"$") { REGEX_PARAM(QString, projectName); REGEX_PARAM(QString, sourceName); ScenarioScope context; auto availableSources = context->app->property("availableSources").value(); VERIFY(availableSources); auto sourceList = availableSources->property("sourceListModel").value(); VERIFY(sourceList); context->waitForStableState(); QModelIndex index = Zanshin::findIndex(sourceList, sourceName); VERIFY(index.isValid()); auto source = index.data(Presentation::QueryTreeModelBase::ObjectRole) .value(); VERIFY(source); VERIFY(QMetaObject::invokeMethod(context->presentation, "addProject", Q_ARG(QString, projectName), Q_ARG(Domain::DataSource::Ptr, source))); context->waitForStableState(); } WHEN("^I rename the page named \"(.*)\" under \"(.*)\" to \"(.*)\"$") { REGEX_PARAM(QString, oldName); REGEX_PARAM(QString, path); REGEX_PARAM(QString, newName); const QString pageNodeName = path + " / "; VERIFY(!pageNodeName.isEmpty()); ScenarioScope context; auto availablePages = context->app->property("availablePages").value(); VERIFY(availablePages); auto pageListModel = availablePages->property("pageListModel").value(); VERIFY(pageListModel); context->waitForStableState(); QModelIndex pageIndex = Zanshin::findIndex(pageListModel, pageNodeName + oldName); VERIFY(pageIndex.isValid()); pageListModel->setData(pageIndex, newName); context->waitForStableState(); } WHEN("^I remove the page named \"(.*)\" under \"(.*)\"$") { REGEX_PARAM(QString, name); REGEX_PARAM(QString, path); const QString pageNodeName = path + " / "; VERIFY(!pageNodeName.isEmpty()); ScenarioScope context; auto availablePages = context->app->property("availablePages").value(); VERIFY(availablePages); auto pageListModel = availablePages->property("pageListModel").value(); VERIFY(pageListModel); context->waitForStableState(); QModelIndex pageIndex = Zanshin::findIndex(pageListModel, pageNodeName + name); VERIFY(pageIndex.isValid()); VERIFY(QMetaObject::invokeMethod(availablePages, "removeItem", Q_ARG(QModelIndex, pageIndex))); context->waitForStableState(); } WHEN("^I add a \"(.*)\" named \"(.+)\"$") { REGEX_PARAM(QString, objectType); REGEX_PARAM(QString, objectName); QByteArray actionName = (objectType == QStringLiteral("context")) ? "addContext" : (objectType == QStringLiteral("note")) ? "addItem" : (objectType == QStringLiteral("task")) ? "addItem" : (objectType == QStringLiteral("tag")) ? "addTag" : QByteArray(); VERIFY(!actionName.isEmpty()); ScenarioScope context; context->waitForStableState(); VERIFY(QMetaObject::invokeMethod(context->presentation, actionName.data(), Q_ARG(QString, objectName))); context->waitForStableState(); } WHEN("^I add a child named \"(.+)\" under the task named \"(.+)\"$") { REGEX_PARAM(QString, childName); REGEX_PARAM(QString, parentName); ScenarioScope context; context->waitForStableState(); auto parentIndex = QModelIndex(); for (int row = 0; row < context->indices.size(); row++) { auto index = context->indices.at(row); if (Zanshin::indexString(index) == parentName) { parentIndex = index; break; } } VERIFY_OR_DUMP(parentIndex.isValid()); VERIFY(QMetaObject::invokeMethod(context->presentation, "addItem", Q_ARG(QString, childName), Q_ARG(QModelIndex, parentIndex))); context->waitForStableState(); } WHEN("^I list the items$") { ScenarioScope context; context->waitForStableState(); Zanshin::collectIndices(context.get()); context->waitForStableState(); } WHEN("^I open the item in the editor$") { ScenarioScope context; auto task = context->currentTask(); VERIFY(task); context->editor = context->app->property("editor").value(); VERIFY(context->editor); VERIFY(context->editor->setProperty("task", QVariant::fromValue(task))); } WHEN("^I mark it done in the editor$") { ScenarioScope context; VERIFY(context->editor->setProperty("done", true)); } WHEN("^I change the editor (.*) to \"(.*)\"$") { REGEX_PARAM(QString, field); REGEX_PARAM(QString, string); const QVariant value = (field == QStringLiteral("text")) ? string : (field == QStringLiteral("title")) ? string : (field == QStringLiteral("start date")) ? QDateTime::fromString(string, Qt::ISODate) : (field == QStringLiteral("due date")) ? QDateTime::fromString(string, Qt::ISODate) : QVariant(); const QByteArray property = (field == QStringLiteral("text")) ? field.toUtf8() : (field == QStringLiteral("title")) ? field.toUtf8() : (field == QStringLiteral("start date")) ? "startDate" : (field == QStringLiteral("due date")) ? "dueDate" : QByteArray(); VERIFY(value.isValid()); VERIFY(!property.isEmpty()); ScenarioScope context; VERIFY(context->editor->setProperty("editingInProgress", true)); VERIFY(context->editor->setProperty(property, value)); } WHEN("^I rename the item to \"(.+)\"$") { REGEX_PARAM(QString, title); ScenarioScope context; VERIFY(context->editor->setProperty("editingInProgress", false)); VERIFY(context->model()->setData(context->index, title, Qt::EditRole)); context->waitForStableState(); } WHEN("^I open the item in the editor again$") { ScenarioScope context; auto task = context->currentTask(); VERIFY(task); VERIFY(context->editor->setProperty("task", QVariant::fromValue(Domain::Task::Ptr()))); VERIFY(context->editor->setProperty("task", QVariant::fromValue(task))); context->waitForStableState(); } WHEN("^I drop the item on \"(.*)\" in the central list") { REGEX_PARAM(QString, itemName); ScenarioScope context; VERIFY(context->index.isValid()); const QMimeData *data = context->model()->mimeData(QModelIndexList() << context->index); QAbstractItemModel *destModel = context->model(); QModelIndex dropIndex = Zanshin::findIndex(destModel, itemName); VERIFY(dropIndex.isValid()); VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, dropIndex)); context->waitForStableState(); } WHEN("^I drop the item on the blank area of the central list") { ScenarioScope context; VERIFY(context->index.isValid()); const QMimeData *data = context->model()->mimeData(QModelIndexList() << context->index); QAbstractItemModel *destModel = context->model(); VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, QModelIndex())); context->waitForStableState(); } WHEN("^I drop items on \"(.*)\" in the central list") { REGEX_PARAM(QString, itemName); ScenarioScope context; VERIFY(!context->dragIndices.isEmpty()); QModelIndexList indexes; std::transform(context->dragIndices.constBegin(), context->dragIndices.constEnd(), std::back_inserter(indexes), [] (const QPersistentModelIndex &index) { VERIFY(index.isValid()); return index; }); const QMimeData *data = context->model()->mimeData(indexes); QAbstractItemModel *destModel = context->model(); QModelIndex dropIndex = Zanshin::findIndex(destModel, itemName); VERIFY(dropIndex.isValid()); VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, dropIndex)); context->waitForStableState(); } WHEN("^I drop the item on \"(.*)\" in the page list") { REGEX_PARAM(QString, itemName); ScenarioScope context; VERIFY(context->index.isValid()); const QMimeData *data = context->model()->mimeData(QModelIndexList() << context->index); auto availablePages = context->app->property("availablePages").value(); VERIFY(availablePages); auto destModel = availablePages->property("pageListModel").value(); VERIFY(destModel); context->waitForStableState(); QModelIndex dropIndex = Zanshin::findIndex(destModel, itemName); VERIFY(dropIndex.isValid()); VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, dropIndex)); context->waitForStableState(); } WHEN("^I drop items on \"(.*)\" in the page list") { REGEX_PARAM(QString, itemName); ScenarioScope context; VERIFY(!context->dragIndices.isEmpty()); QModelIndexList indexes; std::transform(context->dragIndices.constBegin(), context->dragIndices.constEnd(), std::back_inserter(indexes), [] (const QPersistentModelIndex &index) { VERIFY(index.isValid()); return index; }); const QMimeData *data = context->model()->mimeData(indexes); auto availablePages = context->app->property("availablePages").value(); VERIFY(availablePages); auto destModel = availablePages->property("pageListModel").value(); VERIFY(destModel); context->waitForStableState(); QModelIndex dropIndex = Zanshin::findIndex(destModel, itemName); VERIFY(dropIndex.isValid()); VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, dropIndex)); context->waitForStableState(); } WHEN("^the setting key (\\S+) changes to (\\d+)$") { REGEX_PARAM(QString, keyName); REGEX_PARAM(qint64, id); ScenarioScope context; KConfigGroup config(KSharedConfig::openConfig(), "General"); config.writeEntry(keyName, id); } WHEN("^the user changes the default data source to \"(.*)\"$") { REGEX_PARAM(QString, sourceName); ScenarioScope context; context->waitForStableState(); auto sourceIndex = Zanshin::findIndex(context->model(), sourceName); auto availableSources = context->app->property("availableSources").value(); VERIFY(availableSources); VERIFY(QMetaObject::invokeMethod(availableSources, "setDefaultItem", Q_ARG(QModelIndex, sourceIndex))); context->waitForStableState(); } THEN("^the list is") { TABLE_PARAM(tableParam); ScenarioScope context; auto roleNames = context->model()->roleNames(); QSet usedRoles; QStandardItemModel inputModel; for (const auto &row : tableParam.hashes()) { QStandardItem *item = new QStandardItem; for (const auto &it : row) { const QByteArray roleName = it.first.data(); const QString value = QString::fromUtf8(it.second.data()); const int role = roleNames.key(roleName, -1); VERIFY_OR_DUMP(role != -1); item->setData(value, role); usedRoles.insert(role); } inputModel.appendRow(item); } QSortFilterProxyModel proxy; QAbstractItemModel *referenceModel; if (!qobject_cast(context->sourceModel())) { referenceModel = &proxy; proxy.setSourceModel(&inputModel); proxy.setSortRole(Qt::DisplayRole); proxy.sort(0); proxy.setObjectName(QStringLiteral("the_list_is_proxy")); } else { referenceModel = &inputModel; } for (int row = 0; row < context->indices.size(); row++) { QModelIndex expectedIndex = referenceModel->index(row, 0); QModelIndex resultIndex = context->indices.at(row); foreach (const auto &role, usedRoles) { COMPARE_OR_DUMP(Zanshin::indexString(resultIndex, role), Zanshin::indexString(expectedIndex, role)); } } COMPARE_OR_DUMP(context->indices.size(), referenceModel->rowCount()); } THEN("^the list contains \"(.+)\"$") { REGEX_PARAM(QString, itemName); ScenarioScope context; for (int row = 0; row < context->indices.size(); row++) { if (Zanshin::indexString(context->indices.at(row)) == itemName) return; } VERIFY_OR_DUMP(false); } THEN("^the list does not contain \"(.+)\"$") { REGEX_PARAM(QString, itemName); ScenarioScope context; for (int row = 0; row < context->indices.size(); row++) { VERIFY_OR_DUMP(Zanshin::indexString(context->indices.at(row)) != itemName); } } THEN("^the task corresponding to the item is done$") { ScenarioScope context; auto task = context->currentTask(); VERIFY(task); VERIFY(task->isDone()); } THEN("^the editor shows the task as done$") { ScenarioScope context; VERIFY(context->editor->property("done").toBool()); } THEN("^the editor shows \"(.*)\" as (.*)$") { REGEX_PARAM(QString, string); REGEX_PARAM(QString, field); const QVariant value = (field == QStringLiteral("text")) ? string : (field == QStringLiteral("title")) ? string : (field == QStringLiteral("start date")) ? QDateTime::fromString(string, Qt::ISODate) : (field == QStringLiteral("due date")) ? QDateTime::fromString(string, Qt::ISODate) : QVariant(); const QByteArray property = (field == QStringLiteral("text")) ? field.toUtf8() : (field == QStringLiteral("title")) ? field.toUtf8() : (field == QStringLiteral("start date")) ? "startDate" : (field == QStringLiteral("due date")) ? "dueDate" : QByteArray(); VERIFY(value.isValid()); VERIFY(!property.isEmpty()); ScenarioScope context; COMPARE(context->editor->property(property), value); } THEN("^the default data source is \"(.*)\"$") { REGEX_PARAM(QString, expectedName); ScenarioScope context; context->waitForStableState(); auto expectedIndex = Zanshin::findIndex(context->model(), expectedName); VERIFY(expectedIndex.isValid()); auto defaultRole = context->model()->roleNames().key("default", -1); VERIFY(expectedIndex.data(defaultRole).toBool()); } THEN("^the setting key (\\S+) is (\\d+)$") { REGEX_PARAM(QString, keyName); REGEX_PARAM(qint64, expectedId); KConfigGroup config(KSharedConfig::openConfig(), "General"); const qint64 id = config.readEntry(keyName, -1); COMPARE(id, expectedId); } #include "cuke-steps.moc" diff --git a/tests/testlib/akonadifakemonitor.h b/tests/testlib/akonadifakemonitor.h index 96f614c6..bb1be7ad 100644 --- a/tests/testlib/akonadifakemonitor.h +++ b/tests/testlib/akonadifakemonitor.h @@ -1,57 +1,57 @@ /* 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 TESTLIB_AKONADIFAKEMONITOR_H #define TESTLIB_AKONADIFAKEMONITOR_H #include "akonadi/akonadimonitorinterface.h" namespace Testlib { class AkonadiFakeMonitor : public Akonadi::MonitorInterface { Q_OBJECT public: typedef QSharedPointer Ptr; - explicit AkonadiFakeMonitor(QObject *parent = Q_NULLPTR); + explicit AkonadiFakeMonitor(QObject *parent = nullptr); public slots: void addCollection(const Akonadi::Collection &collection); void removeCollection(const Akonadi::Collection &collection); void changeCollection(const Akonadi::Collection &collection); void changeCollectionSelection(const Akonadi::Collection &collection); void addItem(const Akonadi::Item &item); void removeItem(const Akonadi::Item &item); void changeItem(const Akonadi::Item &item); void moveItem(const Akonadi::Item &item); void addTag(const Akonadi::Tag &tag); void removeTag(const Akonadi::Tag &tag); void changeTag(const Akonadi::Tag &tag); }; } #endif // TESTLIB_AKONADIFAKEMONITOR_H diff --git a/tests/testlib/akonadifakestorage.h b/tests/testlib/akonadifakestorage.h index 1890affb..6e777457 100644 --- a/tests/testlib/akonadifakestorage.h +++ b/tests/testlib/akonadifakestorage.h @@ -1,74 +1,74 @@ /* 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 = Q_NULLPTR) override; + KJob *updateItem(Akonadi::Item item, QObject *parent = nullptr) override; KJob *removeItem(Akonadi::Item item) override; - KJob *removeItems(Akonadi::Item::List items, QObject *parent = Q_NULLPTR) override; - KJob *moveItem(Akonadi::Item item, Akonadi::Collection collection, QObject *parent = Q_NULLPTR) override; - KJob *moveItems(Akonadi::Item::List items, Akonadi::Collection collection, QObject *parent = Q_NULLPTR) override; + KJob *removeItems(Akonadi::Item::List items, QObject *parent = nullptr) override; + KJob *moveItem(Akonadi::Item item, Akonadi::Collection collection, QObject *parent = nullptr) override; + KJob *moveItems(Akonadi::Item::List items, Akonadi::Collection collection, QObject *parent = nullptr) override; - KJob *createCollection(Akonadi::Collection collection, QObject *parent = Q_NULLPTR) override; - KJob *updateCollection(Akonadi::Collection collection, QObject *parent = Q_NULLPTR) override; - KJob *removeCollection(Akonadi::Collection collection, QObject *parent = Q_NULLPTR) override; + KJob *createCollection(Akonadi::Collection collection, QObject *parent = nullptr) override; + KJob *updateCollection(Akonadi::Collection collection, QObject *parent = nullptr) override; + KJob *removeCollection(Akonadi::Collection collection, QObject *parent = nullptr) override; KJob *createTransaction() override; KJob *createTag(Akonadi::Tag tag) override; KJob *updateTag(Akonadi::Tag tag) override; KJob *removeTag(Akonadi::Tag tag) override; Akonadi::CollectionFetchJobInterface *fetchCollections(Akonadi::Collection collection, FetchDepth depth) override; Akonadi::ItemFetchJobInterface *fetchItems(Akonadi::Collection collection) override; Akonadi::ItemFetchJobInterface *fetchItem(Akonadi::Item item) override; Akonadi::ItemFetchJobInterface *fetchTagItems(Akonadi::Tag tag) override; Akonadi::TagFetchJobInterface *fetchTags() override; private: Akonadi::Tag::Id findId(const Akonadi::Tag &tag); 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.h b/tests/testlib/akonadistoragetestbase.h index 27ac61a2..93a7c969 100644 --- a/tests/testlib/akonadistoragetestbase.h +++ b/tests/testlib/akonadistoragetestbase.h @@ -1,103 +1,103 @@ /* 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. */ #ifndef TESTLIB_AKONADISTORAGETESTBASE_H #define TESTLIB_AKONADISTORAGETESTBASE_H #include "akonadi/akonadimonitorinterface.h" #include "akonadi/akonadistorageinterface.h" Q_DECLARE_METATYPE(Akonadi::StorageInterface::FetchDepth) namespace Testlib { class AkonadiStorageTestBase : public QObject { Q_OBJECT public: - explicit AkonadiStorageTestBase(QObject *parent = Q_NULLPTR); + explicit AkonadiStorageTestBase(QObject *parent = nullptr); protected: virtual Akonadi::StorageInterface::Ptr createStorage() = 0; virtual Akonadi::MonitorInterface::Ptr createMonitor() = 0; private slots: void cleanupTestCase(); void dumpTree(); void shouldListCollections_data(); void shouldListCollections(); void shouldRetrieveAllCollectionAncestors(); void shouldListFullItemsInACollection(); void shouldListTags(); void shouldListItemsAssociatedWithTag(); void shouldNotifyCollectionAdded(); void shouldNotifyCollectionRemoved(); void shouldNotifyCollectionChanged(); void shouldNotifyItemAdded(); void shouldNotifyItemRemoved(); void shouldNotifyItemChanged(); void shouldNotifyItemTagAdded(); void shouldNotifyItemTagRemoved(); void shouldNotifyTagAdded(); void shouldNotifyTagRemoved(); void shouldNotifyTagChanged(); void shouldReadDefaultCollectionFromSettings(); // This test must be run before shouldCreateItem because createItem // sometimes notifies an itemChanged with a delay. So this test might // receive this notification in addition to the itemChanged generated by updateItem. void shouldUpdateItem(); // This test must be run before shouldCreateItem because createItem // sometimes notifies an itemChanged with a delay. So this test might // receive this notification in addition to the itemChanged generated by updateItem. void shouldUseTransaction(); void shouldCreateItem(); void shouldRetrieveItem(); void shouldMoveItem(); void shouldMoveItems(); void shouldDeleteItem(); void shouldDeleteItems(); void shouldCreateTag(); void shouldRemoveTag(); void shouldUpdateTag(); void shouldUpdateCollection(); void shouldNotifyCollectionTimestampChanges(); void shouldNotifyCollectionSelectionChanges(); void shouldNotNotifyCollectionSelectionChangesForIrrelevantCollections(); private: Akonadi::Item fetchItemByRID(const QString &remoteId, const Akonadi::Collection &collection); Akonadi::Collection fetchCollectionByRID(const QString &remoteId); Akonadi::Tag fetchTagByGID(const QString &gid); Akonadi::Collection calendar1(); Akonadi::Collection calendar2(); Akonadi::Collection emails(); }; } #endif // TESTLIB_AKONADISTORAGETESTBASE_H diff --git a/tests/testlib/fakejob.h b/tests/testlib/fakejob.h index 83f6c75b..c3cc5907 100644 --- a/tests/testlib/fakejob.h +++ b/tests/testlib/fakejob.h @@ -1,55 +1,55 @@ /* 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 ZANSHIN_TESTLIB_FAKEJOB_H #define ZANSHIN_TESTLIB_FAKEJOB_H #include class FakeJob : public KJob { Q_OBJECT public: static const int DURATION = 50; - explicit FakeJob(QObject *parent = Q_NULLPTR); + explicit FakeJob(QObject *parent = nullptr); void setExpectedError(int errorCode, const QString &errorText = QString()); void start() override; private slots: virtual void onTimeout(); public: bool isDone() const; int expectedError() const; QString expectedErrorText() const; private: bool m_done; bool m_launched; int m_errorCode; QString m_errorText; }; #endif diff --git a/tests/testlib/modeltest.h b/tests/testlib/modeltest.h index c226adbc..561011f7 100644 --- a/tests/testlib/modeltest.h +++ b/tests/testlib/modeltest.h @@ -1,94 +1,94 @@ /**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef MODELTEST_H #define MODELTEST_H #include #include #include class ModelTest : public QObject { Q_OBJECT public: - ModelTest( QAbstractItemModel *model, QObject *parent = Q_NULLPTR ); + ModelTest( QAbstractItemModel *model, QObject *parent = nullptr ); private Q_SLOTS: void nonDestructiveBasicTest(); void rowCount(); void columnCount(); void hasIndex(); void index(); void parent(); void data(); protected Q_SLOTS: void runAllTests(); void layoutAboutToBeChanged(); void layoutChanged(); void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end ); void rowsInserted( const QModelIndex & parent, int start, int end ); void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end ); void rowsRemoved( const QModelIndex & parent, int start, int end ); private: void checkChildren( const QModelIndex &parent, int currentDepth = 0 ); QAbstractItemModel *model; struct Changing { QModelIndex parent; int oldSize; QVariant last; QVariant next; }; QStack insert; QStack remove; bool fetchingMore; QList changing; }; #endif diff --git a/tests/testlib/monitorspy.h b/tests/testlib/monitorspy.h index 61ccc33b..9ca70083 100644 --- a/tests/testlib/monitorspy.h +++ b/tests/testlib/monitorspy.h @@ -1,53 +1,53 @@ /* This file is part of Zanshin Copyright 2014 Mario Bensi 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 ZANSHIN_TESTLIB_MONITORSPY_H #define ZANSHIN_TESTLIB_MONITORSPY_H #include "akonadi/akonadimonitorimpl.h" #include #include class MonitorSpy : public QObject { Q_OBJECT public: static int expirationDelay(); static void setExpirationDelay(int value); - explicit MonitorSpy(Akonadi::MonitorInterface *monitor, QObject *parent = Q_NULLPTR); + explicit MonitorSpy(Akonadi::MonitorInterface *monitor, QObject *parent = nullptr); void waitForStableState(); private slots: void restartTimer(); void onDelayExpired(); private: Akonadi::MonitorInterface *m_monitor; QTimer *m_timer; bool m_isFinished; }; #endif diff --git a/tests/units/akonadi/akonadicachingstorageintegrationtest.cpp b/tests/units/akonadi/akonadicachingstorageintegrationtest.cpp index 503157c3..2295e7f7 100644 --- a/tests/units/akonadi/akonadicachingstorageintegrationtest.cpp +++ b/tests/units/akonadi/akonadicachingstorageintegrationtest.cpp @@ -1,65 +1,65 @@ /* This file is part of Zanshin 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 #include #include #include "akonadi/akonadicachingstorage.h" #include "akonadi/akonadimonitorimpl.h" #include "akonadi/akonadiserializer.h" #include "akonadi/akonadistorage.h" class AkonadiCachingStorageIntegrationTest : public Testlib::AkonadiStorageTestBase { Q_OBJECT public: - explicit AkonadiCachingStorageIntegrationTest(QObject *parent = Q_NULLPTR) + explicit AkonadiCachingStorageIntegrationTest(QObject *parent = nullptr) : AkonadiStorageTestBase(parent) { } Akonadi::StorageInterface::Ptr createStorage() override { auto serializer = Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer); return Akonadi::StorageInterface::Ptr(new Akonadi::CachingStorage(Akonadi::Cache::Ptr::create(serializer, createMonitor()), Akonadi::StorageInterface::Ptr(new Akonadi::Storage))); } Akonadi::MonitorInterface::Ptr createMonitor() override { return Akonadi::MonitorInterface::Ptr(new Akonadi::MonitorImpl); } private slots: void initTestCase() { QVERIFY(TestLib::TestSafety::checkTestIsIsolated()); } }; ZANSHIN_TEST_MAIN(AkonadiCachingStorageIntegrationTest) #include "akonadicachingstorageintegrationtest.moc" diff --git a/tests/units/akonadi/akonadicontextrepositorytest.cpp b/tests/units/akonadi/akonadicontextrepositorytest.cpp index 23bcd76c..498d0dfb 100644 --- a/tests/units/akonadi/akonadicontextrepositorytest.cpp +++ b/tests/units/akonadi/akonadicontextrepositorytest.cpp @@ -1,340 +1,340 @@ /* 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*) Q_DECLARE_METATYPE(Testlib::AkonadiFakeTagFetchJob*) class AkonadiContextRepositoryTest : public QObject { Q_OBJECT private slots: void shouldCreateContext() { // GIVEN // A Context and its corresponding Tag not existing in akonadi Akonadi::Tag tag; auto context = Domain::Context::Ptr::create(); // A mock creating job auto tagCreateJob = new FakeJob(this); // Storage mock returning the tagCreatejob Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::createTag).when(tag) .thenReturn(tagCreateJob); // Serializer mock Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createTagFromContext).when(context).thenReturn(tag); // WHEN QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->create(context)->exec(); //THEN QVERIFY(storageMock(&Akonadi::StorageInterface::createTag).when(tag).exactly(1)); } void shouldUpdateContext() { // GIVEN Akonadi::Tag tag; tag.setName(QStringLiteral("tag42")); tag.setId(42); auto context = Domain::Context::Ptr::create(); // A mock creating job auto tagModifyJob = new FakeJob(this); // Storage mock returning the tagCreatejob Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::updateTag).when(tag) .thenReturn(tagModifyJob); // Serializer mock Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createTagFromContext).when(context).thenReturn(tag); // WHEN QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->update(context)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createTagFromContext).when(context).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::updateTag).when(tag).exactly(1)); } void shouldRemoveContext() { // GIVEN Akonadi::Tag tag; tag.setName(QStringLiteral("tag42")); tag.setId(42); auto context = Domain::Context::Ptr::create(); // A mock creating job auto tagDeleteJob= new FakeJob(this); // Storage mock returning the tagCreatejob Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::removeTag).when(tag) .thenReturn(tagDeleteJob); // Serializer mock Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createTagFromContext).when(context).thenReturn(tag); // WHEN QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->remove(context)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createTagFromContext).when(context).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::removeTag).when(tag).exactly(1)); } void shouldAssociateATaskToAContext_data() { QTest::addColumn("associatedTag"); 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::Tag associatedTag(qint64(43)); auto associatedContext = Domain::Context::Ptr::create(); auto itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setItems(Akonadi::Item::List() << item); QTest::newRow("nominal case") << associatedTag << item << associatedContext << task << itemFetchJob << true; itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setExpectedError(KJob::KilledJobError); QTest::newRow("task job error, cannot find task") << associatedTag << item << associatedContext << task << itemFetchJob << false; } void shouldAssociateATaskToAContext() { // GIVEN QFETCH(Akonadi::Tag,associatedTag); 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); // Storage mock returning the create job Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::fetchItem).when(item) .thenReturn(itemFetchJob); - storageMock(&Akonadi::StorageInterface::updateItem).when(item, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::updateItem).when(item, nullptr) .thenReturn(itemModifyJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task) .thenReturn(item); serializerMock(&Akonadi::SerializerInterface::createTagFromContext).when(context) .thenReturn(associatedTag); // WHEN QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), serializerMock.getInstance())); auto associateJob = repository->associate(context, task); if (execJob) associateJob->exec(); // THEN QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(item).exactly(1)); if (execJob) { QVERIFY(serializerMock(&Akonadi::SerializerInterface::createTagFromContext).when(context).exactly(1)); - QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, nullptr).exactly(1)); } } void shoudDissociateTaskFromContext_data() { QTest::addColumn("associatedTag"); 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); Akonadi::Tag associatedTag(qint64(43)); auto associatedContext = Domain::Context::Ptr::create(); auto itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setItems(Akonadi::Item::List() << item); QTest::newRow("nominal case") << associatedTag << item << associatedContext << task << itemFetchJob << true; itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setExpectedError(KJob::KilledJobError); QTest::newRow("task job error, cannot find task") << associatedTag << item << associatedContext << task << itemFetchJob << false; } void shoudDissociateTaskFromContext() { QFETCH(Akonadi::Tag,associatedTag); 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); // Storage mock returning the create job Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::fetchItem).when(item) .thenReturn(itemFetchJob); - storageMock(&Akonadi::StorageInterface::updateItem).when(item, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::updateItem).when(item, nullptr) .thenReturn(itemModifyJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task) .thenReturn(item); serializerMock(&Akonadi::SerializerInterface::createTagFromContext).when(context) .thenReturn(associatedTag); // WHEN QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), serializerMock.getInstance())); auto dissociateJob = repository->dissociate(context, task); if (execJob) dissociateJob->exec(); // THEN QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(item).exactly(1)); if (execJob) { QVERIFY(serializerMock(&Akonadi::SerializerInterface::createTagFromContext).when(context).exactly(1)); - QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, nullptr).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); // Storage mock returning the create job Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::fetchItem).when(item) .thenReturn(itemFetchJob); - storageMock(&Akonadi::StorageInterface::updateItem).when(item, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::updateItem).when(item, nullptr) .thenReturn(itemModifyJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task) .thenReturn(item); // WHEN QScopedPointer repository(new Akonadi::ContextRepository(storageMock.getInstance(), serializerMock.getInstance())); auto dissociateJob = repository->dissociateAll(task); if (execJob) dissociateJob->exec(); // THEN QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(item).exactly(1)); if (execJob) { - QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, nullptr).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/akonadidatasourcequeriestest.cpp b/tests/units/akonadi/akonadidatasourcequeriestest.cpp index dcca42c2..c6a1e11c 100644 --- a/tests/units/akonadi/akonadidatasourcequeriestest.cpp +++ b/tests/units/akonadi/akonadidatasourcequeriestest.cpp @@ -1,847 +1,847 @@ /* 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 "akonadi/akonadidatasourcequeries.h" #include "akonadi/akonadiserializer.h" #include "akonadi/akonadistoragesettings.h" #include "utils/jobhandler.h" #include "utils/mem_fn.h" #include "testlib/akonadifakedata.h" #include "testlib/gencollection.h" #include "testlib/gentodo.h" #include "testlib/testhelpers.h" using namespace Testlib; typedef std::function::Ptr(Domain::DataSourceQueries*)> QueryFunction; Q_DECLARE_METATYPE(QueryFunction) typedef std::function SetDefaultCollectionFunction; Q_DECLARE_METATYPE(SetDefaultCollectionFunction) typedef std::function GetDefaultCollectionFunction; Q_DECLARE_METATYPE(GetDefaultCollectionFunction) class AkonadiDataSourceQueriesTest : public QObject { Q_OBJECT public: - explicit AkonadiDataSourceQueriesTest(QObject *parent = Q_NULLPTR) + explicit AkonadiDataSourceQueriesTest(QObject *parent = nullptr) : QObject(parent) { qRegisterMetaType(); } private slots: void shouldCheckIfASourceIsDefaultFromSettings() { // GIVEN const auto minId = qint64(42); const auto maxId = qint64(44); const auto defaultId = qint64(43); // A default collection for saving Akonadi::StorageSettings::instance().setDefaultCollection(Akonadi::Collection(defaultId)); // A few data sources AkonadiFakeData data; for (auto id = minId; id <= maxId; ++id) { auto col = GenCollection().withId(id).withName(QString::number(id)).withRootAsParent(); data.createCollection(col.withTaskContent()); } // WHEN auto serializer = Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); // THEN for (auto id = minId; id <= maxId; ++id) { auto source = serializer->createDataSourceFromCollection(data.collection(id), Akonadi::SerializerInterface::BaseName); QCOMPARE(queries->isDefaultSource(source), id == defaultId); } } void shouldStoreDefaultSourceInTheSettingsAndNotify() { // GIVEN const auto minId = qint64(42); const auto maxId = qint64(44); const auto defaultId = qint64(43); // A default collection for saving Akonadi::StorageSettings::instance().setDefaultCollection(Akonadi::Collection(minId)); // A few data sources AkonadiFakeData data; for (auto id = minId; id <= maxId; ++id) { auto col = GenCollection().withId(id).withName(QString::number(id)).withRootAsParent(); data.createCollection(col.withTaskContent()); } // WHEN auto serializer = Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); QSignalSpy spy(queries->notifier(), &Domain::DataSourceQueriesNotifier::defaultSourceChanged); auto defaultSource = serializer->createDataSourceFromCollection(data.collection(defaultId), Akonadi::SerializerInterface::BaseName); queries->setDefaultSource(defaultSource); // THEN QCOMPARE(Akonadi::StorageSettings::instance().defaultCollection().id(), defaultId); QCOMPARE(spy.count(), 1); } void shouldLookInAllReportedForTopLevelSources() { // GIVEN AkonadiFakeData data; // Two top level collections, one with tasks, one with notes data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent()); // One with a note child collection data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskChild")).withParent(42).withTaskContent()); // One with a task child collection data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45NoteChild")).withParent(44).withNoteContent()); // WHEN QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findTopLevel(); result->data(); result = queries->findTopLevel(); // Should not cause any problem or wrong data // THEN QVERIFY(result->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); const auto sources = result->data(); auto actualNames = QStringList(); std::transform(sources.constBegin(), sources.constEnd(), std::back_inserter(actualNames), [] (const Domain::DataSource::Ptr &source) { return source->name(); }); actualNames.sort(); QCOMPARE(actualNames, QStringList() << "42Task"); } void shouldReactToCollectionAddsForTopLevelSources() { // GIVEN AkonadiFakeData data; QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findTopLevel(); TestHelpers::waitForEmptyJobQueue(); QVERIFY(result->data().isEmpty()); // WHEN data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); // THEN QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42Task")); } void shouldReactToCollectionRemovesForTopLevelSources() { // GIVEN AkonadiFakeData data; // Two top level collections and two child collections data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Note")).withRootAsParent().withNoteContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("43TaskChild")).withParent(42).withTaskContent()); data.createCollection(GenCollection().withId(45).withName(QStringLiteral("43NoteChild")).withParent(43).withNoteContent()); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findTopLevel(); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 1); // WHEN data.removeCollection(Akonadi::Collection(42)); data.removeCollection(Akonadi::Collection(43)); // THEN QCOMPARE(result->data().size(), 0); } void shouldReactToItemChangesForTopLevelTasks() { // GIVEN AkonadiFakeData data; // Two top level collections and one child collection data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Note")).withRootAsParent().withNoteContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44NoteChild")).withParent(43).withNoteContent()); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findTopLevel(); bool replaceHandlerCalled = false; result->addPostReplaceHandler([&replaceHandlerCalled](const Domain::DataSource::Ptr &, int) { replaceHandlerCalled = true; }); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 1); // WHEN data.modifyCollection(GenCollection(data.collection(42)).withName(QStringLiteral("42TaskBis"))); TestHelpers::waitForEmptyJobQueue(); // THEN QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42TaskBis")); QVERIFY(replaceHandlerCalled); } void shouldNotCrashDuringFindTopLevelWhenFetchJobFailedOrEmpty_data() { QTest::addColumn("colErrorCode"); QTest::addColumn("colFetchBehavior"); QTest::addColumn("deleteQuery"); QTest::newRow("No error with empty collection list") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << false; QTest::newRow("No error with empty collection list (+ query delete)") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << true; QTest::newRow("Error with empty collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << false; QTest::newRow("Error with empty collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << true; QTest::newRow("Error with collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) << false; QTest::newRow("Error with collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) << true; } void shouldNotCrashDuringFindTopLevelWhenFetchJobFailedOrEmpty() { // GIVEN AkonadiFakeData data; // Two top level collections data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withRootAsParent().withNoteContent()); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); QFETCH(int, colErrorCode); QFETCH(int, colFetchBehavior); data.storageBehavior().setFetchCollectionsErrorCode(Akonadi::Collection::root().id(), colErrorCode); data.storageBehavior().setFetchCollectionsBehavior(Akonadi::Collection::root().id(), AkonadiFakeStorageBehavior::FetchBehavior(colFetchBehavior)); QFETCH(bool, deleteQuery); // WHEN Domain::QueryResult::Ptr result = queries->findTopLevel(); if (deleteQuery) delete queries.take(); // THEN QVERIFY(result->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 0); } void shouldLookInAllReportedForChildSources() { // GIVEN AkonadiFakeData data; // One top level collection with two children (one of them also having a child) data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskFirstChild")).withParent(42).withTaskContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44TaskFirstChildChild")).withParent(43).withTaskContent()); data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45NoteSecondChild")).withParent(42).withNoteContent()); // Serializer auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); // WHEN QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findChildren(topLevelDataSource); result->data(); result = queries->findChildren(topLevelDataSource); // Should not cause any problem or wrong data // THEN QVERIFY(result->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); const auto sources = result->data(); auto actualNames = QStringList(); std::transform(sources.constBegin(), sources.constEnd(), std::back_inserter(actualNames), [] (const Domain::DataSource::Ptr &source) { return source->name(); }); actualNames.sort(); QCOMPARE(actualNames, QStringList() << QStringLiteral("43TaskFirstChild")); } void shouldReactToCollectionAddsForChildSources() { // GIVEN AkonadiFakeData data; // One top level collection with no child yet data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); // Serializer auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findChildren(topLevelDataSource); result->data(); result = queries->findChildren(topLevelDataSource); // Should not cause any problem or wrong data TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 0); // WHEN data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskChild")).withParent(42).withTaskContent()); // THEN QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().first()->name(), QStringLiteral("43TaskChild")); } void shouldReactToCollectionRemovesForChildSources() { // GIVEN AkonadiFakeData data; // One top level collection with two children data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskFirstChild")).withParent(42).withTaskContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("45NoteSecondChild")).withParent(42).withNoteContent()); // Serializer auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findChildren(topLevelDataSource); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 1); // WHEN data.removeCollection(Akonadi::Collection(44)); // THEN QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().first()->name(), QStringLiteral("43TaskFirstChild")); } void shouldReactToCollectionChangesForChildSources() { // GIVEN AkonadiFakeData data; // One top level collection with two children data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskFirstChild")).withParent(42).withTaskContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44NoteSecondChild")).withParent(42).withNoteContent()); // Serializer auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findChildren(topLevelDataSource); bool replaceHandlerCalled = false; result->addPostReplaceHandler([&replaceHandlerCalled](const Domain::DataSource::Ptr &, int) { replaceHandlerCalled = true; }); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 1); // WHEN data.modifyCollection(GenCollection(data.collection(43)).withName(QStringLiteral("43TaskFirstChildBis"))); // THEN TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().first()->name(), QStringLiteral("43TaskFirstChildBis")); QVERIFY(replaceHandlerCalled); } void shouldNotCrashDuringFindChildrenWhenFetchJobFailedOrEmpty_data() { QTest::addColumn("colErrorCode"); QTest::addColumn("colFetchBehavior"); QTest::addColumn("deleteQuery"); QTest::newRow("No error with empty collection list") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << false; QTest::newRow("No error with empty collection list (+ query delete)") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << true; QTest::newRow("Error with empty collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << false; QTest::newRow("Error with empty collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << true; QTest::newRow("Error with collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) << false; QTest::newRow("Error with collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) << true; } void shouldNotCrashDuringFindChildrenWhenFetchJobFailedOrEmpty() { // GIVEN AkonadiFakeData data; // One top level collection with two children data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskFirstChild")).withParent(42).withTaskContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44NoteSecondChild")).withParent(42).withNoteContent()); // Serializer auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); QFETCH(bool, deleteQuery); QFETCH(int, colErrorCode); QFETCH(int, colFetchBehavior); data.storageBehavior().setFetchCollectionsErrorCode(data.collection(42).id(), colErrorCode); data.storageBehavior().setFetchCollectionsBehavior(data.collection(42).id(), AkonadiFakeStorageBehavior::FetchBehavior(colFetchBehavior)); // WHEN QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findChildren(topLevelDataSource); if (deleteQuery) delete queries.take(); // THEN QVERIFY(result->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 0); } void shouldLookInAllReportedForSelectedSources() { // GIVEN AkonadiFakeData data; // Two top level collections, one with tasks, one with notes and two child collections data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); // WHEN QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findAllSelected(); result->data(); result = queries->findAllSelected(); // Should not cause any problem or wrong data // THEN QVERIFY(result->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); const auto sources = result->data(); auto actualNames = QStringList(); std::transform(sources.constBegin(), sources.constEnd(), std::back_inserter(actualNames), [] (const Domain::DataSource::Ptr &source) { return source->name(); }); actualNames.sort(); QCOMPARE(actualNames, QStringList() << QStringLiteral("42Task » 43Task")); } void shouldReactToCollectionAddsForSelectedSources() { // GIVEN AkonadiFakeData data; QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findAllSelected(); TestHelpers::waitForEmptyJobQueue(); QVERIFY(result->data().isEmpty()); // WHEN data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); // THEN QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42Task » 43Task")); } void shouldReactToCollectionRemovesForSelectedSources() { // GIVEN AkonadiFakeData data; // Two top level collections and two child collections data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findAllSelected(); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 1); // WHEN data.removeCollection(Akonadi::Collection(43)); data.removeCollection(Akonadi::Collection(45)); // THEN QCOMPARE(result->data().size(), 0); } void shouldReactToCollectionChangesForSelectedSources() { // GIVEN AkonadiFakeData data; // Two top level collections and one child collection data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findAllSelected(); bool replaceHandlerCalled = false; result->addPostReplaceHandler([&replaceHandlerCalled](const Domain::DataSource::Ptr &, int) { replaceHandlerCalled = true; }); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 1); // WHEN data.modifyCollection(GenCollection(data.collection(43)).withName(QStringLiteral("43TaskBis"))); data.modifyCollection(GenCollection(data.collection(45)).withName(QStringLiteral("45NoteBis"))); TestHelpers::waitForEmptyJobQueue(); // THEN QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42Task » 43TaskBis")); QVERIFY(replaceHandlerCalled); } void shouldNotCrashDuringFindAllSelectedWhenFetchJobFailedOrEmpty_data() { QTest::addColumn("colErrorCode"); QTest::addColumn("colFetchBehavior"); QTest::addColumn("deleteQuery"); QTest::newRow("No error with empty collection list") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << false; QTest::newRow("No error with empty collection list (+ query delete)") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << true; QTest::newRow("Error with empty collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << false; QTest::newRow("Error with empty collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << true; QTest::newRow("Error with collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) << false; QTest::newRow("Error with collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) << true; } void shouldNotCrashDuringFindAllSelectedWhenFetchJobFailedOrEmpty() { // GIVEN AkonadiFakeData data; // Two top level collections and two child collections data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); QFETCH(int, colErrorCode); QFETCH(int, colFetchBehavior); data.storageBehavior().setFetchCollectionsErrorCode(Akonadi::Collection::root().id(), colErrorCode); data.storageBehavior().setFetchCollectionsBehavior(Akonadi::Collection::root().id(), AkonadiFakeStorageBehavior::FetchBehavior(colFetchBehavior)); QFETCH(bool, deleteQuery); // WHEN Domain::QueryResult::Ptr result = queries->findAllSelected(); if (deleteQuery) delete queries.take(); // THEN QVERIFY(result->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 0); } void shouldLookInCollectionForProjects() { // GIVEN AkonadiFakeData data; // Two top level collections data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withRootAsParent().withTaskContent()); // One project in the first collection data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); // Two projects in the second collection data.createItem(GenTodo().withId(43).withParent(43).withTitle(QStringLiteral("43")).asProject()); data.createItem(GenTodo().withId(44).withParent(43).withTitle(QStringLiteral("44")).asProject()); // WHEN auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::DataSource::Ptr dataSource1 = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); auto result1 = queries->findProjects(dataSource1); result1->data(); result1 = queries->findProjects(dataSource1); // Should not cause any problem or wrong data // THEN QVERIFY(result1->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result1->data().size(), 1); QCOMPARE(result1->data().at(0)->name(), QStringLiteral("42")); // WHEN Domain::DataSource::Ptr dataSource2 = serializer->createDataSourceFromCollection(data.collection(43), Akonadi::SerializerInterface::BaseName); auto result2 = queries->findProjects(dataSource2); result2->data(); result2 = queries->findProjects(dataSource2); // Should not cause any problem or wrong data // THEN QVERIFY(result2->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result1->data().size(), 1); QCOMPARE(result1->data().at(0)->name(), QStringLiteral("42")); QCOMPARE(result2->data().size(), 2); QCOMPARE(result2->data().at(0)->name(), QStringLiteral("43")); QCOMPARE(result2->data().at(1)->name(), QStringLiteral("44")); } void shouldIgnoreItemsWhichAreNotProjects() { // GIVEN AkonadiFakeData data; // One top level collection data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // One project and one regular task in the collection data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43"))); // WHEN auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::DataSource::Ptr dataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); auto result = queries->findProjects(dataSource); QVERIFY(result->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); // THEN QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); } void shouldReactToItemAddsForProjectsOnly() { // GIVEN AkonadiFakeData data; // One empty collection data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::DataSource::Ptr dataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); auto result = queries->findProjects(dataSource); TestHelpers::waitForEmptyJobQueue(); QVERIFY(result->data().isEmpty()); // WHEN data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43"))); // THEN QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); } void shouldReactToItemRemovesForProjects() { // GIVEN AkonadiFakeData data; // One top level collection data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // Three projects in the collection data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43")).asProject()); data.createItem(GenTodo().withId(44).withParent(42).withTitle(QStringLiteral("44")).asProject()); auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::DataSource::Ptr dataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); auto result = queries->findProjects(dataSource); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 3); // WHEN data.removeItem(Akonadi::Item(43)); // THEN QCOMPARE(result->data().size(), 2); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); QCOMPARE(result->data().at(1)->name(), QStringLiteral("44")); } void shouldReactToItemChangesForProjects() { // GIVEN AkonadiFakeData data; // One top level collection data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // Three projects in the collection data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43")).asProject()); data.createItem(GenTodo().withId(44).withParent(42).withTitle(QStringLiteral("44")).asProject()); auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::DataSource::Ptr dataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); auto result = queries->findProjects(dataSource); // Even though the pointer didn't change it's convenient to user if we call // the replace handlers bool replaceHandlerCalled = false; result->addPostReplaceHandler([&replaceHandlerCalled](const Domain::Project::Ptr &, int) { replaceHandlerCalled = true; }); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 3); // WHEN data.modifyItem(GenTodo(data.item(43)).withTitle(QStringLiteral("43bis"))); // THEN QCOMPARE(result->data().size(), 3); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); QCOMPARE(result->data().at(1)->name(), QStringLiteral("43bis")); QCOMPARE(result->data().at(2)->name(), QStringLiteral("44")); QVERIFY(replaceHandlerCalled); } }; ZANSHIN_TEST_MAIN(AkonadiDataSourceQueriesTest) #include "akonadidatasourcequeriestest.moc" diff --git a/tests/units/akonadi/akonadidatasourcerepositorytest.cpp b/tests/units/akonadi/akonadidatasourcerepositorytest.cpp index 3ed3a6d0..5ac96e08 100644 --- a/tests/units/akonadi/akonadidatasourcerepositorytest.cpp +++ b/tests/units/akonadi/akonadidatasourcerepositorytest.cpp @@ -1,76 +1,76 @@ /* 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/akonadidatasourcerepository.h" #include "akonadi/akonadiserializerinterface.h" #include "akonadi/akonadistorageinterface.h" using namespace mockitopp; Q_DECLARE_METATYPE(Testlib::AkonadiFakeItemFetchJob*) class AkonadiDataSourceRepositoryTest : public QObject { Q_OBJECT private slots: void shouldUpdateExistingSources() { // GIVEN // A source and its corresponding collection already existing in storage Akonadi::Collection collection(42); auto source = Domain::DataSource::Ptr::create(); // A mock modify job auto collectionModifyJob = new FakeJob(this); // Storage mock returning the create job Utils::MockObject storageMock; - storageMock(&Akonadi::StorageInterface::updateCollection).when(collection, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::updateCollection).when(collection, nullptr) .thenReturn(collectionModifyJob); // Serializer mock returning the item for the project Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createCollectionFromDataSource).when(source).thenReturn(collection); // WHEN QScopedPointer repository(new Akonadi::DataSourceRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->update(source)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createCollectionFromDataSource).when(source).exactly(1)); - QVERIFY(storageMock(&Akonadi::StorageInterface::updateCollection).when(collection, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::updateCollection).when(collection, nullptr).exactly(1)); } }; ZANSHIN_TEST_MAIN(AkonadiDataSourceRepositoryTest) #include "akonadidatasourcerepositorytest.moc" diff --git a/tests/units/akonadi/akonadiprojectrepositorytest.cpp b/tests/units/akonadi/akonadiprojectrepositorytest.cpp index 2f5a386e..ec63666b 100644 --- a/tests/units/akonadi/akonadiprojectrepositorytest.cpp +++ b/tests/units/akonadi/akonadiprojectrepositorytest.cpp @@ -1,356 +1,356 @@ /* 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); // Storage mock returning the create job Utils::MockObject storageMock; - storageMock(&Akonadi::StorageInterface::updateItem).when(item, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::updateItem).when(item, nullptr) .thenReturn(itemModifyJob); // 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->update(project)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(project).exactly(1)); - QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, nullptr).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) .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)); } 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; // Storage mock returning the create job Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem) .thenReturn(itemFetchJob1); storageMock(&Akonadi::StorageInterface::fetchItem).when(parentItem) .thenReturn(itemFetchJob2); if (parentItem.parentCollection().id() != childItem.parentCollection().id()) { storageMock(&Akonadi::StorageInterface::fetchItems).when(childItem.parentCollection()) .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, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, nullptr) .thenReturn(itemModifyJob); } // Serializer mock returning the item for the task Utils::MockObject serializerMock; 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 QScopedPointer repository(new Akonadi::ProjectRepository(storageMock.getInstance(), serializerMock.getInstance())); auto associateJob = repository->associate(parent, child); if (execJob) associateJob->exec(); // THEN QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem).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).exactly(1)); if (execParentJob) { if (parentItem.parentCollection().id() != childItem.parentCollection().id()) { QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItems).when(childItem.parentCollection()).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, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, nullptr).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); // Storage mock returning the delete job Utils::MockObject storageMock; - storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, nullptr) .thenReturn(itemModifyJob); storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem) .thenReturn(itemFetchJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).thenReturn(childItem); serializerMock(&Akonadi::SerializerInterface::removeItemParent).when(childItem).thenReturn(); // WHEN QScopedPointer repository(new Akonadi::ProjectRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->dissociate(child)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem).exactly(1)); if (!fetchJobFailed) { QVERIFY(serializerMock(&Akonadi::SerializerInterface::removeItemParent).when(childItem).exactly(1));; - QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, nullptr).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" diff --git a/tests/units/akonadi/akonadistoragesettingstest.cpp b/tests/units/akonadi/akonadistoragesettingstest.cpp index 52490d2a..f0d3eea4 100644 --- a/tests/units/akonadi/akonadistoragesettingstest.cpp +++ b/tests/units/akonadi/akonadistoragesettingstest.cpp @@ -1,125 +1,125 @@ /* 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 #include #include "akonadi/akonadistoragesettings.h" using namespace Akonadi; Q_DECLARE_METATYPE(QSet) class AkonadiStorageSettingsTest : public QObject { Q_OBJECT public: - explicit AkonadiStorageSettingsTest(QObject *parent = Q_NULLPTR) + explicit AkonadiStorageSettingsTest(QObject *parent = nullptr) : QObject(parent) { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType>(); } private: KConfigGroup configGroup() { return KConfigGroup(KSharedConfig::openConfig(), "General"); } QList idList(int max) { QList list; list.reserve(max); for (int i = 1; i < max; i++) { list << i; } return list; } Akonadi::Collection::List colList(int max) { Akonadi::Collection::List list; list.reserve(max); foreach (auto id, idList(max)) { list << Collection(id); } return list; } private slots: void shouldReadFromConfigurationFile() { // GIVEN KConfigGroup g = configGroup(); for (int i = 1; i <= 18; i += 3) { // WHEN g.writeEntry("defaultCollection", i); // THEN QCOMPARE(StorageSettings::instance().defaultCollection(), Collection(i)); } } void shouldWriteToConfigurationFile() { // GIVEN KConfigGroup g = configGroup(); for (int i = 1; i <= 18; i += 3) { // WHEN StorageSettings::instance().setDefaultCollection(Collection(i)); // THEN QCOMPARE(g.readEntry("defaultCollection", -1), i); } } void shouldNotifyTaskCollectionChanges() { StorageSettings &settings = StorageSettings::instance(); QSignalSpy spy(&settings, &Akonadi::StorageSettings::defaultCollectionChanged); settings.setDefaultCollection(Collection(2)); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().value(), Collection(2)); } void shouldNotNotifyIdenticalTaskCollectionChanges() { StorageSettings &settings = StorageSettings::instance(); settings.setDefaultCollection(Collection(4)); QSignalSpy spy(&settings, &Akonadi::StorageSettings::defaultCollectionChanged); settings.setDefaultCollection(Collection(4)); QCOMPARE(spy.count(), 0); } }; ZANSHIN_TEST_MAIN(AkonadiStorageSettingsTest) #include "akonadistoragesettingstest.moc" diff --git a/tests/units/akonadi/akonadistoragetest.cpp b/tests/units/akonadi/akonadistoragetest.cpp index cc57ad5b..866747d5 100644 --- a/tests/units/akonadi/akonadistoragetest.cpp +++ b/tests/units/akonadi/akonadistoragetest.cpp @@ -1,60 +1,60 @@ /* 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 #include #include #include "akonadi/akonadimonitorimpl.h" #include "akonadi/akonadistorage.h" class AkonadiStorageTest : public Testlib::AkonadiStorageTestBase { Q_OBJECT public: - explicit AkonadiStorageTest(QObject *parent = Q_NULLPTR) + explicit AkonadiStorageTest(QObject *parent = nullptr) : AkonadiStorageTestBase(parent) { } Akonadi::StorageInterface::Ptr createStorage() override { return Akonadi::StorageInterface::Ptr(new Akonadi::Storage); } Akonadi::MonitorInterface::Ptr createMonitor() override { return Akonadi::MonitorInterface::Ptr(new Akonadi::MonitorImpl); } private slots: void initTestCase() { QVERIFY(TestLib::TestSafety::checkTestIsIsolated()); } }; ZANSHIN_TEST_MAIN(AkonadiStorageTest) #include "akonadistoragetest.moc" diff --git a/tests/units/akonadi/akonaditaskrepositorytest.cpp b/tests/units/akonadi/akonaditaskrepositorytest.cpp index 62b8dc89..b74d2eda 100644 --- a/tests/units/akonadi/akonaditaskrepositorytest.cpp +++ b/tests/units/akonadi/akonaditaskrepositorytest.cpp @@ -1,788 +1,788 @@ /* 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 #include #include "utils/mockobject.h" #include "testlib/akonadifakedata.h" #include "testlib/akonadifakejobs.h" #include "testlib/akonadifakemonitor.h" #include "testlib/akonadifakestorage.h" #include "testlib/gencollection.h" #include "testlib/gentodo.h" #include "akonadi/akonaditaskrepository.h" #include "akonadi/akonadiserializer.h" #include "akonadi/akonadistorageinterface.h" using namespace mockitopp; using namespace mockitopp::matcher; using namespace Testlib; Q_DECLARE_METATYPE(Testlib::AkonadiFakeItemFetchJob*) class AkonadiTaskRepositoryTest : public QObject { Q_OBJECT public: - explicit AkonadiTaskRepositoryTest(QObject *parent = Q_NULLPTR) + explicit AkonadiTaskRepositoryTest(QObject *parent = nullptr) : QObject(parent) { } private slots: void shouldCreateNewItems() { // GIVEN // A default collection for saving Akonadi::Collection col(42); // A task and its corresponding item not existing in storage yet Akonadi::Item item; Domain::Task::Ptr task(new Domain::Task); // A mock create job auto itemCreateJob = new FakeJob(this); // Storage mock returning the create job Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::defaultCollection).when().thenReturn(col); storageMock(&Akonadi::StorageInterface::createItem).when(item, col) .thenReturn(itemCreateJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).thenReturn(item); // WHEN QScopedPointer repository(new Akonadi::TaskRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->create(task)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::defaultCollection).when().exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::createItem).when(item, col).exactly(1)); } void shouldCreateNewItemsInFirstWritableCollectionIsNothingInSettings() { // GIVEN // A few collections auto col1 = Akonadi::Collection(GenCollection().withId(42).withRootAsParent().withTaskContent()); col1.setRights(Akonadi::Collection::ReadOnly); auto col2 = Akonadi::Collection(GenCollection().withId(43).withRootAsParent().withTaskContent()); col2.setRights(Akonadi::Collection::CanCreateItem); auto col3 = Akonadi::Collection(GenCollection().withId(44).withRootAsParent().withTaskContent()); col3.setRights(Akonadi::Collection::CanCreateItem | Akonadi::Collection::CanChangeItem | Akonadi::Collection::CanDeleteItem); auto collectionFetchJob = new Testlib::AkonadiFakeCollectionFetchJob; collectionFetchJob->setCollections(Akonadi::Collection::List() << col1 << col2 << col3); // A task and its corresponding item not existing in storage yet Akonadi::Item item; Domain::Task::Ptr task(new Domain::Task); // A mock create job auto itemCreateJob = new FakeJob(this); // Storage mock returning the create job and with no default collection Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::defaultCollection).when().thenReturn(Akonadi::Collection()); storageMock(&Akonadi::StorageInterface::fetchCollections).when(Akonadi::Collection::root(), Akonadi::StorageInterface::Recursive) .thenReturn(collectionFetchJob); storageMock(&Akonadi::StorageInterface::createItem).when(item, col3) .thenReturn(itemCreateJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).thenReturn(item); // WHEN QScopedPointer repository(new Akonadi::TaskRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->create(task)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::defaultCollection).when().exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::createItem).when(item, col3).exactly(1)); } void shouldEmitErrorIfNoFallbackCollectionIsFound() { // GIVEN // A few collections auto col1 = Akonadi::Collection(42); col1.setRights(Akonadi::Collection::ReadOnly); auto col2 = Akonadi::Collection(43); col2.setRights(Akonadi::Collection::ReadOnly); auto col3 = Akonadi::Collection(44); col3.setRights(Akonadi::Collection::ReadOnly); auto collectionFetchJob = new Testlib::AkonadiFakeCollectionFetchJob; collectionFetchJob->setCollections(Akonadi::Collection::List() << col1 << col2 << col3); // A task and its corresponding item not existing in storage yet Akonadi::Item item; Domain::Task::Ptr task(new Domain::Task); // Storage mock returning the create job and with no default collection Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::defaultCollection).when().thenReturn(Akonadi::Collection()); storageMock(&Akonadi::StorageInterface::fetchCollections).when(Akonadi::Collection::root(), Akonadi::StorageInterface::Recursive) .thenReturn(collectionFetchJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).thenReturn(item); // WHEN QScopedPointer repository(new Akonadi::TaskRepository(storageMock.getInstance(), serializerMock.getInstance())); auto job = repository->create(task); job->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::defaultCollection).when().exactly(1)); QVERIFY(job->error()); QVERIFY(!job->errorText().isEmpty()); } void shouldCreateNewChildrenInParentCollection() { // GIVEN // A parent item with a collection Akonadi::Collection col(42); Akonadi::Item parentItem(43); parentItem.setParentCollection(col); auto parent = Domain::Task::Ptr::create(); // A task and its corresponding item not existing in storage yet Akonadi::Item childItem; auto child = Domain::Task::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(childItem, col) .thenReturn(itemCreateJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(parent).thenReturn(parentItem); serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).thenReturn(childItem); serializerMock(&Akonadi::SerializerInterface::updateItemParent).when(childItem, parent).thenReturn(); // WHEN QScopedPointer repository(new Akonadi::TaskRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->createChild(child, parent)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(parent).exactly(1)); QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).exactly(1)); QVERIFY(serializerMock(&Akonadi::SerializerInterface::updateItemParent).when(childItem, parent).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::createItem).when(childItem, col).exactly(1)); } void shouldCreateNewItemsInProjectCollection() { // GIVEN // A project item with a collection Akonadi::Collection col(42); Akonadi::Item projectItem(43); projectItem.setParentCollection(col); auto project = Domain::Project::Ptr::create(); // A task and its corresponding item not existing in storage yet Akonadi::Item taskItem; auto task = Domain::Task::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(taskItem, col) .thenReturn(itemCreateJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(project).thenReturn(projectItem); serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).thenReturn(taskItem); serializerMock(&Akonadi::SerializerInterface::updateItemProject).when(taskItem, project).thenReturn(); // WHEN QScopedPointer repository(new Akonadi::TaskRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->createInProject(task, project)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(project).exactly(1)); QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).exactly(1)); QVERIFY(serializerMock(&Akonadi::SerializerInterface::updateItemProject).when(taskItem, project).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::createItem).when(taskItem, col).exactly(1)); } void shouldCreateNewItemsInContext() { // GIVEN // a tag Akonadi::Tag tag; tag.setName(QStringLiteral("tag42")); tag.setId(42); // the context related to the tag auto context = Domain::Context::Ptr::create(); // a default collection Akonadi::Collection defaultCollection(42); // A task and its corresponding item not existing in storage yet Akonadi::Item taskItem; auto task = Domain::Task::Ptr::create(); // A mock create job auto itemCreateJob = new FakeJob(this); // serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createTagFromContext).when(context).thenReturn(tag); serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).thenReturn(taskItem); // Storage mock returning the create job Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::defaultCollection).when().thenReturn(defaultCollection); storageMock(&Akonadi::StorageInterface::createItem).when(taskItem, defaultCollection).thenReturn(itemCreateJob); // WHEN QScopedPointer repository(new Akonadi::TaskRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->createInContext(task, context)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).exactly(1)); QVERIFY(serializerMock(&Akonadi::SerializerInterface::createTagFromContext).when(context).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::defaultCollection).when().exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::createItem).when(taskItem, defaultCollection).exactly(1)); } void shouldUpdateExistingItems() { // GIVEN // A default collection for saving Akonadi::Collection col(42); // A task and its corresponding item already existing in storage Akonadi::Item item(42); Domain::Task::Ptr task(new Domain::Task); // A mock create job auto itemModifyJob = new FakeJob(this); // Storage mock returning the create job Utils::MockObject storageMock; - storageMock(&Akonadi::StorageInterface::updateItem).when(item, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::updateItem).when(item, nullptr) .thenReturn(itemModifyJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).thenReturn(item); // WHEN QScopedPointer repository(new Akonadi::TaskRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->update(task)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).exactly(1)); - QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, nullptr).exactly(1)); } void shouldRemoveATask_data() { QTest::addColumn("item"); QTest::addColumn("itemFetchJob1"); QTest::addColumn("itemFetchJob2"); QTest::addColumn("list"); QTest::addColumn("itemFetchJobSucceeded"); QTest::addColumn("collectionItemsFetchJobSucceeded"); Akonadi::Collection col(40); Akonadi::Item item(42); item.setParentCollection(col); Akonadi::Item item2(43); auto itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setItems(Akonadi::Item::List() << item); auto itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this); Akonadi::Item::List list; QTest::newRow("nominal case") << item << itemFetchJob1 << itemFetchJob2 << list << true << true; itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setExpectedError(KJob::KilledJobError); QTest::newRow("item job error with empty list") << item << itemFetchJob1 << itemFetchJob2 << list << false << false; itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setExpectedError(KJob::KilledJobError); itemFetchJob1->setItems(Akonadi::Item::List() << item); QTest::newRow("item job error with item") << item << itemFetchJob1 << itemFetchJob2 << list << false << false; itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setItems(Akonadi::Item::List() << item); itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob2->setExpectedError(KJob::KilledJobError); QTest::newRow("items job error with empty list") << item << itemFetchJob1 << itemFetchJob2 << list << true << false; itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setItems(Akonadi::Item::List() << item); itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this); list << item2; itemFetchJob2->setItems(list); QTest::newRow("remove item and his child") << item << itemFetchJob1 << itemFetchJob2 << list << true << true; } void shouldRemoveATask() { // GIVEN QFETCH(Akonadi::Item, item); QFETCH(Testlib::AkonadiFakeItemFetchJob*, itemFetchJob1); QFETCH(Testlib::AkonadiFakeItemFetchJob*, itemFetchJob2); QFETCH(Akonadi::Item::List, list); QFETCH(bool, itemFetchJobSucceeded); QFETCH(bool, collectionItemsFetchJobSucceeded); Domain::Task::Ptr task(new Domain::Task); Akonadi::Item::List removedList; removedList << list << item; // A mock delete job auto itemDeleteJob = new FakeJob(this); // Storage mock returning the delete job Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::fetchItem).when(item) .thenReturn(itemFetchJob1); storageMock(&Akonadi::StorageInterface::fetchItems).when(item.parentCollection()) .thenReturn(itemFetchJob2); - storageMock(&Akonadi::StorageInterface::removeItems).when(removedList, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::removeItems).when(removedList, nullptr) .thenReturn(itemDeleteJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).thenReturn(item); serializerMock(&Akonadi::SerializerInterface::filterDescendantItems).when(list, item).thenReturn(list); // WHEN QScopedPointer repository(new Akonadi::TaskRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->remove(task)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(item).exactly(1)); if (itemFetchJobSucceeded) { QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItems).when(item.parentCollection()).exactly(1)); if (collectionItemsFetchJobSucceeded) { - QVERIFY(storageMock(&Akonadi::StorageInterface::removeItems).when(removedList, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::removeItems).when(removedList, nullptr).exactly(1)); } } } void shouldPromoteTaskToProject() { // GIVEN // A default collection for saving Akonadi::Collection col(42); // A task and its corresponding item already existing in storage Akonadi::Item item(42); Domain::Task::Ptr task(new Domain::Task); // A mock fetch job auto itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setItems(Akonadi::Item::List() << item); // A mock modify job auto itemModifyJob = new FakeJob(this); // Serializer mock returning the item for the task and transforming it into a project Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).thenReturn(item); serializerMock(&Akonadi::SerializerInterface::promoteItemToProject).when(item).thenReturn(); // Storage mock returning the modify job Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::fetchItem).when(item) .thenReturn(itemFetchJob); - storageMock(&Akonadi::StorageInterface::updateItem).when(item, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::updateItem).when(item, nullptr) .thenReturn(itemModifyJob); // WHEN QScopedPointer repository(new Akonadi::TaskRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->promoteToProject(task)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(task).exactly(1)); QVERIFY(serializerMock(&Akonadi::SerializerInterface::promoteItemToProject).when(item).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(item).exactly(1)); - QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, nullptr).exactly(1)); } void shouldAssociateATaskToAnother_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 child(new Domain::Task); Akonadi::Item parentItem(41); parentItem.setParentCollection(col); Domain::Task::Ptr parent(new Domain::Task); auto itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob1->setItems(Akonadi::Item::List() << childItem); auto itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob2->setItems(Akonadi::Item::List() << childItem << parentItem); auto itemFetchJob3 = new Testlib::AkonadiFakeItemFetchJob(this); Akonadi::Item::List list; QTest::newRow("nominal case") << childItem << parentItem << child << 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 << child << 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") << childItem << parentItem << child << 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") << childItem << parentItem << child << 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() << childItem << parentItem); QTest::newRow("parent job error with item") << childItem << parentItem << child << 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") << childItem << parentItem2 << child << 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") << childItem << parentItem2 << child << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << true << true << list2; } void shouldAssociateATaskToAnother() { // GIVEN QFETCH(Akonadi::Item, childItem); QFETCH(Akonadi::Item, parentItem); QFETCH(Domain::Task::Ptr, child); QFETCH(Domain::Task::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; // Storage mock returning the create job Utils::MockObject storageMock; storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem) .thenReturn(itemFetchJob1); storageMock(&Akonadi::StorageInterface::fetchItems).when(parentItem.parentCollection()) .thenReturn(itemFetchJob2); if (parentItem.parentCollection().id() != childItem.parentCollection().id()) { storageMock(&Akonadi::StorageInterface::fetchItems).when(childItem.parentCollection()) .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, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, nullptr) .thenReturn(itemModifyJob); } // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).thenReturn(childItem); serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(parent).thenReturn(parentItem); serializerMock(&Akonadi::SerializerInterface::createTaskFromItem).when(childItem).thenReturn(child); serializerMock(&Akonadi::SerializerInterface::createTaskFromItem).when(parentItem).thenReturn(parent); serializerMock(&Akonadi::SerializerInterface::updateItemParent).when(childItem, parent).thenReturn(); serializerMock(&Akonadi::SerializerInterface::itemUid).when(parentItem).thenReturn(QStringLiteral("parent")); serializerMock(&Akonadi::SerializerInterface::itemUid).when(childItem).thenReturn(QStringLiteral("child")); serializerMock(&Akonadi::SerializerInterface::relatedUidFromItem).when(parentItem).thenReturn(QString()); serializerMock(&Akonadi::SerializerInterface::relatedUidFromItem).when(childItem).thenReturn(QString()); if (execParentJob) serializerMock(&Akonadi::SerializerInterface::filterDescendantItems).when(list, childItem).thenReturn(list); // WHEN QScopedPointer repository(new Akonadi::TaskRepository(storageMock.getInstance(), serializerMock.getInstance())); auto associateJob = repository->associate(parent, child); if (execJob) associateJob->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem).exactly(1)); if (execJob) { QVERIFY(serializerMock(&Akonadi::SerializerInterface::updateItemParent).when(childItem, parent).exactly(1)); QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(parent).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItems).when(parentItem.parentCollection()).exactly(1)); if (execParentJob) { if (parentItem.parentCollection().id() == childItem.parentCollection().id()) - QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, nullptr).exactly(1)); else { //QVERIFY(serializerMock(&Akonadi::SerializerInterface::filterDescendantItems).when(list, childItem).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItems).when(childItem.parentCollection()).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)); } } } } void shouldPreventCyclesDuringAssociation() { // GIVEN AkonadiFakeData data; // One top level collection data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // Three tasks in the collection (one being child of the second one) data.createItem(GenTodo().withId(42).withParent(42) .withTitle(QStringLiteral("42")).withUid(QStringLiteral("uid-42"))); data.createItem(GenTodo().withId(43).withParent(42) .withTitle(QStringLiteral("43")).withUid(QStringLiteral("uid-43")) .withParentUid(QStringLiteral("uid-42"))); data.createItem(GenTodo().withId(44).withParent(42) .withTitle(QStringLiteral("44")).withUid(QStringLiteral("uid-44")) .withParentUid(QStringLiteral("uid-43"))); auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); auto task42 = serializer->createTaskFromItem(data.item(42)); auto task44 = serializer->createTaskFromItem(data.item(44)); auto monitor = Akonadi::MonitorInterface::Ptr(data.createMonitor()); QScopedPointer repository(new Akonadi::TaskRepository(Akonadi::StorageInterface::Ptr(data.createStorage()), serializer)); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemChanged); // WHEN auto job = repository->associate(task44, task42); QVERIFY(!job->exec()); // THEN QVERIFY(spy.isEmpty()); QVERIFY(job->error() != KJob::NoError); QVERIFY(!job->errorText().isEmpty()); } void shouldDissociateATaskFromItsParent_data() { QTest::addColumn("child"); QTest::addColumn("childItem"); QTest::addColumn("itemFetchJob"); QTest::addColumn("childJobFailed"); Domain::Task::Ptr child(new Domain::Task); Akonadi::Item childItem(42); auto itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setItems(Akonadi::Item::List() << childItem); QTest::newRow("nominal case") << child << childItem << itemFetchJob << false; itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setExpectedError(KJob::KilledJobError); QTest::newRow("child job error with empty list") << child << childItem << itemFetchJob << true; itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this); itemFetchJob->setExpectedError(KJob::KilledJobError); itemFetchJob->setItems(Akonadi::Item::List() << childItem); QTest::newRow("child job error with item") << child << childItem << itemFetchJob << true; } void shouldDissociateATaskFromItsParent() { // GIVEN QFETCH(Domain::Task::Ptr, child); QFETCH(Akonadi::Item, childItem); QFETCH(Testlib::AkonadiFakeItemFetchJob*, itemFetchJob); QFETCH(bool, childJobFailed); auto itemModifyJob = new FakeJob(this); // Storage mock returning the delete job Utils::MockObject storageMock; - storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, nullptr) .thenReturn(itemModifyJob); storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem) .thenReturn(itemFetchJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).thenReturn(childItem); serializerMock(&Akonadi::SerializerInterface::removeItemParent).when(childItem).thenReturn(); // WHEN QScopedPointer repository(new Akonadi::TaskRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->dissociate(child)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem).exactly(1)); if (!childJobFailed) { QVERIFY(serializerMock(&Akonadi::SerializerInterface::removeItemParent).when(childItem).exactly(1));; - QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, nullptr).exactly(1)); } } void shouldDissociateAllLinksOfTask_data() { shouldDissociateATaskFromItsParent_data(); } void shouldDissociateAllLinksOfTask() { // GIVEN QFETCH(Domain::Task::Ptr, child); QFETCH(Akonadi::Item, childItem); QFETCH(Testlib::AkonadiFakeItemFetchJob*, itemFetchJob); QFETCH(bool, childJobFailed); auto itemModifyJob = new FakeJob(this); // Storage mock returning the delete job Utils::MockObject storageMock; - storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, Q_NULLPTR) + storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, nullptr) .thenReturn(itemModifyJob); storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem) .thenReturn(itemFetchJob); // Serializer mock returning the item for the task Utils::MockObject serializerMock; serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).thenReturn(childItem); serializerMock(&Akonadi::SerializerInterface::removeItemParent).when(childItem).thenReturn(); serializerMock(&Akonadi::SerializerInterface::clearItem).when(any()).thenReturn(); // WHEN QScopedPointer repository(new Akonadi::TaskRepository(storageMock.getInstance(), serializerMock.getInstance())); repository->dissociateAll(child)->exec(); // THEN QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).exactly(1)); QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem).exactly(1)); if (!childJobFailed) { QVERIFY(serializerMock(&Akonadi::SerializerInterface::removeItemParent).when(childItem).exactly(1)); QVERIFY(serializerMock(&Akonadi::SerializerInterface::clearItem).when(any()).exactly(1)); - QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, Q_NULLPTR).exactly(1)); + QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, nullptr).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(AkonadiTaskRepositoryTest) #include "akonaditaskrepositorytest.moc" diff --git a/tests/units/domain/datasourcetest.cpp b/tests/units/domain/datasourcetest.cpp index e26f69a8..1f26c737 100644 --- a/tests/units/domain/datasourcetest.cpp +++ b/tests/units/domain/datasourcetest.cpp @@ -1,126 +1,126 @@ /* 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 "domain/datasource.h" using namespace Domain; class DataSourceTest : public QObject { Q_OBJECT public: - explicit DataSourceTest(QObject *parent = Q_NULLPTR) + explicit DataSourceTest(QObject *parent = nullptr) : QObject(parent) { qRegisterMetaType(); } private slots: void shouldHaveEmptyPropertiesByDefault() { DataSource ds; QCOMPARE(ds.name(), QString()); QCOMPARE(ds.iconName(), QString()); QCOMPARE(ds.contentTypes(), DataSource::NoContent); QVERIFY(!ds.isSelected()); } void shouldNotifyNameChanges() { DataSource ds; QSignalSpy spy(&ds, &DataSource::nameChanged); ds.setName(QStringLiteral("Foo")); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().toString(), QStringLiteral("Foo")); } void shouldNotNotifyIdenticalNameChanges() { DataSource ds; ds.setName(QStringLiteral("Foo")); QSignalSpy spy(&ds, &DataSource::nameChanged); ds.setName(QStringLiteral("Foo")); QCOMPARE(spy.count(), 0); } void shouldNotifyIconNameChanges() { DataSource ds; QSignalSpy spy(&ds, &DataSource::iconNameChanged); ds.setIconName(QStringLiteral("Foo")); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().toString(), QStringLiteral("Foo")); } void shouldNotNotifyIdenticalIconNameChanges() { DataSource ds; ds.setIconName(QStringLiteral("Foo")); QSignalSpy spy(&ds, &DataSource::iconNameChanged); ds.setIconName(QStringLiteral("Foo")); QCOMPARE(spy.count(), 0); } void shouldNotifyContentTypesChanges() { DataSource ds; QSignalSpy spy(&ds, &DataSource::contentTypesChanged); ds.setContentTypes(Domain::DataSource::Notes); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().value(), Domain::DataSource::Notes); } void shouldNotNotifyIdenticalContentTypesChanges() { DataSource ds; ds.setContentTypes(Domain::DataSource::Notes); QSignalSpy spy(&ds, &DataSource::contentTypesChanged); ds.setContentTypes(Domain::DataSource::Notes); QCOMPARE(spy.count(), 0); } void shouldNotifySelectedChanges() { DataSource ds; QSignalSpy spy(&ds, &DataSource::selectedChanged); ds.setSelected(true); QCOMPARE(spy.count(), 1); QVERIFY(spy.first().first().toBool()); } void shouldNotNotifyIdenticalSelectedChanges() { DataSource ds; ds.setSelected(true); QSignalSpy spy(&ds, &DataSource::selectedChanged); ds.setSelected(true); QCOMPARE(spy.count(), 0); } }; ZANSHIN_TEST_MAIN(DataSourceTest) #include "datasourcetest.moc" diff --git a/tests/units/domain/livequerytest.cpp b/tests/units/domain/livequerytest.cpp index 99185bea..42cedda3 100644 --- a/tests/units/domain/livequerytest.cpp +++ b/tests/units/domain/livequerytest.cpp @@ -1,532 +1,532 @@ /* 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 "domain/livequery.h" #include "utils/jobhandler.h" #include "testlib/fakejob.h" using namespace Domain; typedef QSharedPointer QObjectPtr; class LiveQueryTest : public QObject { Q_OBJECT private: QObject *createObject(int id, const QString &name) { QObject *obj = new QObject(this); obj->setObjectName(name); obj->setProperty("objectId", id); return obj; } private slots: void shouldHaveInitialFetchFunctionAndPredicate() { // GIVEN Domain::LiveQuery> query; query.setFetchFunction([this] (const Domain::LiveQueryInput::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("0A"))); add(createObject(1, QStringLiteral("1A"))); add(createObject(2, QStringLiteral("2A"))); add(createObject(3, QStringLiteral("0B"))); add(createObject(4, QStringLiteral("1B"))); add(createObject(5, QStringLiteral("2B"))); add(createObject(6, QStringLiteral("0C"))); add(createObject(7, QStringLiteral("1C"))); add(createObject(8, QStringLiteral("2C"))); }); }); query.setConvertFunction([] (QObject *object) { return QPair(object->property("objectId").toInt(), object->objectName()); }); query.setPredicateFunction([] (QObject *object) { return object->objectName().startsWith('0'); }); // WHEN Domain::QueryResult>::Ptr result = query.result(); result->data(); result = query.result(); // Should not cause any problem or wrong data QVERIFY(result->data().isEmpty()); QTest::qWait(150); // THEN QList> expected; expected << QPair(0, QStringLiteral("0A")) << QPair(3, QStringLiteral("0B")) << QPair(6, QStringLiteral("0C")); QCOMPARE(result->data(), expected); } void shouldFilterOutNullRawPointers() { // GIVEN auto query = Domain::LiveQuery(); query.setFetchFunction([this] (const Domain::LiveQueryInput::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(QStringLiteral("0")); add(QStringLiteral("1")); add(QString()); add(QStringLiteral("a")); add(QStringLiteral("2")); }); }); query.setConvertFunction([this] (const QString &s) -> QObject* { bool ok = false; const int id = s.toInt(&ok); if (ok) { auto object = new QObject(this); object->setProperty("id", id); return object; } else { - return Q_NULLPTR; + return nullptr; } }); query.setPredicateFunction([] (const QString &s) { return !s.isEmpty(); }); // WHEN auto result = query.result(); result->data(); result = query.result(); // Should not cause any problem or wrong data QVERIFY(result->data().isEmpty()); QTest::qWait(150); // THEN QCOMPARE(result->data().size(), 3); QCOMPARE(result->data().at(0)->property("id").toInt(), 0); QCOMPARE(result->data().at(1)->property("id").toInt(), 1); QCOMPARE(result->data().at(2)->property("id").toInt(), 2); } void shouldFilterOutNullSharedPointers() { // GIVEN auto query = Domain::LiveQuery(); query.setFetchFunction([this] (const Domain::LiveQueryInput::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(QStringLiteral("0")); add(QStringLiteral("1")); add(QString()); add(QStringLiteral("a")); add(QStringLiteral("2")); }); }); query.setConvertFunction([this] (const QString &s) { bool ok = false; const int id = s.toInt(&ok); if (ok) { auto object = QObjectPtr::create(); object->setProperty("id", id); return object; } else { return QObjectPtr(); } }); query.setPredicateFunction([] (const QString &s) { return !s.isEmpty(); }); // WHEN auto result = query.result(); result->data(); result = query.result(); // Should not cause any problem or wrong data QVERIFY(result->data().isEmpty()); QTest::qWait(150); // THEN QCOMPARE(result->data().size(), 3); QCOMPARE(result->data().at(0)->property("id").toInt(), 0); QCOMPARE(result->data().at(1)->property("id").toInt(), 1); QCOMPARE(result->data().at(2)->property("id").toInt(), 2); } void shouldDealWithSeveralFetchesProperly() { // GIVEN Domain::LiveQuery> query; query.setFetchFunction([this] (const Domain::LiveQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("0A"))); add(createObject(1, QStringLiteral("1A"))); add(createObject(2, QStringLiteral("2A"))); add(createObject(3, QStringLiteral("0B"))); add(createObject(4, QStringLiteral("1B"))); add(createObject(5, QStringLiteral("2B"))); add(createObject(6, QStringLiteral("0C"))); add(createObject(7, QStringLiteral("1C"))); add(createObject(8, QStringLiteral("2C"))); }); }); query.setConvertFunction([] (QObject *object) { return QPair(object->property("objectId").toInt(), object->objectName()); }); query.setPredicateFunction([] (QObject *object) { return object->objectName().startsWith('0'); }); for (int i = 0; i < 2; i++) { // WHEN * 2 Domain::QueryResult>::Ptr result = query.result(); // THEN * 2 QVERIFY(result->data().isEmpty()); QTest::qWait(150); QList> expected; expected << QPair(0, QStringLiteral("0A")) << QPair(3, QStringLiteral("0B")) << QPair(6, QStringLiteral("0C")); QCOMPARE(result->data(), expected); } } void shouldClearProviderWhenDeleted() { // GIVEN auto query = new Domain::LiveQuery>; query->setFetchFunction([this] (const Domain::LiveQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("0A"))); add(createObject(1, QStringLiteral("1A"))); add(createObject(2, QStringLiteral("2A"))); }); }); query->setConvertFunction([] (QObject *object) { return QPair(object->property("objectId").toInt(), object->objectName()); }); query->setPredicateFunction([] (QObject *object) { return object->objectName().startsWith('0'); }); Domain::QueryResult>::Ptr result = query->result(); QTest::qWait(150); QCOMPARE(result->data().count(), 1); // WHEN delete query; // THEN QVERIFY(result->data().isEmpty()); } void shouldReactToAdds() { // GIVEN Domain::LiveQuery> query; query.setFetchFunction([this] (const Domain::LiveQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("0A"))); add(createObject(1, QStringLiteral("1A"))); add(createObject(2, QStringLiteral("2A"))); }); }); query.setConvertFunction([] (QObject *object) { return QPair(object->property("objectId").toInt(), object->objectName()); }); query.setPredicateFunction([] (QObject *object) { return object->objectName().startsWith('0'); }); Domain::QueryResult>::Ptr result = query.result(); QTest::qWait(150); QList> expected; expected << QPair(0, QStringLiteral("0A")); QCOMPARE(result->data(), expected); // WHEN query.onAdded(createObject(3, QStringLiteral("0B"))); query.onAdded(createObject(4, QStringLiteral("1B"))); query.onAdded(createObject(5, QStringLiteral("2B"))); // THEN expected << QPair(3, QStringLiteral("0B")); QCOMPARE(result->data(), expected); } void shouldReactToRemoves() { // GIVEN Domain::LiveQuery> query; query.setFetchFunction([this] (const Domain::LiveQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("0A"))); add(createObject(1, QStringLiteral("1A"))); add(createObject(2, QStringLiteral("2A"))); add(createObject(3, QStringLiteral("0B"))); add(createObject(4, QStringLiteral("1B"))); add(createObject(5, QStringLiteral("2B"))); add(createObject(6, QStringLiteral("0C"))); add(createObject(7, QStringLiteral("1C"))); add(createObject(8, QStringLiteral("2C"))); }); }); query.setConvertFunction([] (QObject *object) { return QPair(object->property("objectId").toInt(), object->objectName()); }); query.setPredicateFunction([] (QObject *object) { return object->objectName().startsWith('0'); }); query.setRepresentsFunction([] (QObject *object, const QPair &output) { return object->property("objectId").toInt() == output.first; }); Domain::QueryResult>::Ptr result = query.result(); QTest::qWait(150); QList> expected; expected << QPair(0, QStringLiteral("0A")) << QPair(3, QStringLiteral("0B")) << QPair(6, QStringLiteral("0C")); QCOMPARE(result->data(), expected); // WHEN query.onRemoved(createObject(3, QStringLiteral("0B"))); query.onRemoved(createObject(4, QStringLiteral("1B"))); query.onRemoved(createObject(5, QStringLiteral("2B"))); // THEN expected.removeAt(1); QCOMPARE(result->data(), expected); } void shouldReactToChanges() { // GIVEN Domain::LiveQuery> query; query.setFetchFunction([this] (const Domain::LiveQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("0A"))); add(createObject(1, QStringLiteral("1A"))); add(createObject(2, QStringLiteral("2A"))); add(createObject(3, QStringLiteral("0B"))); add(createObject(4, QStringLiteral("1B"))); add(createObject(5, QStringLiteral("2B"))); add(createObject(6, QStringLiteral("0C"))); add(createObject(7, QStringLiteral("1C"))); add(createObject(8, QStringLiteral("2C"))); }); }); query.setConvertFunction([] (QObject *object) { return QPair(object->property("objectId").toInt(), object->objectName()); }); query.setUpdateFunction([] (QObject *object, QPair &output) { output.second = object->objectName(); }); query.setPredicateFunction([] (QObject *object) { return object->objectName().startsWith('0'); }); query.setRepresentsFunction([] (QObject *object, const QPair &output) { return object->property("objectId").toInt() == output.first; }); Domain::QueryResult>::Ptr result = query.result(); QTest::qWait(150); QList> expected; expected << QPair(0, QStringLiteral("0A")) << QPair(3, QStringLiteral("0B")) << QPair(6, QStringLiteral("0C")); QCOMPARE(result->data(), expected); bool replaceHandlerCalled = false; result->addPostReplaceHandler([&replaceHandlerCalled](const QPair &, int) { replaceHandlerCalled = true; }); // WHEN query.onChanged(createObject(3, QStringLiteral("0BB"))); // Then expected.clear(); expected << QPair(0, QStringLiteral("0A")) << QPair(3, QStringLiteral("0BB")) << QPair(6, QStringLiteral("0C")); QCOMPARE(result->data(), expected); QVERIFY(replaceHandlerCalled); } void shouldRemoveWhenChangesMakeInputUnsuitableForQuery() { // GIVEN Domain::LiveQuery> query; query.setFetchFunction([this] (const Domain::LiveQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("0A"))); add(createObject(1, QStringLiteral("1A"))); add(createObject(2, QStringLiteral("2A"))); add(createObject(3, QStringLiteral("0B"))); add(createObject(4, QStringLiteral("1B"))); add(createObject(5, QStringLiteral("2B"))); add(createObject(6, QStringLiteral("0C"))); add(createObject(7, QStringLiteral("1C"))); add(createObject(8, QStringLiteral("2C"))); }); }); query.setConvertFunction([] (QObject *object) { return QPair(object->property("objectId").toInt(), object->objectName()); }); query.setPredicateFunction([] (QObject *object) { return object->objectName().startsWith('0'); }); query.setRepresentsFunction([] (QObject *object, const QPair &output) { return object->property("objectId").toInt() == output.first; }); Domain::QueryResult>::Ptr result = query.result(); QTest::qWait(150); QList> expected; expected << QPair(0, QStringLiteral("0A")) << QPair(3, QStringLiteral("0B")) << QPair(6, QStringLiteral("0C")); QCOMPARE(result->data(), expected); bool replaceHandlerCalled = false; result->addPostReplaceHandler([&replaceHandlerCalled](const QPair &, int) { replaceHandlerCalled = true; }); // WHEN query.onChanged(createObject(3, QStringLiteral("1B"))); // Then expected.removeAt(1); QCOMPARE(result->data(), expected); QVERIFY(!replaceHandlerCalled); } void shouldAddWhenChangesMakeInputSuitableForQuery() { // GIVEN Domain::LiveQuery> query; query.setFetchFunction([this] (const Domain::LiveQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("0A"))); add(createObject(1, QStringLiteral("1A"))); add(createObject(2, QStringLiteral("2A"))); add(createObject(3, QStringLiteral("0B"))); add(createObject(4, QStringLiteral("1B"))); add(createObject(5, QStringLiteral("2B"))); add(createObject(6, QStringLiteral("0C"))); add(createObject(7, QStringLiteral("1C"))); add(createObject(8, QStringLiteral("2C"))); }); }); query.setConvertFunction([] (QObject *object) { return QPair(object->property("objectId").toInt(), object->objectName()); }); query.setPredicateFunction([] (QObject *object) { return object->objectName().startsWith('0'); }); query.setRepresentsFunction([] (QObject *object, const QPair &output) { return object->property("objectId").toInt() == output.first; }); Domain::QueryResult>::Ptr result = query.result(); QTest::qWait(150); QList> expected; expected << QPair(0, QStringLiteral("0A")) << QPair(3, QStringLiteral("0B")) << QPair(6, QStringLiteral("0C")); QCOMPARE(result->data(), expected); bool replaceHandlerCalled = false; result->addPostReplaceHandler([&replaceHandlerCalled](const QPair &, int) { replaceHandlerCalled = true; }); // WHEN query.onChanged(createObject(4, QStringLiteral("0BB"))); // Then expected << QPair(4, QStringLiteral("0BB")); QCOMPARE(result->data(), expected); QVERIFY(!replaceHandlerCalled); } void shouldEmptyAndFetchAgainOnReset() { // GIVEN bool afterReset = false; Domain::LiveQuery> query; query.setFetchFunction([this, &afterReset] (const Domain::LiveQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, &afterReset, add] { add(createObject(0, QStringLiteral("0A"))); add(createObject(1, QStringLiteral("1A"))); add(createObject(2, QStringLiteral("2A"))); add(createObject(3, QStringLiteral("0B"))); add(createObject(4, QStringLiteral("1B"))); add(createObject(5, QStringLiteral("2B"))); if (afterReset) { add(createObject(6, QStringLiteral("0C"))); add(createObject(7, QStringLiteral("1C"))); add(createObject(8, QStringLiteral("2C"))); } }); }); query.setConvertFunction([] (QObject *object) { return QPair(object->property("objectId").toInt(), object->objectName()); }); query.setPredicateFunction([&afterReset] (QObject *object) { if (afterReset) return object->objectName().startsWith('1'); else return object->objectName().startsWith('0'); }); Domain::QueryResult>::Ptr result = query.result(); int removeHandlerCallCount = 0; result->addPostRemoveHandler([&removeHandlerCallCount](const QPair &, int) { removeHandlerCallCount++; }); QTest::qWait(150); QVERIFY(!result->data().isEmpty()); QCOMPARE(removeHandlerCallCount, 0); // WHEN query.reset(); afterReset = true; QTest::qWait(150); // THEN QList> expected; expected << QPair(1, QStringLiteral("1A")) << QPair(4, QStringLiteral("1B")) << QPair(7, QStringLiteral("1C")); QVERIFY(afterReset); QCOMPARE(result->data(), expected); QCOMPARE(removeHandlerCallCount, 2); } }; ZANSHIN_TEST_MAIN(LiveQueryTest) #include "livequerytest.moc" diff --git a/tests/units/domain/liverelationshipquerytest.cpp b/tests/units/domain/liverelationshipquerytest.cpp index 34011ab3..ce1d96a0 100644 --- a/tests/units/domain/liverelationshipquerytest.cpp +++ b/tests/units/domain/liverelationshipquerytest.cpp @@ -1,483 +1,483 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens Copyright 2018 David Faure 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 "domain/livequery.h" #include "utils/jobhandler.h" #include "testlib/fakejob.h" using namespace Domain; typedef QSharedPointer QObjectPtr; static const char objectIdPropName[] = "objectId"; class LiveRelationshipQueryTest : public QObject { Q_OBJECT private: QObject *createObject(int id, const QString &name) { QObject *obj = new QObject(this); obj->setObjectName(name); obj->setProperty(objectIdPropName, id); return obj; } static bool compareObjectIds(QObject *obj1, QObject *obj2) { return obj1->property(objectIdPropName).toInt() == obj2->property(objectIdPropName).toInt(); } static bool isProject(QObject *obj) { return obj->objectName().startsWith(QLatin1String("Project")); } static QPair convertToPair(QObject *object) { return qMakePair(object->property(objectIdPropName).toInt(), object->objectName()); } static bool representsPair(QObject *object, const QPair &output) { return object->property(objectIdPropName).toInt() == output.first; }; private slots: void shouldHaveInitialFetchFunctionAndPredicate() { // GIVEN Domain::LiveRelationshipQuery> query; query.setFetchFunction([this] (const Domain::LiveQueryInput::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("ProjectA"))); add(createObject(1, QStringLiteral("ItemA"))); add(createObject(2, QStringLiteral("ParentA"))); add(createObject(3, QStringLiteral("ProjectB"))); add(createObject(4, QStringLiteral("ItemB"))); add(createObject(5, QStringLiteral("ParentB"))); add(createObject(6, QStringLiteral("ProjectC"))); add(createObject(7, QStringLiteral("ItemC"))); add(createObject(8, QStringLiteral("ParentC"))); }); }); query.setConvertFunction(convertToPair); query.setPredicateFunction(isProject); query.setCompareFunction(compareObjectIds); // WHEN Domain::QueryResult>::Ptr result = query.result(); result->data(); result = query.result(); // Should not cause any problem or wrong data QVERIFY(result->data().isEmpty()); // THEN QList> expected; expected << QPair(0, QStringLiteral("ProjectA")) << QPair(3, QStringLiteral("ProjectB")) << QPair(6, QStringLiteral("ProjectC")); QTRY_COMPARE(result->data(), expected); } void shouldFilterOutNullRawPointers() { // GIVEN auto query = Domain::LiveRelationshipQuery(); query.setFetchFunction([this] (const Domain::LiveQueryInput::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(QStringLiteral("0")); add(QStringLiteral("1")); add(QString()); add(QStringLiteral("a")); add(QStringLiteral("2")); }); }); query.setConvertFunction([this] (const QString &s) -> QObject* { bool ok = false; const int id = s.toInt(&ok); if (ok) { auto object = new QObject(this); object->setProperty("id", id); return object; } else { - return Q_NULLPTR; + return nullptr; } }); query.setPredicateFunction([] (const QString &s) { return !s.isEmpty(); }); // WHEN auto result = query.result(); result->data(); result = query.result(); // Should not cause any problem or wrong data QVERIFY(result->data().isEmpty()); // THEN QTRY_COMPARE(result->data().size(), 3); QCOMPARE(result->data().at(0)->property("id").toInt(), 0); QCOMPARE(result->data().at(1)->property("id").toInt(), 1); QCOMPARE(result->data().at(2)->property("id").toInt(), 2); } void shouldFilterOutNullSharedPointers() { // GIVEN auto query = Domain::LiveRelationshipQuery(); query.setFetchFunction([this] (const Domain::LiveQueryInput::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(QStringLiteral("0")); add(QStringLiteral("1")); add(QString()); add(QStringLiteral("a")); add(QStringLiteral("2")); }); }); query.setConvertFunction([this] (const QString &s) { bool ok = false; const int id = s.toInt(&ok); if (ok) { auto object = QObjectPtr::create(); object->setProperty("id", id); return object; } else { return QObjectPtr(); } }); query.setPredicateFunction([] (const QString &s) { return !s.isEmpty(); }); // WHEN auto result = query.result(); result->data(); result = query.result(); // Should not cause any problem or wrong data QVERIFY(result->data().isEmpty()); // THEN QTRY_COMPARE(result->data().size(), 3); QCOMPARE(result->data().at(0)->property("id").toInt(), 0); QCOMPARE(result->data().at(1)->property("id").toInt(), 1); QCOMPARE(result->data().at(2)->property("id").toInt(), 2); } void shouldDealWithSeveralFetchesProperly() { // GIVEN Domain::LiveRelationshipQuery> query; query.setFetchFunction([this] (const Domain::LiveRelationshipQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("ProjectA"))); add(createObject(1, QStringLiteral("ItemA"))); add(createObject(2, QStringLiteral("ParentA"))); add(createObject(3, QStringLiteral("ProjectB"))); add(createObject(4, QStringLiteral("ItemB"))); add(createObject(5, QStringLiteral("ParentB"))); add(createObject(6, QStringLiteral("ProjectC"))); add(createObject(7, QStringLiteral("ItemC"))); add(createObject(8, QStringLiteral("ParentC"))); }); }); query.setConvertFunction(convertToPair); query.setPredicateFunction(isProject); for (int i = 0; i < 2; i++) { // WHEN * 2 Domain::QueryResult>::Ptr result = query.result(); // THEN * 2 QVERIFY(result->data().isEmpty()); QList> expected; expected << QPair(0, QStringLiteral("ProjectA")) << QPair(3, QStringLiteral("ProjectB")) << QPair(6, QStringLiteral("ProjectC")); QTRY_COMPARE(result->data(), expected); } } void shouldClearProviderWhenDeleted() { // GIVEN auto query = new Domain::LiveRelationshipQuery>; query->setFetchFunction([this] (const Domain::LiveRelationshipQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("ProjectA"))); add(createObject(1, QStringLiteral("ItemA"))); add(createObject(2, QStringLiteral("ParentA"))); }); }); query->setConvertFunction(convertToPair); query->setPredicateFunction(isProject); query->setCompareFunction(compareObjectIds); Domain::QueryResult>::Ptr result = query->result(); QTRY_COMPARE(result->data().count(), 1); // WHEN delete query; // THEN QVERIFY(result->data().isEmpty()); } void shouldReactToAdds() { // GIVEN Domain::LiveRelationshipQuery> query; query.setFetchFunction([this] (const Domain::LiveRelationshipQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("ProjectA"))); add(createObject(1, QStringLiteral("ItemA"))); add(createObject(2, QStringLiteral("ParentA"))); }); }); query.setConvertFunction(convertToPair); query.setPredicateFunction(isProject); query.setCompareFunction(compareObjectIds); Domain::QueryResult>::Ptr result = query.result(); QList> expected{ qMakePair(0, QString::fromLatin1("ProjectA")) }; QTRY_COMPARE(result->data(), expected); // WHEN query.onAdded(createObject(3, QStringLiteral("ProjectB"))); query.onAdded(createObject(4, QStringLiteral("ItemB"))); query.onAdded(createObject(5, QStringLiteral("ParentB"))); // THEN expected << QPair(3, QStringLiteral("ProjectB")); QCOMPARE(result->data(), expected); } void shouldReactToRemoves() { // GIVEN Domain::LiveRelationshipQuery> query; query.setFetchFunction([this] (const Domain::LiveRelationshipQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("ProjectA"))); add(createObject(1, QStringLiteral("ItemA"))); add(createObject(2, QStringLiteral("ParentA"))); }); }); query.setConvertFunction(convertToPair); query.setPredicateFunction(isProject); query.setCompareFunction(compareObjectIds); query.setRepresentsFunction(representsPair); Domain::QueryResult>::Ptr result = query.result(); QList> expected{ qMakePair(0, QString::fromLatin1("ProjectA")) }; QTRY_COMPARE(result->data(), expected); // WHEN query.setFetchFunction([this] (const Domain::LiveRelationshipQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] {}); }); // unrelated remove -> ignore query.onRemoved(createObject(3, QStringLiteral("ItemB"))); QTRY_COMPARE(result->data(), expected); // remove item -> reset happens query.onRemoved(createObject(1, QStringLiteral("ItemA"))); // THEN expected.clear(); QTRY_COMPARE(result->data(), expected); } void shouldReactToChanges() { // GIVEN Domain::LiveRelationshipQuery> query; query.setFetchFunction([this] (const Domain::LiveRelationshipQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("ProjectA"))); add(createObject(1, QStringLiteral("ItemA"))); add(createObject(2, QStringLiteral("ParentA"))); add(createObject(3, QStringLiteral("ProjectB"))); add(createObject(4, QStringLiteral("ItemB"))); add(createObject(5, QStringLiteral("ParentB"))); add(createObject(6, QStringLiteral("ProjectC"))); add(createObject(7, QStringLiteral("ItemC"))); add(createObject(8, QStringLiteral("ParentC"))); }); }); query.setConvertFunction(convertToPair); query.setPredicateFunction(isProject); query.setCompareFunction(compareObjectIds); query.setRepresentsFunction(representsPair); Domain::QueryResult>::Ptr result = query.result(); QList> expected{ qMakePair(0, QString::fromLatin1("ProjectA")), qMakePair(3, QString::fromLatin1("ProjectB")), qMakePair(6, QString::fromLatin1("ProjectC")) }; QTRY_COMPARE(result->data(), expected); // WHEN query.setFetchFunction([this] (const Domain::LiveRelationshipQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(0, QStringLiteral("ProjectA"))); add(createObject(1, QStringLiteral("ItemA"))); add(createObject(2, QStringLiteral("ParentA"))); add(createObject(3, QStringLiteral("ProjectB-Renamed"))); add(createObject(4, QStringLiteral("ItemB"))); add(createObject(5, QStringLiteral("ParentB"))); add(createObject(6, QStringLiteral("ProjectC"))); add(createObject(7, QStringLiteral("ItemC"))); add(createObject(8, QStringLiteral("ParentC"))); }); }); query.onChanged(createObject(3, QStringLiteral("whatever"))); // THEN expected[1] = qMakePair(3, QString::fromLatin1("ProjectB-Renamed")); QTRY_COMPARE(result->data(), expected); } void shouldIgnoreUnrelatedChangesWhenEmpty() { // GIVEN Domain::LiveRelationshipQuery> query; bool listingDone = false; query.setFetchFunction([this, &listingDone] (const Domain::LiveRelationshipQuery::AddFunction &add) { Q_UNUSED(add); Utils::JobHandler::install(new FakeJob, [&listingDone] { listingDone = true; }); }); query.setConvertFunction(convertToPair); query.setPredicateFunction(isProject); query.setCompareFunction(compareObjectIds); query.setRepresentsFunction(representsPair); Domain::QueryResult>::Ptr result = query.result(); QTRY_VERIFY(listingDone); listingDone = false; QVERIFY(result->data().isEmpty()); // WHEN query.onChanged(createObject(1, QStringLiteral("ProjectA"))); // THEN QTest::qWait(150); QVERIFY(!listingDone); QVERIFY(result->data().isEmpty()); } void shouldAddWhenChangesMakeInputSuitableForQuery() { // GIVEN Domain::LiveRelationshipQuery> query; bool listingDone = false; query.setFetchFunction([this, &listingDone] (const Domain::LiveRelationshipQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add, &listingDone] { add(createObject(1, QStringLiteral("ItemA"))); add(createObject(2, QStringLiteral("ParentA"))); listingDone = true; }); }); query.setConvertFunction(convertToPair); query.setPredicateFunction(isProject); query.setCompareFunction(compareObjectIds); query.setRepresentsFunction(representsPair); Domain::QueryResult>::Ptr result = query.result(); QList> expected; QTRY_VERIFY(listingDone); QCOMPARE(result->data(), expected); // WHEN query.setFetchFunction([this] (const Domain::LiveRelationshipQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, add] { add(createObject(1, QStringLiteral("ItemA"))); add(createObject(2, QStringLiteral("ProjectA"))); // parent promoted to project }); }); query.onChanged(createObject(2, QStringLiteral("whatever"))); // Then expected << qMakePair(2, QStringLiteral("ProjectA")); QTRY_COMPARE(result->data(), expected); } void shouldEmptyAndFetchAgainOnReset() { // GIVEN bool afterReset = false; Domain::LiveRelationshipQuery> query; query.setFetchFunction([this, &afterReset] (const Domain::LiveRelationshipQuery::AddFunction &add) { Utils::JobHandler::install(new FakeJob, [this, &afterReset, add] { add(createObject(0, QStringLiteral("ProjectA"))); add(createObject(1, QStringLiteral("ItemA"))); add(createObject(2, QStringLiteral("ParentA"))); add(createObject(3, QStringLiteral("ProjectB"))); add(createObject(4, QStringLiteral("ItemB"))); add(createObject(5, QStringLiteral("ParentB"))); if (afterReset) { add(createObject(6, QStringLiteral("ProjectC"))); add(createObject(7, QStringLiteral("ItemC"))); add(createObject(8, QStringLiteral("ParentC"))); } }); }); query.setConvertFunction(convertToPair); query.setPredicateFunction([&afterReset] (QObject *object) { if (afterReset) return object->objectName().startsWith(QLatin1String("Item")); else return object->objectName().startsWith(QLatin1String("Project")); }); query.setCompareFunction(compareObjectIds); Domain::QueryResult>::Ptr result = query.result(); int removeHandlerCallCount = 0; result->addPostRemoveHandler([&removeHandlerCallCount](const QPair &, int) { removeHandlerCallCount++; }); QTRY_VERIFY(!result->data().isEmpty()); QCOMPARE(removeHandlerCallCount, 0); // WHEN query.reset(); afterReset = true; // THEN const QList> expected = { qMakePair(1, QStringLiteral("ItemA")), qMakePair(4, QStringLiteral("ItemB")), qMakePair(7, QStringLiteral("ItemC")) }; QTRY_COMPARE(result->data(), expected); QCOMPARE(removeHandlerCallCount, 2); } }; ZANSHIN_TEST_MAIN(LiveRelationshipQueryTest) #include "liverelationshipquerytest.moc" diff --git a/tests/units/domain/tasktest.cpp b/tests/units/domain/tasktest.cpp index 23f6cc10..1f4de04a 100644 --- a/tests/units/domain/tasktest.cpp +++ b/tests/units/domain/tasktest.cpp @@ -1,307 +1,307 @@ /* 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 "domain/task.h" #include "utils/datetime.h" using namespace Domain; class TaskTest : public QObject { Q_OBJECT public: - explicit TaskTest(QObject *parent = Q_NULLPTR) + explicit TaskTest(QObject *parent = nullptr) : QObject(parent) { qRegisterMetaType(); qRegisterMetaType(); } private slots: void shouldHaveEmptyPropertiesByDefault() { Task t; QCOMPARE(t.text(), QString()); QCOMPARE(t.title(), QString()); QCOMPARE(t.isDone(), false); QCOMPARE(t.startDate(), QDate()); QCOMPARE(t.dueDate(), QDate()); QCOMPARE(t.doneDate(), QDate()); QCOMPARE(t.recurrence(), Domain::Task::NoRecurrence); QVERIFY(t.attachments().isEmpty()); } void shouldNotifyTextChanges() { Task t; QSignalSpy spy(&t, &Task::textChanged); t.setText(QStringLiteral("foo")); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().toString(), QStringLiteral("foo")); } void shouldNotNotifyIdenticalTextChanges() { Task t; t.setText(QStringLiteral("foo")); QSignalSpy spy(&t, &Task::textChanged); t.setText(QStringLiteral("foo")); QCOMPARE(spy.count(), 0); } void shouldNotifyTitleChanges() { Task t; QSignalSpy spy(&t, &Task::titleChanged); t.setTitle(QStringLiteral("foo")); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().toString(), QStringLiteral("foo")); } void shouldNotNotifyIdenticalTitleChanges() { Task t; t.setTitle(QStringLiteral("foo")); QSignalSpy spy(&t, &Task::titleChanged); t.setTitle(QStringLiteral("foo")); QCOMPARE(spy.count(), 0); } void shouldHaveValueBasedAttachment() { Task::Attachment a; QVERIFY(!a.isValid()); QVERIFY(!a.isUri()); QCOMPARE(a.uri(), QUrl()); QCOMPARE(a.data(), QByteArray()); QCOMPARE(a.label(), QString()); QCOMPARE(a.mimeType(), QString()); QCOMPARE(a.iconName(), QString()); a.setUri(QUrl("https://www.kde.org")); QVERIFY(a.isValid()); QVERIFY(a.isUri()); QCOMPARE(a.uri(), QUrl("https://www.kde.org")); QCOMPARE(a.data(), QByteArray()); QCOMPARE(a.label(), QString()); QCOMPARE(a.mimeType(), QString()); QCOMPARE(a.iconName(), QString()); a.setData(QByteArrayLiteral("foobar")); QVERIFY(a.isValid()); QVERIFY(!a.isUri()); QCOMPARE(a.uri(), QUrl()); QCOMPARE(a.data(), QByteArrayLiteral("foobar")); QCOMPARE(a.label(), QString()); QCOMPARE(a.mimeType(), QString()); QCOMPARE(a.iconName(), QString()); a.setLabel(QStringLiteral("baz")); QVERIFY(a.isValid()); QVERIFY(!a.isUri()); QCOMPARE(a.uri(), QUrl()); QCOMPARE(a.data(), QByteArrayLiteral("foobar")); QCOMPARE(a.label(), QStringLiteral("baz")); QCOMPARE(a.mimeType(), QString()); QCOMPARE(a.iconName(), QString()); a.setMimeType(QStringLiteral("text/plain")); QVERIFY(a.isValid()); QVERIFY(!a.isUri()); QCOMPARE(a.uri(), QUrl()); QCOMPARE(a.data(), QByteArrayLiteral("foobar")); QCOMPARE(a.label(), QStringLiteral("baz")); QCOMPARE(a.mimeType(), QStringLiteral("text/plain")); QCOMPARE(a.iconName(), QString()); a.setIconName(QStringLiteral("text")); QVERIFY(a.isValid()); QVERIFY(!a.isUri()); QCOMPARE(a.uri(), QUrl()); QCOMPARE(a.data(), QByteArrayLiteral("foobar")); QCOMPARE(a.label(), QStringLiteral("baz")); QCOMPARE(a.mimeType(), QStringLiteral("text/plain")); QCOMPARE(a.iconName(), QStringLiteral("text")); a.setUri(QUrl("https://www.kde.org")); QVERIFY(a.isValid()); QVERIFY(a.isUri()); QCOMPARE(a.uri(), QUrl("https://www.kde.org")); QCOMPARE(a.data(), QByteArray()); QCOMPARE(a.label(), QStringLiteral("baz")); QCOMPARE(a.mimeType(), QStringLiteral("text/plain")); QCOMPARE(a.iconName(), QStringLiteral("text")); a.setUri(QUrl()); QVERIFY(!a.isValid()); QVERIFY(!a.isUri()); QCOMPARE(a.uri(), QUrl()); QCOMPARE(a.data(), QByteArray()); QCOMPARE(a.label(), QStringLiteral("baz")); QCOMPARE(a.mimeType(), QStringLiteral("text/plain")); QCOMPARE(a.iconName(), QStringLiteral("text")); } void shouldNotifyStatusChanges() { Task t; QSignalSpy spy(&t, &Task::doneChanged); t.setDone(true); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().toBool(), true); } void shouldNotNotifyIdenticalStatusChanges() { Task t; t.setDone(true); QSignalSpy spy(&t, &Task::doneChanged); t.setDone(true); QCOMPARE(spy.count(), 0); } void shouldNotifyStartDateChanges() { Task t; QSignalSpy spy(&t, &Task::startDateChanged); t.setStartDate(QDate(2014, 1, 13)); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().toDate(), QDate(2014, 1, 13)); } void shouldNotNotifyIdenticalStartDateChanges() { Task t; t.setStartDate(QDate(2014, 1, 13)); QSignalSpy spy(&t, &Task::startDateChanged); t.setStartDate(QDate(2014, 1, 13)); QCOMPARE(spy.count(), 0); } void shouldNotifyDueDateChanges() { Task t; QSignalSpy spy(&t, &Task::dueDateChanged); t.setDueDate(QDate(2014, 1, 13)); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().toDate(), QDate(2014, 1, 13)); } void shouldNotNotifyIdenticalDueDateChanges() { Task t; t.setDueDate(QDate(2014, 1, 13)); QSignalSpy spy(&t, &Task::dueDateChanged); t.setDueDate(QDate(2014, 1, 13)); QCOMPARE(spy.count(), 0); } void shouldNotifyRecurrenceChanges() { Task t; QSignalSpy spy(&t, &Task::recurrenceChanged); t.setRecurrence(Task::RecursWeekly); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().value(), Task::RecursWeekly); } void shouldNotNotifyIdenticalRecurrenceChanges() { Task t; t.setRecurrence(Task::RecursWeekly); QSignalSpy spy(&t, &Task::recurrenceChanged); t.setRecurrence(Task::RecursWeekly); QCOMPARE(spy.count(), 0); } void shouldNotifyAttachmentsChanges() { Task::Attachments attachments; attachments.append(Task::Attachment(QByteArrayLiteral("foobar"))); attachments.append(Task::Attachment(QUrl("https://www.kde.org"))); Task t; QSignalSpy spy(&t, &Task::attachmentsChanged); t.setAttachments(attachments); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().value(), attachments); } void shouldNotNotifyIdenticalAttachmentsChanges() { Task::Attachments attachments; attachments.append(Task::Attachment(QByteArrayLiteral("foobar"))); attachments.append(Task::Attachment(QUrl("https://www.kde.org"))); Task t; t.setAttachments(attachments); QSignalSpy spy(&t, &Task::attachmentsChanged); t.setAttachments(attachments); QCOMPARE(spy.count(), 0); } void shouldNotifyDoneDateChanges() { Task t; QSignalSpy spy(&t, &Task::doneDateChanged); t.setDoneDate(QDate(2014, 1, 13)); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().toDate(), QDate(2014, 1, 13)); } void shouldNotNotifyIdenticalDoneDateChanges() { Task t; t.setDoneDate(QDate(2014, 1, 13)); QSignalSpy spy(&t, &Task::doneDateChanged); t.setDoneDate(QDate(2014, 1, 13)); QCOMPARE(spy.count(), 0); } void shouldNotifyDoneDateSet() { Task t; QSignalSpy spy(&t, &Task::doneDateChanged); t.setDone(true); QCOMPARE(spy.count(), 1); QCOMPARE(spy.takeFirst().at(0).toDate(), Utils::DateTime::currentDate()); } void shouldNotifyDoneDateUnset() { Task t; t.setDone(true); QSignalSpy spy(&t, &Task::doneDateChanged); t.setDone(false); QCOMPARE(spy.count(), 1); QCOMPARE(spy.takeFirst().at(0).toDate(), QDate()); } }; ZANSHIN_TEST_MAIN(TaskTest) #include "tasktest.moc" diff --git a/tests/units/migrator/zanshin021migrationtest.cpp b/tests/units/migrator/zanshin021migrationtest.cpp index 8ef70967..368efeb6 100644 --- a/tests/units/migrator/zanshin021migrationtest.cpp +++ b/tests/units/migrator/zanshin021migrationtest.cpp @@ -1,184 +1,184 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens Copyright 2014 David Faure 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 "zanshin021migrator.h" #include #include #include #include #include #include #include #include #include "akonadi/akonadicollectionfetchjobinterface.h" #include "akonadi/akonadiitemfetchjobinterface.h" #include "akonadi/akonadistorage.h" #include #include #include #include class Zanshin021MigrationTest : public QObject { Q_OBJECT public: - explicit Zanshin021MigrationTest(QObject *parent = Q_NULLPTR) + explicit Zanshin021MigrationTest(QObject *parent = nullptr) : QObject(parent) { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); } private slots: void initTestCase() { QVERIFY(TestLib::TestSafety::checkTestIsIsolated()); auto storage = Akonadi::StorageInterface::Ptr(new Akonadi::Storage); TestLib::AkonadiDebug::dumpTree(storage); //QProcess proc; //proc.execute("akonadiconsole"); //qApp->exec(); } void cleanupTestCase() { // Give a chance for jobs still waiting for an event loop // run to be deleted through deleteLater() QTest::qWait(10); } void shouldFetchAllItems() { // GIVEN Zanshin021Migrator migrator; // WHEN Zanshin021Migrator::SeenItemHash hash = migrator.fetchAllItems(); // THEN // the migrator gathered all items and the initial state of "is a project" for each one is correct m_expectedUids.insert(QStringLiteral("child-of-project"), false); m_expectedUids.insert(QStringLiteral("new-project-with-property"), true); m_expectedUids.insert(QStringLiteral("old-project-with-comment"), false); // not yet m_expectedUids.insert(QStringLiteral("project-with-comment-and-property"), true); m_expectedUids.insert(QStringLiteral("project-with-children"), false); // not yet m_expectedUids.insert(QStringLiteral("standalone-task"), false); checkExpectedIsProject(hash, m_expectedUids); } void shouldMigrateCommentToProperty() { // GIVEN Zanshin021Migrator migrator; Zanshin021Migrator::SeenItemHash hash = migrator.fetchAllItems(); // WHEN Akonadi::TransactionSequence *sequence = new Akonadi::TransactionSequence(); migrator.migrateProjectComments(hash, sequence); // THEN // the project with an old-style comment was modified to have the property SeenItem item = hash.value(QStringLiteral("old-project-with-comment")); QVERIFY(item.isDirty()); m_expectedUids[QStringLiteral("old-project-with-comment")] = true; // migrated! checkExpectedIsProject(hash, m_expectedUids); m_expectedUids[QStringLiteral("old-project-with-comment")] = false; // revert for now sequence->rollback(); sequence->exec(); } void shouldMigrateTaskWithChildrenToProject() { // GIVEN Zanshin021Migrator migrator; Zanshin021Migrator::SeenItemHash hash = migrator.fetchAllItems(); // WHEN Akonadi::TransactionSequence *sequence = new Akonadi::TransactionSequence(); migrator.migrateProjectWithChildren(hash, sequence); // THEN // the project with children was modified to have the property SeenItem item = hash.value(QStringLiteral("project-with-children")); QVERIFY(item.isDirty()); m_expectedUids[QStringLiteral("project-with-children")] = true; // migrated! checkExpectedIsProject(hash, m_expectedUids); m_expectedUids[QStringLiteral("project-with-children")] = false; // revert for now sequence->rollback(); sequence->exec(); } void shouldMigrateProjects() { // GIVEN Zanshin021Migrator migrator; // WHEN const bool ret = migrator.migrateProjects(); // THEN QVERIFY(ret); // success m_expectedUids[QStringLiteral("old-project-with-comment")] = true; // migrated! m_expectedUids[QStringLiteral("project-with-children")] = true; // migrated! Zanshin021Migrator::SeenItemHash hash = migrator.fetchAllItems(); checkExpectedIsProject(hash, m_expectedUids); } private: void checkExpectedIsProject(const Zanshin021Migrator::SeenItemHash &hash, const QMap &expectedItems) { QStringList uids = hash.keys(); uids.sort(); if (uids.count() != expectedItems.count()) // QCOMPARE for QStringList isn't verbose enough qWarning() << "Got" << uids << "expected" << expectedItems.keys(); QCOMPARE(uids, QStringList(expectedItems.keys())); for (auto it = expectedItems.constBegin(); it != expectedItems.constEnd(); ++it) { //qDebug() << it.key(); QCOMPARE(Zanshin021Migrator::isProject(hash.value(it.key()).item()), it.value()); } } QMap m_expectedUids; }; ZANSHIN_TEST_MAIN(Zanshin021MigrationTest) #include "zanshin021migrationtest.moc" diff --git a/tests/units/presentation/applicationmodeltest.cpp b/tests/units/presentation/applicationmodeltest.cpp index efe3cd3f..54d7cae6 100644 --- a/tests/units/presentation/applicationmodeltest.cpp +++ b/tests/units/presentation/applicationmodeltest.cpp @@ -1,260 +1,260 @@ /* 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/dependencymanager.h" #include "utils/jobhandler.h" #include "utils/mockobject.h" #include "presentation/applicationmodel.h" #include "presentation/availablepagesmodel.h" #include "presentation/availablesourcesmodel.h" #include "presentation/editormodel.h" #include "presentation/pagemodel.h" #include "presentation/errorhandler.h" #include "presentation/runningtaskmodel.h" #include "testlib/fakejob.h" using namespace mockitopp; using namespace mockitopp::matcher; class FakeErrorHandler : public Presentation::ErrorHandler { public: void doDisplayMessage(const QString &message) override { m_message = message; } QString m_message; }; class FakePageModel : public Presentation::PageModel { Q_OBJECT public: - explicit FakePageModel(QObject *parent = Q_NULLPTR) + explicit FakePageModel(QObject *parent = nullptr) : Presentation::PageModel(parent) {} Domain::Task::Ptr addItem(const QString &, const QModelIndex &) override { return {}; } void removeItem(const QModelIndex &) override {} void promoteItem(const QModelIndex &) override {} private: QAbstractItemModel *createCentralListModel() override { return {}; } }; class ApplicationModelTest : public QObject { Q_OBJECT public: - explicit ApplicationModelTest(QObject *parent = Q_NULLPTR) + explicit ApplicationModelTest(QObject *parent = nullptr) : QObject(parent) { Utils::DependencyManager::globalInstance().add( [] (Utils::DependencyManager *) { return new Presentation::AvailablePagesModel(Domain::DataSourceQueries::Ptr(), Domain::ProjectQueries::Ptr(), Domain::ProjectRepository::Ptr(), Domain::ContextQueries::Ptr(), Domain::ContextRepository::Ptr(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); }); Utils::DependencyManager::globalInstance().add( [] (Utils::DependencyManager *) { return new Presentation::EditorModel; }); Utils::DependencyManager::globalInstance().add( [] (Utils::DependencyManager *) { return new Presentation::AvailableSourcesModel(Domain::DataSourceQueries::Ptr(), Domain::DataSourceRepository::Ptr()); }); Utils::DependencyManager::globalInstance().add( [] (Utils::DependencyManager *) { return new Presentation::RunningTaskModel(Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); }); } private slots: void shouldProvideAvailableSourcesModel() { // GIVEN Presentation::ApplicationModel app; // WHEN QObject *available = app.availableSources(); // THEN QVERIFY(qobject_cast(available)); } void shouldProvideAvailablePagesModel() { // GIVEN Presentation::ApplicationModel app; // WHEN QObject *available = app.availablePages(); // THEN QVERIFY(qobject_cast(available)); } void shouldProvideCurrentPage() { // GIVEN Presentation::ApplicationModel app; QVERIFY(!app.currentPage()); QSignalSpy spy(&app, &Presentation::ApplicationModel::currentPageChanged); // WHEN auto page = new FakePageModel(this); app.setCurrentPage(page); // THEN QCOMPARE(app.currentPage(), page); QCOMPARE(spy.count(), 1); QCOMPARE(spy.takeFirst().at(0).value(), page); } void shouldSupportNullPage() { // GIVEN Presentation::ApplicationModel app; auto page = new FakePageModel(this); app.setCurrentPage(page); QCOMPARE(app.currentPage(), page); QSignalSpy spy(&app, &Presentation::ApplicationModel::currentPageChanged); // WHEN - app.setCurrentPage(Q_NULLPTR); + app.setCurrentPage(nullptr); // THEN QVERIFY(!app.currentPage()); QCOMPARE(spy.count(), 1); QVERIFY(!spy.takeFirst().at(0).value()); } void shouldTakeOwnershipOfCurrentPage() { // GIVEN auto page = QPointer(new FakePageModel(this)); { Presentation::ApplicationModel app; // WHEN app.setCurrentPage(page.data()); // THEN QVERIFY(!page->parent()); QCOMPARE(app.currentPage(), page.data()); } // We don't crash and page is deleted QVERIFY(!page); } void shouldProvideEditorModel() { // GIVEN Presentation::ApplicationModel app; // WHEN QObject *page = app.editor(); // THEN QVERIFY(qobject_cast(page)); } void shouldProvideRunningTaskModel() { // GIVEN Presentation::ApplicationModel app; // WHEN QObject *model = app.runningTaskModel(); // THEN QVERIFY(qobject_cast(model)); } void shouldSetErrorHandlerToAllModels() { // GIVEN // An ErrorHandler FakeErrorHandler errorHandler; Presentation::ApplicationModel app; app.setCurrentPage(new FakePageModel); // WHEN app.setErrorHandler(&errorHandler); // THEN auto availableSource = static_cast(app.availableSources()); auto availablePages = static_cast(app.availablePages()); auto editor = static_cast(app.editor()); auto page = static_cast(app.currentPage()); auto runningTask = static_cast(app.runningTaskModel()); QCOMPARE(availableSource->errorHandler(), &errorHandler); QCOMPARE(availablePages->errorHandler(), &errorHandler); QCOMPARE(editor->errorHandler(), &errorHandler); QCOMPARE(page->errorHandler(), &errorHandler); QCOMPARE(runningTask->errorHandler(), &errorHandler); // WHEN FakeErrorHandler errorHandler2; app.setErrorHandler(&errorHandler2); // THEN QCOMPARE(availableSource->errorHandler(), &errorHandler2); QCOMPARE(availablePages->errorHandler(), &errorHandler2); QCOMPARE(editor->errorHandler(), &errorHandler2); QCOMPARE(page->errorHandler(), &errorHandler2); QCOMPARE(runningTask->errorHandler(), &errorHandler2); } void shouldClearJobHandlersOnExit() { // GIVEN auto app = new Presentation::ApplicationModel; Utils::JobHandler::install(new FakeJob, [] { qFatal("Shouldn't happen"); }); QCOMPARE(Utils::JobHandler::jobCount(), 1); // WHEN delete app; // THEN QCOMPARE(Utils::JobHandler::jobCount(), 0); QTest::qWait(FakeJob::DURATION * 2); } }; ZANSHIN_TEST_MAIN(ApplicationModelTest) #include "applicationmodeltest.moc" diff --git a/tests/units/presentation/availablesourcesmodeltest.cpp b/tests/units/presentation/availablesourcesmodeltest.cpp index 6f150110..27b78c63 100644 --- a/tests/units/presentation/availablesourcesmodeltest.cpp +++ b/tests/units/presentation/availablesourcesmodeltest.cpp @@ -1,282 +1,282 @@ /* 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" #define ZANSHIN_I_SWEAR_I_AM_IN_A_PRESENTATION_TEST #include "domain/datasourcequeries.h" #include "domain/datasourcerepository.h" #include "presentation/availablesourcesmodel.h" #include "presentation/querytreemodelbase.h" #include "presentation/errorhandler.h" #include "testlib/fakejob.h" Q_DECLARE_METATYPE(QModelIndex); using namespace mockitopp; using namespace mockitopp::matcher; class FakeErrorHandler : public Presentation::ErrorHandler { public: void doDisplayMessage(const QString &message) override { m_message = message; } QString m_message; }; class AvailableSourcesModelTest : public QObject { Q_OBJECT public: - explicit AvailableSourcesModelTest(QObject *parent = Q_NULLPTR) + explicit AvailableSourcesModelTest(QObject *parent = nullptr) : QObject(parent) { qRegisterMetaType(); } private slots: void shouldListAvailableSources() { // GIVEN // Two top level sources auto source1 = Domain::DataSource::Ptr::create(); source1->setName(QStringLiteral("Source 1")); source1->setIconName(QStringLiteral("foo-icon")); source1->setSelected(true); auto source2 = Domain::DataSource::Ptr::create(); source2->setName(QStringLiteral("Source 2")); source2->setSelected(false); source2->setContentTypes(Domain::DataSource::Tasks); auto topLevelProvider = Domain::QueryResultProvider::Ptr::create(); auto topLevelResult = Domain::QueryResult::create(topLevelProvider); topLevelProvider->append(source1); topLevelProvider->append(source2); // Two other sources under source1 auto source3 = Domain::DataSource::Ptr::create(); source3->setName(QStringLiteral("Source 3")); source3->setSelected(false); source3->setContentTypes(Domain::DataSource::Notes); auto source4 = Domain::DataSource::Ptr::create(); source4->setSelected(true); source4->setName(QStringLiteral("Source 4")); source4->setContentTypes(Domain::DataSource::Notes | Domain::DataSource::Tasks); auto source1Provider = Domain::QueryResultProvider::Ptr::create(); auto source1Result = Domain::QueryResult::create(source1Provider); source1Provider->append(source3); source1Provider->append(source4); // Nothing under source2, source3 or source4 auto source2Provider = Domain::QueryResultProvider::Ptr::create(); auto source2Result = Domain::QueryResult::create(source2Provider); auto source3Provider = Domain::QueryResultProvider::Ptr::create(); auto source3Result = Domain::QueryResult::create(source3Provider); auto source4Provider = Domain::QueryResultProvider::Ptr::create(); auto source4Result = Domain::QueryResult::create(source4Provider); Utils::MockObject sourceQueriesMock; sourceQueriesMock(&Domain::DataSourceQueries::findTopLevel).when().thenReturn(topLevelResult); sourceQueriesMock(&Domain::DataSourceQueries::findChildren).when(source1).thenReturn(source1Result); sourceQueriesMock(&Domain::DataSourceQueries::findChildren).when(source2).thenReturn(source2Result); sourceQueriesMock(&Domain::DataSourceQueries::findChildren).when(source3).thenReturn(source3Result); sourceQueriesMock(&Domain::DataSourceQueries::findChildren).when(source4).thenReturn(source4Result); // We'll simulate a default source change later on sourceQueriesMock(&Domain::DataSourceQueries::isDefaultSource).when(source1).thenReturn(false); sourceQueriesMock(&Domain::DataSourceQueries::isDefaultSource).when(source2).thenReturn(true) .thenReturn(false); sourceQueriesMock(&Domain::DataSourceQueries::isDefaultSource).when(source3).thenReturn(false); sourceQueriesMock(&Domain::DataSourceQueries::isDefaultSource).when(source4).thenReturn(false) .thenReturn(false) .thenReturn(true); Utils::MockObject sourceRepositoryMock; Presentation::AvailableSourcesModel sources(sourceQueriesMock.getInstance(), sourceRepositoryMock.getInstance(), - Q_NULLPTR); + nullptr); // WHEN QAbstractItemModel *model = sources.sourceListModel(); // THEN const QModelIndex source1Index = model->index(0, 0); const QModelIndex source2Index = model->index(1, 0); const QModelIndex source3Index = model->index(0, 0, source1Index); const QModelIndex source4Index = model->index(1, 0, source1Index); QCOMPARE(model->rowCount(), 2); QCOMPARE(model->rowCount(source1Index), 2); QCOMPARE(model->rowCount(source2Index), 0); QCOMPARE(model->rowCount(source3Index), 0); QCOMPARE(model->rowCount(source4Index), 0); const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; QCOMPARE(model->flags(source1Index), defaultFlags); QCOMPARE(model->flags(source2Index), defaultFlags | Qt::ItemIsUserCheckable); QCOMPARE(model->flags(source3Index), defaultFlags | Qt::ItemIsUserCheckable); QCOMPARE(model->flags(source4Index), defaultFlags | Qt::ItemIsUserCheckable); QCOMPARE(model->data(source1Index).toString(), source1->name()); QCOMPARE(model->data(source2Index).toString(), source2->name()); QCOMPARE(model->data(source3Index).toString(), source3->name()); QCOMPARE(model->data(source4Index).toString(), source4->name()); QCOMPARE(model->data(source1Index, Qt::EditRole).toString(), source1->name()); QCOMPARE(model->data(source2Index, Qt::EditRole).toString(), source2->name()); QCOMPARE(model->data(source3Index, Qt::EditRole).toString(), source3->name()); QCOMPARE(model->data(source4Index, Qt::EditRole).toString(), source4->name()); QVERIFY(!model->data(source1Index, Qt::CheckStateRole).isValid()); QCOMPARE(model->data(source2Index, Qt::CheckStateRole).toBool(), source2->isSelected()); QCOMPARE(model->data(source3Index, Qt::CheckStateRole).toBool(), source3->isSelected()); QCOMPARE(model->data(source4Index, Qt::CheckStateRole).toBool(), source4->isSelected()); QCOMPARE(model->data(source1Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), source1->iconName()); QCOMPARE(model->data(source2Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("folder")); QCOMPARE(model->data(source3Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("folder")); QCOMPARE(model->data(source4Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("folder")); QCOMPARE(model->data(source1Index, Presentation::QueryTreeModelBase::IsDefaultRole).toBool(), false); QCOMPARE(model->data(source2Index, Presentation::QueryTreeModelBase::IsDefaultRole).toBool(), true); QCOMPARE(model->data(source3Index, Presentation::QueryTreeModelBase::IsDefaultRole).toBool(), false); QCOMPARE(model->data(source4Index, Presentation::QueryTreeModelBase::IsDefaultRole).toBool(), false); // WHEN sourceRepositoryMock(&Domain::DataSourceRepository::update).when(source2).thenReturn(new FakeJob(this)); sourceRepositoryMock(&Domain::DataSourceRepository::update).when(source4).thenReturn(new FakeJob(this)); QVERIFY(!model->setData(source1Index, Qt::Unchecked, Qt::CheckStateRole)); QVERIFY(model->setData(source2Index, Qt::Checked, Qt::CheckStateRole)); QVERIFY(model->setData(source4Index, Qt::Unchecked, Qt::CheckStateRole)); // THEN QVERIFY(sourceRepositoryMock(&Domain::DataSourceRepository::update).when(source2).exactly(1)); QVERIFY(sourceRepositoryMock(&Domain::DataSourceRepository::update).when(source4).exactly(1)); QVERIFY(source2->isSelected()); QVERIFY(!source4->isSelected()); // WHEN QSignalSpy spy(model, &QAbstractItemModel::dataChanged); sourceQueriesMock(&Domain::DataSourceQueries::changeDefaultSource).when(source4).thenReturn(); sources.setDefaultItem(source4Index); // THEN QCOMPARE(model->data(source1Index, Presentation::QueryTreeModelBase::IsDefaultRole).toBool(), false); QCOMPARE(model->data(source2Index, Presentation::QueryTreeModelBase::IsDefaultRole).toBool(), false); QCOMPARE(model->data(source3Index, Presentation::QueryTreeModelBase::IsDefaultRole).toBool(), false); QCOMPARE(model->data(source4Index, Presentation::QueryTreeModelBase::IsDefaultRole).toBool(), true); // Not overly efficient way of signaling the change, but doesn't happen often QCOMPARE(spy.count(), 4); QCOMPARE(spy.at(0).at(0).toModelIndex(), source1Index); QCOMPARE(spy.at(0).at(1).toModelIndex(), source1Index); QCOMPARE(spy.at(1).at(0).toModelIndex(), source3Index); QCOMPARE(spy.at(1).at(1).toModelIndex(), source3Index); QCOMPARE(spy.at(2).at(0).toModelIndex(), source4Index); QCOMPARE(spy.at(2).at(1).toModelIndex(), source4Index); QCOMPARE(spy.at(3).at(0).toModelIndex(), source2Index); QCOMPARE(spy.at(3).at(1).toModelIndex(), source2Index); QVERIFY(sourceQueriesMock(&Domain::DataSourceQueries::changeDefaultSource).when(source4).exactly(1)); } void shouldGetAnErrorMessageWhenSetDataSourceFailed() { // GIVEN // Two top level sources auto source1 = Domain::DataSource::Ptr::create(); source1->setName(QStringLiteral("Source 1")); source1->setIconName(QStringLiteral("foo-icon")); source1->setSelected(false); source1->setContentTypes(Domain::DataSource::Tasks); auto topLevelProvider = Domain::QueryResultProvider::Ptr::create(); auto topLevelResult = Domain::QueryResult::create(topLevelProvider); topLevelProvider->append(source1); // Nothing under source1 auto source1Provider = Domain::QueryResultProvider::Ptr::create(); auto source1Result = Domain::QueryResult::create(source1Provider); Utils::MockObject sourceQueriesMock; sourceQueriesMock(&Domain::DataSourceQueries::findTopLevel).when().thenReturn(topLevelResult); sourceQueriesMock(&Domain::DataSourceQueries::findChildren).when(source1).thenReturn(source1Result); Utils::MockObject sourceRepositoryMock; Presentation::AvailableSourcesModel sources(sourceQueriesMock.getInstance(), sourceRepositoryMock.getInstance(), - Q_NULLPTR); + nullptr); FakeErrorHandler errorHandler; sources.setErrorHandler(&errorHandler); // WHEN QAbstractItemModel *model = sources.sourceListModel(); // THEN const QModelIndex source1Index = model->index(0, 0); // WHEN auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); sourceRepositoryMock(&Domain::DataSourceRepository::update).when(source1).thenReturn(job); QVERIFY(model->setData(source1Index, Qt::Unchecked, Qt::CheckStateRole)); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot modify source Source 1: Foo")); } void shouldExecBackendSettingsDialog() { // GIVEN Utils::MockObject sourceRepositoryMock; sourceRepositoryMock(&Domain::DataSourceRepository::showConfigDialog).when().thenReturn(); Presentation::AvailableSourcesModel sources(Domain::DataSourceQueries::Ptr(), sourceRepositoryMock.getInstance()); // WHEN sources.showConfigDialog(); // THEN QVERIFY(sourceRepositoryMock(&Domain::DataSourceRepository::showConfigDialog).when().exactly(1)); } }; ZANSHIN_TEST_MAIN(AvailableSourcesModelTest) #include "availablesourcesmodeltest.moc" diff --git a/tests/units/presentation/editormodeltest.cpp b/tests/units/presentation/editormodeltest.cpp index 59d58724..863b0f6e 100644 --- a/tests/units/presentation/editormodeltest.cpp +++ b/tests/units/presentation/editormodeltest.cpp @@ -1,529 +1,529 @@ /* 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 #include #include "testlib/fakejob.h" #include "domain/task.h" #include "presentation/editormodel.h" #include "presentation/errorhandler.h" using namespace mockitopp; class FakeErrorHandler : public Presentation::ErrorHandler { public: void doDisplayMessage(const QString &message) override { m_message = message; } QString m_message; }; class EditorModelTest : public QObject { Q_OBJECT public: - explicit EditorModelTest(QObject *parent = Q_NULLPTR) + explicit EditorModelTest(QObject *parent = nullptr) : QObject(parent) { qRegisterMetaType(); Presentation::EditorModel::setAutoSaveDelay(50); } private slots: void shouldHaveEmptyDefaultState() { // GIVEN Presentation::EditorModel model; // WHEN // Nothing // THEN QVERIFY(model.task().isNull()); QVERIFY(model.text().isEmpty()); QVERIFY(model.title().isEmpty()); QVERIFY(!model.isDone()); QVERIFY(model.startDate().isNull()); QVERIFY(model.dueDate().isNull()); QCOMPARE(model.recurrence(), Domain::Task::NoRecurrence); QVERIFY(model.attachmentModel() != nullptr); QVERIFY(!model.hasSaveFunction()); auto am = model.attachmentModel(); QCOMPARE(am->rowCount(), 0); } void shouldHaveTaskProperties() { // GIVEN Presentation::EditorModel model; QSignalSpy textSpy(&model, &Presentation::EditorModel::textChanged); QSignalSpy titleSpy(&model, &Presentation::EditorModel::titleChanged); QSignalSpy doneSpy(&model, &Presentation::EditorModel::doneChanged); QSignalSpy startSpy(&model, &Presentation::EditorModel::startDateChanged); QSignalSpy dueSpy(&model, &Presentation::EditorModel::dueDateChanged); QSignalSpy recurrenceSpy(&model, &Presentation::EditorModel::recurrenceChanged); QSignalSpy attachmentSpy(model.attachmentModel(), &QAbstractItemModel::modelReset); Domain::Task::Attachments attachments; Domain::Task::Attachment dataAttachment; dataAttachment.setData("foo"); dataAttachment.setLabel("dataAttachment"); dataAttachment.setMimeType("text/plain"); dataAttachment.setIconName("text-plain"); attachments.append(dataAttachment); Domain::Task::Attachment uriAttachment; uriAttachment.setUri(QUrl("https://www.kde.org")); uriAttachment.setLabel("uriAttachment"); uriAttachment.setMimeType("text/html"); uriAttachment.setIconName("text-html"); attachments.append(uriAttachment); auto task = Domain::Task::Ptr::create(); task->setText(QStringLiteral("description")); task->setTitle(QStringLiteral("title")); task->setDone(true); task->setStartDate(QDate::currentDate()); task->setDueDate(QDate::currentDate().addDays(2)); task->setRecurrence(Domain::Task::RecursDaily); task->setAttachments(attachments); // WHEN model.setTask(task); // To make sure we don't signal too much model.setText(task->text()); model.setTitle(task->title()); model.setDone(task->isDone()); model.setStartDate(task->startDate()); model.setDueDate(task->dueDate()); model.setRecurrence(task->recurrence()); // THEN QCOMPARE(textSpy.size(), 1); QCOMPARE(textSpy.takeFirst().at(0).toString(), task->text()); QCOMPARE(model.property("text").toString(), task->text()); QCOMPARE(titleSpy.size(), 1); QCOMPARE(titleSpy.takeFirst().at(0).toString(), task->title()); QCOMPARE(model.property("title").toString(), task->title()); QCOMPARE(doneSpy.size(), 1); QCOMPARE(doneSpy.takeFirst().at(0).toBool(), task->isDone()); QCOMPARE(model.property("done").toBool(), task->isDone()); QCOMPARE(startSpy.size(), 1); QCOMPARE(startSpy.takeFirst().at(0).toDate(), task->startDate()); QCOMPARE(model.property("startDate").toDate(), task->startDate()); QCOMPARE(dueSpy.size(), 1); QCOMPARE(dueSpy.takeFirst().at(0).toDate(), task->dueDate()); QCOMPARE(model.property("dueDate").toDate(), task->dueDate()); QCOMPARE(recurrenceSpy.size(), 1); QCOMPARE(recurrenceSpy.takeFirst().at(0).value(), task->recurrence()); QCOMPARE(model.property("recurrence").value(), task->recurrence()); QCOMPARE(attachmentSpy.size(), 1); auto am = model.attachmentModel(); QCOMPARE(am->rowCount(), 2); QCOMPARE(am->data(am->index(0, 0), Qt::DisplayRole).toString(), QStringLiteral("dataAttachment")); QCOMPARE(am->data(am->index(0, 0), Qt::DecorationRole).value(), QIcon::fromTheme("text-plain")); QCOMPARE(am->data(am->index(1, 0), Qt::DisplayRole).toString(), QStringLiteral("uriAttachment")); QCOMPARE(am->data(am->index(1, 0), Qt::DecorationRole).value(), QIcon::fromTheme("text-html")); } void shouldReactToTaskPropertyChanges_data() { QTest::addColumn("task"); QTest::addColumn("propertyName"); QTest::addColumn("propertyValue"); QTest::addColumn("signal"); QTest::newRow("task text") << Domain::Task::Ptr(Domain::Task::Ptr::create()) << QByteArray("text") << QVariant("new text") << QByteArray(SIGNAL(textChanged(QString))); QTest::newRow("task title") << Domain::Task::Ptr(Domain::Task::Ptr::create()) << QByteArray("title") << QVariant("new title") << QByteArray(SIGNAL(titleChanged(QString))); QTest::newRow("task done") << Domain::Task::Ptr(Domain::Task::Ptr::create()) << QByteArray("done") << QVariant(true) << QByteArray(SIGNAL(doneChanged(bool))); QTest::newRow("task start") << Domain::Task::Ptr(Domain::Task::Ptr::create()) << QByteArray("startDate") << QVariant(QDate::currentDate()) << QByteArray(SIGNAL(startDateChanged(QDate))); QTest::newRow("task due") << Domain::Task::Ptr(Domain::Task::Ptr::create()) << QByteArray("dueDate") << QVariant(QDate::currentDate().addDays(2)) << QByteArray(SIGNAL(dueDateChanged(QDate))); QTest::newRow("task recurrence") << Domain::Task::Ptr(Domain::Task::Ptr::create()) << QByteArray("recurrence") << QVariant::fromValue(Domain::Task::RecursDaily) << QByteArray(SIGNAL(recurrenceChanged(Domain::Task::Recurrence))); } void shouldReactToTaskPropertyChanges() { // GIVEN QFETCH(Domain::Task::Ptr, task); QFETCH(QByteArray, propertyName); QFETCH(QVariant, propertyValue); QFETCH(QByteArray, signal); Presentation::EditorModel model; model.setTask(task); QSignalSpy spy(&model, signal.constData()); // WHEN task->setProperty(propertyName, propertyValue); // THEN QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0), propertyValue); QCOMPARE(model.property(propertyName), propertyValue); } void shouldNotReactToTaskPropertyChangesWhenEditing_data() { shouldReactToTaskPropertyChanges_data(); } void shouldNotReactToTaskPropertyChangesWhenEditing() { // GIVEN QFETCH(Domain::Task::Ptr, task); QFETCH(QByteArray, propertyName); QFETCH(QVariant, propertyValue); QFETCH(QByteArray, signal); Presentation::EditorModel model; model.setTask(task); QSignalSpy spy(&model, signal.constData()); // WHEN const auto oldPropertyValue = task->property(propertyName); model.setEditingInProgress(true); task->setProperty(propertyName, propertyValue); // THEN QVERIFY(spy.isEmpty()); QCOMPARE(model.property(propertyName), oldPropertyValue); } void shouldApplyChangesBackToTaskAfterADelay_data() { shouldReactToTaskPropertyChanges_data(); } void shouldApplyChangesBackToTaskAfterADelay() { // GIVEN QFETCH(Domain::Task::Ptr, task); QFETCH(QByteArray, propertyName); QFETCH(QVariant, propertyValue); QFETCH(QByteArray, signal); auto savedTask = Domain::Task::Ptr(); auto save = [this, &savedTask] (const Domain::Task::Ptr &task) { savedTask = task; return new FakeJob(this); }; Presentation::EditorModel model; model.setSaveFunction(save); model.setTask(task); QSignalSpy spy(&model, signal.constData()); // WHEN model.setProperty(propertyName, propertyValue); // THEN QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0), propertyValue); QCOMPARE(model.property(propertyName), propertyValue); QVERIFY(task->property(propertyName) != propertyValue); QVERIFY(!savedTask); // WHEN (apply after delay) QTest::qWait(model.autoSaveDelay() + 50); // THEN QCOMPARE(savedTask, task); QCOMPARE(task->property(propertyName), propertyValue); } void shouldApplyChangesImmediatelyIfANewTaskIsSet_data() { shouldReactToTaskPropertyChanges_data(); } void shouldApplyChangesImmediatelyIfANewTaskIsSet() { // GIVEN QFETCH(Domain::Task::Ptr, task); QFETCH(QByteArray, propertyName); QFETCH(QVariant, propertyValue); QFETCH(QByteArray, signal); auto savedTask = Domain::Task::Ptr(); auto save = [this, &savedTask] (const Domain::Task::Ptr &task) { savedTask = task; return new FakeJob(this); }; Presentation::EditorModel model; model.setSaveFunction(save); QVERIFY(model.hasSaveFunction()); model.setTask(task); QSignalSpy spy(&model, signal.constData()); // WHEN model.setProperty(propertyName, propertyValue); // THEN QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0), propertyValue); QCOMPARE(model.property(propertyName), propertyValue); QVERIFY(task->property(propertyName) != propertyValue); QVERIFY(!savedTask); // WHEN (apply immediately) model.setTask(Domain::Task::Ptr::create()); // THEN QCOMPARE(savedTask, task); QCOMPARE(task->property(propertyName), propertyValue); savedTask.clear(); // WHEN (nothing else happens after a delay) QTest::qWait(model.autoSaveDelay() + 50); // THEN QVERIFY(!savedTask); QCOMPARE(task->property(propertyName), propertyValue); } void shouldApplyChangesImmediatelyIfDeleted_data() { shouldReactToTaskPropertyChanges_data(); } void shouldApplyChangesImmediatelyIfDeleted() { // GIVEN QFETCH(Domain::Task::Ptr, task); QFETCH(QByteArray, propertyName); QFETCH(QVariant, propertyValue); QFETCH(QByteArray, signal); auto savedTask = Domain::Task::Ptr(); auto save = [this, &savedTask] (const Domain::Task::Ptr &task) { savedTask = task; return new FakeJob(this); }; auto model = new Presentation::EditorModel; model->setSaveFunction(save); QVERIFY(model->hasSaveFunction()); model->setTask(task); QSignalSpy spy(model, signal.constData()); // WHEN model->setProperty(propertyName, propertyValue); // THEN QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0), propertyValue); QCOMPARE(model->property(propertyName), propertyValue); QVERIFY(task->property(propertyName) != propertyValue); QVERIFY(!savedTask); // WHEN (apply immediately) delete model; // THEN QCOMPARE(savedTask, task); QCOMPARE(task->property(propertyName), propertyValue); } void shouldGetAnErrorMessageWhenSaveFailed() { // GIVEN auto task = Domain::Task::Ptr::create(); task->setTitle(QStringLiteral("Task 1")); auto savedTask = Domain::Task::Ptr(); auto save = [this, &savedTask] (const Domain::Task::Ptr &task) { savedTask = task; auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); return job; }; auto model = new Presentation::EditorModel; model->setSaveFunction(save); QVERIFY(model->hasSaveFunction()); FakeErrorHandler errorHandler; model->setErrorHandler(&errorHandler); model->setTask(task); // WHEN model->setProperty("title", "Foo"); delete model; // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot modify task Task 1: Foo")); } void shouldDisconnectFromPreviousTask_data() { shouldReactToTaskPropertyChanges_data(); } void shouldDisconnectFromPreviousTask() { // GIVEN QFETCH(Domain::Task::Ptr, task); QFETCH(QByteArray, propertyName); QFETCH(QVariant, propertyValue); QFETCH(QByteArray, signal); Presentation::EditorModel model; model.setTask(task); QSignalSpy spy(&model, signal.constData()); Domain::Task::Ptr newTask = Domain::Task::Ptr::create(); // WHEN model.setTask(newTask); // modifying the *old* task should have no effect. task->setProperty(propertyName, propertyValue); // THEN QCOMPARE(spy.size(), 1); // emitted by setTask QVERIFY(model.property(propertyName) != task->property(propertyName)); } void shouldAddAttachments() { // GIVEN QTemporaryFile temporaryFile(QDir::tempPath() + "/taskeditormodeltest_XXXXXX.txt"); temporaryFile.open(); temporaryFile.write("foo bar"); temporaryFile.close(); auto fileName = temporaryFile.fileName().mid(QDir::tempPath().size() + 1); auto task = Domain::Task::Ptr::create(); auto savedTask = Domain::Task::Ptr(); auto save = [this, &savedTask] (const Domain::Task::Ptr &task) { savedTask = task; return new FakeJob(this); }; Presentation::EditorModel model; model.setSaveFunction(save); model.setTask(task); QSignalSpy spy(model.attachmentModel(), &QAbstractItemModel::modelReset); // WHEN model.addAttachment(temporaryFile.fileName()); // THEN QCOMPARE(spy.size(), 1); QCOMPARE(model.attachmentModel()->rowCount(), 1); QVERIFY(!savedTask); // WHEN (nothing else happens after a delay) QTest::qWait(model.autoSaveDelay() + 50); // THEN QCOMPARE(savedTask, task); QCOMPARE(task->attachments().size(), 1); QCOMPARE(task->attachments().first().label(), fileName); QCOMPARE(task->attachments().first().mimeType(), QStringLiteral("text/plain")); QCOMPARE(task->attachments().first().iconName(), QStringLiteral("text-plain")); QCOMPARE(task->attachments().first().data(), QByteArrayLiteral("foo bar")); } void shouldRemoveAttachments() { // GIVEN auto task = Domain::Task::Ptr::create(); task->setAttachments(Domain::Task::Attachments() << Domain::Task::Attachment("foo") << Domain::Task::Attachment("bar")); auto savedTask = Domain::Task::Ptr(); auto save = [this, &savedTask] (const Domain::Task::Ptr &task) { savedTask = task; return new FakeJob(this); }; Presentation::EditorModel model; model.setSaveFunction(save); model.setTask(task); QSignalSpy spy(model.attachmentModel(), &QAbstractItemModel::modelReset); // WHEN model.removeAttachment(model.attachmentModel()->index(0, 0)); // THEN QCOMPARE(spy.size(), 1); QCOMPARE(model.attachmentModel()->rowCount(), 1); QVERIFY(!savedTask); // WHEN (nothing else happens after a delay) QTest::qWait(model.autoSaveDelay() + 50); // THEN QCOMPARE(savedTask, task); QCOMPARE(task->attachments().size(), 1); QCOMPARE(task->attachments().first().data(), QByteArrayLiteral("bar")); } }; ZANSHIN_TEST_MAIN(EditorModelTest) #include "editormodeltest.moc" diff --git a/tests/units/presentation/pagemodeltest.cpp b/tests/units/presentation/pagemodeltest.cpp index 8af07537..f949ac2c 100644 --- a/tests/units/presentation/pagemodeltest.cpp +++ b/tests/units/presentation/pagemodeltest.cpp @@ -1,87 +1,87 @@ /* 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 #include "presentation/pagemodel.h" class FakePageModel : public Presentation::PageModel { Q_OBJECT public: - explicit FakePageModel(QObject *parent = Q_NULLPTR) + explicit FakePageModel(QObject *parent = nullptr) : Presentation::PageModel(parent), createCount(0), - itemModel(Q_NULLPTR) + itemModel(nullptr) { } Domain::Task::Ptr addItem(const QString &, const QModelIndex &) override { return Domain::Task::Ptr::create(); } void removeItem(const QModelIndex &) override {} void promoteItem(const QModelIndex &) override {} private: QAbstractItemModel *createCentralListModel() override { createCount++; itemModel = new QStringListModel(this); return itemModel; } public: int createCount; QAbstractItemModel *itemModel; }; class PageModelTest : public QObject { Q_OBJECT private slots: void shouldLazilyCreateModelOnlyOnce() { // GIVEN FakePageModel model; QCOMPARE(model.createCount, 0); QVERIFY(!model.itemModel); // WHEN QAbstractItemModel *itemModel = model.centralListModel(); // THEN QCOMPARE(model.createCount, 1); QCOMPARE(itemModel, model.itemModel); // WHEN itemModel = model.centralListModel(); // THEN QCOMPARE(model.createCount, 1); QCOMPARE(itemModel, model.itemModel); } }; ZANSHIN_TEST_MAIN(PageModelTest) #include "pagemodeltest.moc" diff --git a/tests/units/presentation/querytreemodeltest.cpp b/tests/units/presentation/querytreemodeltest.cpp index 95d28c29..2dd42404 100644 --- a/tests/units/presentation/querytreemodeltest.cpp +++ b/tests/units/presentation/querytreemodeltest.cpp @@ -1,927 +1,927 @@ /* This file is part of Zanshin Copyright 2014 Mario Bensi 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 #include #include #include "utils/mockobject.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" #include "presentation/querytreemodel.h" #include "testlib/modeltest.h" using namespace mockitopp; Q_DECLARE_METATYPE(QModelIndex) Q_DECLARE_METATYPE(QList) class QueryTreeModelTest : public QObject { Q_OBJECT public: - explicit QueryTreeModelTest(QObject *parent = Q_NULLPTR) + explicit QueryTreeModelTest(QObject *parent = nullptr) : QObject(parent) { qRegisterMetaType(); } private: Domain::Task::List createTasks() const { Domain::Task::List result; const QStringList titles = {"first", "second", "third"}; const QList doneStates = {true, false, false}; Q_ASSERT(titles.size() == doneStates.size()); result.reserve(titles.size()); for (int i = 0; i < titles.size(); i++) { auto task = Domain::Task::Ptr::create(); task->setTitle(titles.at(i)); task->setDone(doneStates.at(i)); result << task; } return result; } Domain::Task::List createChildrenTasks() const { Domain::Task::List result; const QStringList titles = {"childFirst", "childSecond", "childThird"}; const QList doneStates = {true, false, false}; Q_ASSERT(titles.size() == doneStates.size()); result.reserve(titles.size()); for (int i = 0; i < titles.size(); i++) { auto task = Domain::Task::Ptr::create(); task->setTitle(titles.at(i)); task->setDone(doneStates.at(i)); result << task; } return result; } static QVariant standardDataFunction(const Domain::Task::Ptr &task, int role, int) { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; } private slots: void shouldHaveRoleNames() { // GIVEN auto queryGenerator = [](const QColor &) { return Domain::QueryResult::Ptr(); }; auto flagsFunction = [](const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [](const QColor &, int, int) { return QVariant(); }; auto setDataFunction = [](const QColor &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction); // WHEN auto roles = model.roleNames(); // THEN QCOMPARE(roles.value(Qt::DisplayRole), QByteArray("display")); QCOMPARE(roles.value(Presentation::QueryTreeModelBase::ObjectRole), QByteArray("object")); QCOMPARE(roles.value(Presentation::QueryTreeModelBase::IconNameRole), QByteArray("icon")); QCOMPARE(roles.value(Presentation::QueryTreeModelBase::IsDefaultRole), QByteArray("default")); } void shouldListTasks() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); // WHEN auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; - Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, Q_NULLPTR); + Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); new ModelTest(&model, this); // THEN QCOMPARE(model.rowCount(), 3); QCOMPARE(model.rowCount(model.index(0, 0)), 3); QCOMPARE(model.rowCount(model.index(1, 0)), 0); QCOMPARE(model.rowCount(model.index(2, 0)), 0); QCOMPARE(model.rowCount(model.index(0, 0, model.index(0, 0))), 0); QCOMPARE(model.rowCount(model.index(1, 0, model.index(0, 0))), 0); QCOMPARE(model.rowCount(model.index(2, 0, model.index(0, 0))), 0); QCOMPARE(model.rowCount(model.index(3, 0, model.index(0, 0))), 3); for (int i = 0; i < tasks.size(); i++) { auto task = tasks.at(i); auto index = model.index(i, 0); QCOMPARE(model.data(index), model.data(index, Qt::DisplayRole)); QCOMPARE(model.data(index).toString(), task->title()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Checked, task->isDone()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked, !task->isDone()); } for (int i = 0; i < childrenTasks.size(); i++) { auto task = childrenTasks.at(i); auto index = model.index(i, 0, model.index(0, 0)); QCOMPARE(model.data(index), model.data(index, Qt::DisplayRole)); QCOMPARE(model.data(index).toString(), task->title()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Checked, task->isDone()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked, !task->isDone()); } } void shouldDealWithNullQueriesProperly() { // GIVEN auto queryGenerator = [](const QString &) { return Domain::QueryResult::Ptr(); }; auto flagsFunction = [](const QString &) { return Qt::NoItemFlags; }; auto dataFunction = [](const QString &, int, int) { return QVariant(); }; auto setDataFunction = [](const QString &, const QVariant &, int) { return false; }; // WHEN - Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); + Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, nullptr); new ModelTest(&model, this); // THEN QCOMPARE(model.rowCount(), 0); } void shouldReactToTaskAdd() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(tasks.at(1)); provider->append(tasks.at(2)); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; - Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, Q_NULLPTR); + Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); new ModelTest(&model, this); QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); // WHEN queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); provider->insert(0, tasks.at(0)); // THEN QCOMPARE(aboutToBeInsertedSpy.size(), 1); QCOMPARE(aboutToBeInsertedSpy.first().at(0).toModelIndex(), QModelIndex()); QCOMPARE(aboutToBeInsertedSpy.first().at(1).toInt(), 0); QCOMPARE(aboutToBeInsertedSpy.first().at(2).toInt(), 0); QCOMPARE(insertedSpy.size(), 1); QCOMPARE(insertedSpy.first().at(0).toModelIndex(), QModelIndex()); QCOMPARE(insertedSpy.first().at(1).toInt(), 0); QCOMPARE(insertedSpy.first().at(2).toInt(), 0); } void shouldReactToChilrenTaskAdd() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); childrenProvider->append(childrenTasks.at(0)); childrenProvider->append(childrenTasks.at(1)); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; - Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, Q_NULLPTR); + Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); new ModelTest(&model, this); QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); // WHEN queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); childrenProvider->insert(1, tasks.at(2)); // THEN QCOMPARE(aboutToBeInsertedSpy.size(), 1); QCOMPARE(aboutToBeInsertedSpy.first().at(0).toModelIndex(), model.index(0, 0)); QCOMPARE(aboutToBeInsertedSpy.first().at(1).toInt(), 1); QCOMPARE(aboutToBeInsertedSpy.first().at(2).toInt(), 1); QCOMPARE(insertedSpy.size(), 1); QCOMPARE(insertedSpy.first().at(0).toModelIndex(), model.index(0, 0)); QCOMPARE(insertedSpy.first().at(1).toInt(), 1); QCOMPARE(insertedSpy.first().at(2).toInt(), 1); } void shouldReactToTaskRemove() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; - Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, Q_NULLPTR); + Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); new ModelTest(&model, this); QSignalSpy aboutToBeRemovedSpy(&model, &QAbstractItemModel::rowsAboutToBeRemoved); QSignalSpy removedSpy(&model, &QAbstractItemModel::rowsRemoved); QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); QModelIndex removeIndex = model.index(0, 0); // WHEN // Remove children childrenProvider->removeAt(0); childrenProvider->removeAt(0); childrenProvider->removeAt(0); // Move children to Top Level provider->append(childrenTasks.at(0)); provider->append(childrenTasks.at(1)); provider->append(childrenTasks.at(2)); // Remove firt element from topLevel provider->removeAt(0); // THEN QCOMPARE(aboutToBeRemovedSpy.size(), 4); QCOMPARE(removedSpy.size(), 4); for (int i = 0; i < aboutToBeRemovedSpy.size(); i++) { if (i != 3) QCOMPARE(aboutToBeRemovedSpy.at(i).at(0).toModelIndex(), removeIndex); else QCOMPARE(aboutToBeRemovedSpy.at(i).at(0).toModelIndex(), QModelIndex()); QCOMPARE(aboutToBeRemovedSpy.at(i).at(1).toInt(), 0); QCOMPARE(aboutToBeRemovedSpy.at(i).at(2).toInt(), 0); if (i != 3) QCOMPARE(removedSpy.at(i).at(0).toModelIndex(), removeIndex); else QCOMPARE(removedSpy.at(i).at(0).toModelIndex(), QModelIndex()); QCOMPARE(removedSpy.at(i).at(1).toInt(), 0); QCOMPARE(removedSpy.at(i).at(2).toInt(), 0); } QCOMPARE(aboutToBeInsertedSpy.size(), 3); QCOMPARE(insertedSpy.size(), 3); for (int i = 0; i < aboutToBeInsertedSpy.size(); i++) { QCOMPARE(aboutToBeInsertedSpy.at(i).at(0).toModelIndex(), QModelIndex()); QCOMPARE(aboutToBeInsertedSpy.at(i).at(1).toInt(), i + 3); QCOMPARE(aboutToBeInsertedSpy.at(i).at(2).toInt(), i + 3); QCOMPARE(insertedSpy.at(i).at(0).toModelIndex(), QModelIndex()); QCOMPARE(insertedSpy.at(i).at(1).toInt(), i + 3); QCOMPARE(insertedSpy.at(i).at(1).toInt(), i + 3); } } void shouldReactToTaskChange() { // GIVEN // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); // WHEN auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; - Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, Q_NULLPTR); + Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); new ModelTest(&model, this); QSignalSpy dataChangedSpy(&model, &QAbstractItemModel::dataChanged); // WHEN tasks.at(2)->setDone(true); childrenTasks.at(2)->setDone(true); provider->replace(2, tasks.at(2)); childrenProvider->replace(2, tasks.at(2)); // THEN QCOMPARE(dataChangedSpy.size(), 2); QCOMPARE(dataChangedSpy.first().at(0).toModelIndex(), model.index(2, 0)); QCOMPARE(dataChangedSpy.first().at(1).toModelIndex(), model.index(2, 0)); QCOMPARE(dataChangedSpy.last().at(0).toModelIndex(), model.index(2, 0, model.index(0, 0))); QCOMPARE(dataChangedSpy.last().at(1).toModelIndex(), model.index(2, 0, model.index(0, 0))); } void shouldAllowEditsAndChecks() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); // WHEN auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; - Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, Q_NULLPTR); + Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); new ModelTest(&model, this); // WHEN // Nothing particular // THEN for (int row = 0; row < tasks.size(); row++) { QVERIFY(model.flags(model.index(row, 0)) & Qt::ItemIsEditable); QVERIFY(model.flags(model.index(row, 0)) & Qt::ItemIsUserCheckable); } for (int row = 0; row < childrenTasks.size(); row++) { QVERIFY(model.flags(model.index(row, 0, model.index(0, 0))) & Qt::ItemIsEditable); QVERIFY(model.flags(model.index(row, 0, model.index(0, 0))) & Qt::ItemIsUserCheckable); } } void shouldSaveChanges() { // GIVEN auto tasks = createTasks(); const int taskPos = 1; const auto task = tasks[taskPos]; auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); Utils::MockObject repositoryMock; - repositoryMock(&Domain::TaskRepository::update).when(task).thenReturn(Q_NULLPTR); + repositoryMock(&Domain::TaskRepository::update).when(task).thenReturn(nullptr); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto setDataFunction = [&](const Domain::Task::Ptr &task, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) { return false; } if (role == Qt::EditRole) { task->setTitle(value.toString()); } else { task->setDone(value.toInt() == Qt::Checked); } repositoryMock.getInstance()->update(task); return true; }; - Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, Q_NULLPTR); + Presentation::QueryTreeModel model(queryGenerator, flagsFunction, standardDataFunction, setDataFunction, nullptr); new ModelTest(&model, this); QSignalSpy titleChangedSpy(task.data(), &Domain::Task::titleChanged); QSignalSpy doneChangedSpy(task.data(), &Domain::Task::doneChanged); // WHEN const auto index = model.index(taskPos, 0); model.setData(index, "alternate second"); model.setData(index, Qt::Checked, Qt::CheckStateRole); // THEN QVERIFY(repositoryMock(&Domain::TaskRepository::update).when(task).exactly(2)); QCOMPARE(titleChangedSpy.size(), 1); QCOMPARE(titleChangedSpy.first().first().toString(), QStringLiteral("alternate second")); QCOMPARE(doneChangedSpy.size(), 1); QCOMPARE(doneChangedSpy.first().first().toBool(), true); } void shouldProvideUnderlyingObject() { // GIVEN auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(Qt::red); provider->append(Qt::green); provider->append(Qt::blue); auto queryGenerator = [&](const QColor &color) { if (!color.isValid()) return Domain::QueryResult::create(provider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [](const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [](const QColor &, int, int) { return QVariant(); }; auto setDataFunction = [](const QColor &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction); new ModelTest(&model, this); // WHEN const QModelIndex index = model.index(1, 0); const QVariant data = index.data(Presentation::QueryTreeModelBase::ObjectRole); // THEN QVERIFY(data.isValid()); QCOMPARE(data.value(), provider->data().at(1)); } void shouldProvideUnderlyingTask() { // GIVEN auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, createTasks()) provider->append(task); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::NoItemFlags; }; auto dataFunction = [](const Domain::Task::Ptr &, int, int) { return QVariant(); }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction); new ModelTest(&model, this); // WHEN const QModelIndex index = model.index(1, 0); const QVariant data = index.data(Presentation::QueryTreeModelBase::ObjectRole); // THEN QVERIFY(data.isValid()); QVERIFY(!data.value().isNull()); QCOMPARE(data.value(), provider->data().at(1)); } void shouldMoveOnlyDuringDragAndDrop() { // GIVEN auto queryGenerator = [&] (const QColor &) { return Domain::QueryResult::Ptr(); }; auto flagsFunction = [] (const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [] (const QColor &, int, int) { return QVariant(); }; auto setDataFunction = [] (const QColor &, const QVariant &, int) { return false; }; auto dropFunction = [] (const QMimeData *, Qt::DropAction, const QColor &) { return false; }; auto dragFunction = [] (const QList &) { - return Q_NULLPTR; + return nullptr; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction, dragFunction, nullptr); // THEN QCOMPARE(model.supportedDragActions(), Qt::MoveAction); QCOMPARE(model.supportedDropActions(), Qt::MoveAction); } void shouldCreateMimeData() { // GIVEN auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(Qt::red); provider->append(Qt::green); provider->append(Qt::blue); auto queryGenerator = [&] (const QColor &color) { if (!color.isValid()) return Domain::QueryResult::create(provider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [] (const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [] (const QColor &, int, int) { return QVariant(); }; auto setDataFunction = [] (const QColor &, const QVariant &, int) { return false; }; auto dropFunction = [] (const QMimeData *, Qt::DropAction, const QColor &) { return false; }; auto dragFunction = [] (const QList &colors) { auto mimeData = new QMimeData; mimeData->setColorData(QVariant::fromValue(colors)); return mimeData; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction, dragFunction, nullptr); new ModelTest(&model, this); // WHEN auto data = std::unique_ptr(model.mimeData(QList() << model.index(1, 0) << model.index(2, 0))); // THEN QVERIFY(data.get()); QVERIFY(model.mimeTypes().contains(QStringLiteral("application/x-zanshin-object"))); QList colors; colors << Qt::green << Qt::blue; QCOMPARE(data->colorData().value>(), colors); } void shouldDropMimeData_data() { QTest::addColumn("row"); QTest::addColumn("column"); QTest::addColumn("parentRow"); QTest::addColumn("callExpected"); QTest::newRow("drop on object") << -1 << -1 << 2 << true; QTest::newRow("drop between object") << 1 << 0 << -1 << true; QTest::newRow("drop in empty area") << -1 << -1 << -1 << true; } void shouldDropMimeData() { // GIVEN QFETCH(int, row); QFETCH(int, column); QFETCH(int, parentRow); QFETCH(bool, callExpected); bool dropCalled = false; - const QMimeData *droppedData = Q_NULLPTR; + const QMimeData *droppedData = nullptr; QColor colorSeen; auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(Qt::red); provider->append(Qt::green); provider->append(Qt::blue); auto queryGenerator = [&] (const QColor &color) { if (!color.isValid()) return Domain::QueryResult::create(provider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [] (const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [] (const QColor &, int, int) { return QVariant(); }; auto setDataFunction = [] (const QColor &, const QVariant &, int) { return false; }; auto dropFunction = [&] (const QMimeData *data, Qt::DropAction, const QColor &color) { dropCalled = true; droppedData = data; colorSeen = color; return false; }; auto dragFunction = [] (const QList &) -> QMimeData* { - return Q_NULLPTR; + return nullptr; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction, dragFunction, nullptr); new ModelTest(&model, this); // WHEN auto data = std::make_unique(); const QModelIndex parent = parentRow >= 0 ? model.index(parentRow, 0) : QModelIndex(); model.dropMimeData(data.get(), Qt::MoveAction, row, column, parent); // THEN QCOMPARE(dropCalled, callExpected); if (callExpected) { QCOMPARE(droppedData, data.get()); QCOMPARE(colorSeen, parent.data(Presentation::QueryTreeModelBase::ObjectRole).value()); } } void shouldPreventCyclesByDragAndDrop() { // GIVEN bool dropCalled = false; auto topProvider = Domain::QueryResultProvider::Ptr::create(); topProvider->append(QStringLiteral("1")); topProvider->append(QStringLiteral("2")); topProvider->append(QStringLiteral("3")); auto firstLevelProvider = Domain::QueryResultProvider::Ptr::create(); firstLevelProvider->append(QStringLiteral("2.1")); firstLevelProvider->append(QStringLiteral("2.2")); firstLevelProvider->append(QStringLiteral("2.3")); auto secondLevelProvider = Domain::QueryResultProvider::Ptr::create(); secondLevelProvider->append(QStringLiteral("2.1.1")); secondLevelProvider->append(QStringLiteral("2.1.2")); secondLevelProvider->append(QStringLiteral("2.1.3")); auto queryGenerator = [&] (const QString &string) { if (string.isEmpty()) return Domain::QueryResult::create(topProvider); else if (string == QLatin1String("2")) return Domain::QueryResult::create(firstLevelProvider); else if (string == QLatin1String("2.1")) return Domain::QueryResult::create(secondLevelProvider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [] (const QString &) { return Qt::NoItemFlags; }; auto dataFunction = [] (const QString &, int, int) { return QVariant(); }; auto setDataFunction = [] (const QString &, const QVariant &, int) { return false; }; auto dropFunction = [&] (const QMimeData *, Qt::DropAction, const QString &) { dropCalled = true; return false; }; auto dragFunction = [] (const QStringList &strings) -> QMimeData* { auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(strings)); return data; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction, dragFunction, nullptr); new ModelTest(&model, this); const auto indexes = QModelIndexList() << model.index(0, 0) << model.index(1, 0) << model.index(1, 0, model.index(1, 0)); // WHEN auto data = std::unique_ptr(model.mimeData(indexes)); const auto parent = model.index(1, 0, model.index(0, 0, model.index(1, 0))); model.dropMimeData(data.get(), Qt::MoveAction, -1, -1, parent); // THEN QVERIFY(!dropCalled); } }; ZANSHIN_TEST_MAIN(QueryTreeModelTest) #include "querytreemodeltest.moc" diff --git a/tests/units/testlib/akonadifakedatatest.cpp b/tests/units/testlib/akonadifakedatatest.cpp index 0755024d..c6211d09 100644 --- a/tests/units/testlib/akonadifakedatatest.cpp +++ b/tests/units/testlib/akonadifakedatatest.cpp @@ -1,622 +1,622 @@ /* 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 "testlib/akonadifakedata.h" #include "akonadi/akonadimonitorinterface.h" #include namespace QTest { template inline bool zCompareHelper(bool isOk, const T &left, const T &right, const char *actual, const char *expected, const char *file, int line) { return compare_helper(isOk, isOk ? "COMPARE()" : "Compared values are not the same", toString(left), toString(right), actual, expected, file, line); } // More aggressive compare to make sure we just don't get collections with ids out template <> inline bool qCompare(const Akonadi::Collection &left, const Akonadi::Collection &right, const char *actual, const char *expected, const char *file, int line) { return zCompareHelper((left == right) && (left.displayName() == right.displayName()), left, right, actual, expected, file, line); } // More aggressive compare to make sure we just don't get tags with ids out template <> inline bool qCompare(const Akonadi::Tag &left, const Akonadi::Tag &right, const char *actual, const char *expected, const char *file, int line) { return zCompareHelper((left == right) && (left.name() == right.name()), left, right, actual, expected, file, line); } // More aggressive compare to make sure we just don't get items with ids out template <> inline bool qCompare(const Akonadi::Item &left, const Akonadi::Item &right, const char *actual, const char *expected, const char *file, int line) { return zCompareHelper((left == right) && (left.payloadData() == right.payloadData()), left, right, actual, expected, file, line); } } class AkonadiFakeDataTest : public QObject { Q_OBJECT public: - explicit AkonadiFakeDataTest(QObject *parent = Q_NULLPTR) + explicit AkonadiFakeDataTest(QObject *parent = nullptr) : QObject(parent) { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); } private slots: void shouldBeInitiallyEmpty() { // GIVEN auto data = Testlib::AkonadiFakeData(); // THEN QVERIFY(data.collections().isEmpty()); QVERIFY(data.tags().isEmpty()); QVERIFY(data.items().isEmpty()); } void shouldCreateCollections() { // GIVEN auto data = Testlib::AkonadiFakeData(); QScopedPointer monitor(data.createMonitor()); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::collectionAdded); auto c1 = Akonadi::Collection(42); c1.setName(QStringLiteral("42")); auto c2 = Akonadi::Collection(43); c2.setName(QStringLiteral("43")); const auto colSet = QSet() << c1 << c2; // WHEN data.createCollection(c1); data.createCollection(c2); // THEN QCOMPARE(data.collections().toList().toSet(), colSet); QCOMPARE(data.collection(c1.id()), c1); QCOMPARE(data.collection(c2.id()), c2); QCOMPARE(spy.size(), 2); QCOMPARE(spy.takeFirst().at(0).value(), c1); QCOMPARE(spy.takeFirst().at(0).value(), c2); } void shouldModifyCollections() { // GIVEN auto data = Testlib::AkonadiFakeData(); QScopedPointer monitor(data.createMonitor()); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::collectionChanged); auto c1 = Akonadi::Collection(42); c1.setName(QStringLiteral("42")); data.createCollection(c1); auto c2 = Akonadi::Collection(c1.id()); c2.setName(QStringLiteral("42-bis")); // WHEN data.modifyCollection(c2); // THEN QCOMPARE(data.collections().size(), 1); QCOMPARE(data.collection(c1.id()), c2); QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).value(), c2); } void shouldNotLooseParentCollectionOnModifyCollection() { // GIVEN auto data = Testlib::AkonadiFakeData(); auto root = Akonadi::Collection(42); root.setName(QStringLiteral("root")); data.createCollection(root); auto c1 = Akonadi::Collection(43); c1.setName(QStringLiteral("43")); c1.setParentCollection(Akonadi::Collection(root.id())); data.createCollection(c1); auto c2 = Akonadi::Collection(c1.id()); c2.setName(QStringLiteral("43-bis")); // WHEN data.modifyCollection(c2); // THEN QCOMPARE(data.collections().size(), 2); QCOMPARE(data.collection(c1.id()), c2); QCOMPARE(data.collection(c1.id()).parentCollection().id(), root.id()); } void shouldListChildCollections() { // GIVEN auto data = Testlib::AkonadiFakeData(); auto c1 = Akonadi::Collection(42); c1.setName(QStringLiteral("42")); auto c2 = Akonadi::Collection(43); c2.setName(QStringLiteral("43")); c2.setParentCollection(Akonadi::Collection(42)); const auto colSet = QSet() << c2; // WHEN data.createCollection(c1); data.createCollection(c2); // THEN QVERIFY(data.childCollections(c2.id()).isEmpty()); QCOMPARE(data.childCollections(c1.id()).toList().toSet(), colSet); } void shouldReparentCollectionsOnModify() { // GIVEN auto data = Testlib::AkonadiFakeData(); auto c1 = Akonadi::Collection(42); c1.setName(QStringLiteral("42")); data.createCollection(c1); auto c2 = Akonadi::Collection(43); c2.setName(QStringLiteral("43")); data.createCollection(c2); auto c3 = Akonadi::Collection(44); c3.setParentCollection(Akonadi::Collection(42)); data.createCollection(c3); // WHEN c3.setParentCollection(Akonadi::Collection(43)); data.modifyCollection(c3); // THEN QVERIFY(data.childCollections(c1.id()).isEmpty()); QCOMPARE(data.childCollections(c2.id()).size(), 1); QCOMPARE(data.childCollections(c2.id()).at(0), c3); } void shouldRemoveCollections() { // GIVEN auto data = Testlib::AkonadiFakeData(); QScopedPointer monitor(data.createMonitor()); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::collectionRemoved); auto c1 = Akonadi::Collection(42); c1.setName(QStringLiteral("42")); data.createCollection(c1); auto c2 = Akonadi::Collection(43); c2.setName(QStringLiteral("43")); c2.setParentCollection(Akonadi::Collection(42)); data.createCollection(c2); auto c3 = Akonadi::Collection(44); c3.setName(QStringLiteral("44")); c3.setParentCollection(Akonadi::Collection(43)); data.createCollection(c3); auto i1 = Akonadi::Item(42); i1.setPayloadFromData("42"); i1.setParentCollection(Akonadi::Collection(43)); data.createItem(i1); auto i2 = Akonadi::Item(43); i2.setPayloadFromData("43"); i2.setParentCollection(Akonadi::Collection(44)); data.createItem(i2); // WHEN data.removeCollection(c2); // THEN QCOMPARE(data.collections().size(), 1); QCOMPARE(data.collections().at(0), c1); QVERIFY(!data.collection(c2.id()).isValid()); QVERIFY(!data.collection(c3.id()).isValid()); QVERIFY(data.childCollections(c1.id()).isEmpty()); QVERIFY(data.childCollections(c2.id()).isEmpty()); QVERIFY(data.childCollections(c3.id()).isEmpty()); QVERIFY(data.items().isEmpty()); QVERIFY(!data.item(i1.id()).isValid()); QVERIFY(!data.item(i2.id()).isValid()); QVERIFY(data.childItems(c2.id()).isEmpty()); QVERIFY(data.childItems(c3.id()).isEmpty()); QCOMPARE(spy.size(), 2); QCOMPARE(spy.takeFirst().at(0).value(), c3); QCOMPARE(spy.takeFirst().at(0).value(), c2); } void shouldCreateTags() { // GIVEN auto data = Testlib::AkonadiFakeData(); QScopedPointer monitor(data.createMonitor()); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::tagAdded); auto t1 = Akonadi::Tag(42); t1.setName(QStringLiteral("42")); auto t2 = Akonadi::Tag(43); t2.setName(QStringLiteral("43")); // WHEN data.createTag(t1); data.createTag(t2); // THEN QCOMPARE(data.tags().size(), 2); QVERIFY(data.tags().contains(t1)); QVERIFY(data.tags().contains(t2)); QCOMPARE(data.tag(t1.id()), t1); QCOMPARE(data.tag(t2.id()), t2); QCOMPARE(spy.size(), 2); QCOMPARE(spy.takeFirst().at(0).value(), t1); QCOMPARE(spy.takeFirst().at(0).value(), t2); } void shouldModifyTags() { // GIVEN auto data = Testlib::AkonadiFakeData(); QScopedPointer monitor(data.createMonitor()); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::tagChanged); auto t1 = Akonadi::Tag(42); t1.setName(QStringLiteral("42")); data.createTag(t1); auto t2 = Akonadi::Tag(t1.id()); t2.setName(QStringLiteral("42-bis")); // WHEN data.modifyTag(t2); // THEN QCOMPARE(data.tags().size(), 1); QCOMPARE(data.tag(t1.id()), t2); QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).value(), t2); } void shouldRemoveTags() { // GIVEN auto data = Testlib::AkonadiFakeData(); QScopedPointer monitor(data.createMonitor()); QSignalSpy tagSpy(monitor.data(), &Akonadi::MonitorInterface::tagRemoved); QSignalSpy itemSpy(monitor.data(), &Akonadi::MonitorInterface::itemChanged); auto c1 = Akonadi::Collection(42); data.createCollection(c1); auto t1 = Akonadi::Tag(42); t1.setName(QStringLiteral("42")); data.createTag(t1); auto t2 = Akonadi::Tag(43); t2.setName(QStringLiteral("43")); data.createTag(t2); auto i1 = Akonadi::Item(42); i1.setPayloadFromData("42"); i1.setParentCollection(c1); i1.setTag(Akonadi::Tag(t1.id())); data.createItem(i1); auto i2 = Akonadi::Item(43); i2.setPayloadFromData("43"); i2.setParentCollection(c1); i2.setTag(Akonadi::Tag(t2.id())); data.createItem(i2); const auto itemSet = QSet() << i1 << i2; // WHEN data.removeTag(t2); // THEN QCOMPARE(data.tags().size(), 1); QCOMPARE(data.tags().at(0), t1); QVERIFY(!data.tag(t2.id()).isValid()); QCOMPARE(data.tagItems(t1.id()).size(), 1); QCOMPARE(data.tagItems(t1.id()).at(0), i1); QVERIFY(data.tagItems(t2.id()).isEmpty()); QCOMPARE(data.items().toList().toSet(), itemSet); QVERIFY(data.item(i1.id()).isValid()); QVERIFY(data.item(i2.id()).isValid()); QVERIFY(!data.item(i2.id()).tags().contains(t2)); QCOMPARE(tagSpy.size(), 1); QCOMPARE(tagSpy.takeFirst().at(0).value(), t2); QCOMPARE(itemSpy.size(), 1); QCOMPARE(itemSpy.first().at(0).value(), i2); QVERIFY(!itemSpy.first().at(0).value().tags().contains(t2)); } void shouldCreateItems() { // GIVEN auto data = Testlib::AkonadiFakeData(); QScopedPointer monitor(data.createMonitor()); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemAdded); auto i1 = Akonadi::Item(42); i1.setPayloadFromData("42"); auto i2 = Akonadi::Item(43); i2.setPayloadFromData("43"); const auto itemSet = QSet() << i1 << i2; // WHEN data.createItem(i1); data.createItem(i2); // THEN QCOMPARE(data.items().toList().toSet(), itemSet); QCOMPARE(data.item(i1.id()), i1); QCOMPARE(data.item(i2.id()), i2); QCOMPARE(spy.size(), 2); QCOMPARE(spy.takeFirst().at(0).value(), i1); QCOMPARE(spy.takeFirst().at(0).value(), i2); } void shouldModifyItems() { // GIVEN auto data = Testlib::AkonadiFakeData(); QScopedPointer monitor(data.createMonitor()); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemChanged); QSignalSpy moveSpy(monitor.data(), &Akonadi::MonitorInterface::itemMoved); auto c1 = Akonadi::Collection(42); c1.setName(QStringLiteral("42")); data.createCollection(c1); auto i1 = Akonadi::Item(42); i1.setPayloadFromData("42"); i1.setParentCollection(Akonadi::Collection(42)); data.createItem(i1); auto i2 = Akonadi::Item(i1.id()); i2.setPayloadFromData("42-bis"); i2.setParentCollection(Akonadi::Collection(42)); // WHEN data.modifyItem(i2); // THEN QCOMPARE(data.items().size(), 1); QCOMPARE(data.item(i1.id()), i2); QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).value(), i2); QCOMPARE(moveSpy.size(), 0); } void shouldNotLooseParentCollectionOnModifyItem() { // GIVEN auto data = Testlib::AkonadiFakeData(); auto c1 = Akonadi::Collection(42); c1.setName(QStringLiteral("42")); data.createCollection(c1); auto i1 = Akonadi::Item(42); i1.setPayloadFromData("42"); i1.setParentCollection(Akonadi::Collection(42)); data.createItem(i1); auto i2 = Akonadi::Item(i1.id()); i2.setPayloadFromData("42-bis"); // WHEN data.modifyItem(i2); // THEN QCOMPARE(data.items().size(), 1); QCOMPARE(data.item(i1.id()), i2); QCOMPARE(data.item(i1.id()).parentCollection().id(), c1.id()); } void shouldListChildItems() { // GIVEN auto data = Testlib::AkonadiFakeData(); auto c1 = Akonadi::Collection(42); c1.setName(QStringLiteral("42")); data.createCollection(c1); auto i1 = Akonadi::Item(42); i1.setPayloadFromData("42"); i1.setParentCollection(Akonadi::Collection(42)); // WHEN data.createItem(i1); // THEN QCOMPARE(data.childItems(c1.id()).size(), 1); QCOMPARE(data.childItems(c1.id()).at(0), i1); } void shouldReparentItemsOnModify() { // GIVEN auto data = Testlib::AkonadiFakeData(); QScopedPointer monitor(data.createMonitor()); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemMoved); auto c1 = Akonadi::Collection(42); c1.setName(QStringLiteral("42")); data.createCollection(c1); auto c2 = Akonadi::Collection(43); c2.setName(QStringLiteral("43")); data.createCollection(c2); auto i1 = Akonadi::Item(42); i1.setPayloadFromData("42"); i1.setParentCollection(Akonadi::Collection(42)); data.createItem(i1); // WHEN i1.setPayloadFromData("42-bis"); i1.setParentCollection(Akonadi::Collection(43)); data.modifyItem(i1); // THEN QVERIFY(data.childItems(c1.id()).isEmpty()); QCOMPARE(data.childItems(c2.id()).size(), 1); QCOMPARE(data.childItems(c2.id()).at(0), i1); QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).value(), i1); } void shouldListTagItems() { // GIVEN auto data = Testlib::AkonadiFakeData(); auto t1 = Akonadi::Tag(42); t1.setName(QStringLiteral("42")); data.createTag(t1); auto i1 = Akonadi::Item(42); i1.setPayloadFromData("42"); i1.setTag(Akonadi::Tag(42)); // WHEN data.createItem(i1); // THEN QCOMPARE(data.tagItems(t1.id()).size(), 1); QCOMPARE(data.tagItems(t1.id()).at(0), i1); } void shouldRetagItemsOnModify() { // GIVEN auto data = Testlib::AkonadiFakeData(); QScopedPointer monitor(data.createMonitor()); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemChanged); auto t1 = Akonadi::Tag(42); t1.setName(QStringLiteral("42")); data.createTag(t1); auto t2 = Akonadi::Tag(43); t2.setName(QStringLiteral("43")); data.createTag(t2); auto i1 = Akonadi::Item(42); i1.setPayloadFromData("42"); i1.setTag(Akonadi::Tag(42)); data.createItem(i1); // WHEN i1.setPayloadFromData("42-bis"); i1.clearTag(Akonadi::Tag(42)); i1.setTag(Akonadi::Tag(43)); data.modifyItem(i1); // THEN QVERIFY(data.tagItems(t1.id()).isEmpty()); QCOMPARE(data.tagItems(t2.id()).size(), 1); QCOMPARE(data.tagItems(t2.id()).at(0), i1); QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).value(), i1); } void shouldRemoveItems() { // GIVEN auto data = Testlib::AkonadiFakeData(); QScopedPointer monitor(data.createMonitor()); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemRemoved); auto c1 = Akonadi::Collection(42); c1.setName(QStringLiteral("42")); data.createCollection(c1); auto i1 = Akonadi::Item(42); i1.setPayloadFromData("42"); i1.setParentCollection(Akonadi::Collection(42)); data.createItem(i1); // WHEN data.removeItem(i1); // THEN QVERIFY(data.items().isEmpty()); QVERIFY(!data.item(i1.id()).isValid()); QVERIFY(data.childItems(c1.id()).isEmpty()); QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).value(), i1); } }; ZANSHIN_TEST_MAIN(AkonadiFakeDataTest) #include "akonadifakedatatest.moc" diff --git a/tests/units/testlib/akonadifakestoragetest.cpp b/tests/units/testlib/akonadifakestoragetest.cpp index b4682388..dee9fde1 100644 --- a/tests/units/testlib/akonadifakestoragetest.cpp +++ b/tests/units/testlib/akonadifakestoragetest.cpp @@ -1,63 +1,63 @@ /* 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 #include #include "testlib/akonadifakedata.h" #include "testlib/akonadifakedataxmlloader.h" #include "testlib/akonadifakemonitor.h" #include "testlib/akonadifakestorage.h" #include "testlib/monitorspy.h" class AkonadiFakeStorageTest : public Testlib::AkonadiStorageTestBase { Q_OBJECT public: - explicit AkonadiFakeStorageTest(QObject *parent = Q_NULLPTR) + explicit AkonadiFakeStorageTest(QObject *parent = nullptr) : Testlib::AkonadiStorageTestBase(parent) { MonitorSpy::setExpirationDelay(100); auto loader = Testlib::AkonadiFakeDataXmlLoader(&m_data); loader.load(SOURCE_DIR "/../akonadi/testenv/data/testdata.xml"); } Akonadi::StorageInterface::Ptr createStorage() override { return Akonadi::StorageInterface::Ptr(m_data.createStorage()); } Akonadi::MonitorInterface::Ptr createMonitor() override { return Akonadi::MonitorInterface::Ptr(m_data.createMonitor()); } private: Testlib::AkonadiFakeData m_data; }; ZANSHIN_TEST_MAIN(AkonadiFakeStorageTest) #include "akonadifakestoragetest.moc" diff --git a/tests/units/testlib/monitorspytest.cpp b/tests/units/testlib/monitorspytest.cpp index a115d537..5d57981c 100644 --- a/tests/units/testlib/monitorspytest.cpp +++ b/tests/units/testlib/monitorspytest.cpp @@ -1,96 +1,96 @@ /* This file is part of Zanshin Copyright 2014 Mario Bensi 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 "testlib/monitorspy.h" #include "testlib/akonadifakemonitor.h" #include using namespace Testlib; class TimerTest: public QObject { Q_OBJECT public: TimerTest(AkonadiFakeMonitor *monitor, int duration) : m_monitor(monitor) { QTimer::singleShot(duration, Qt::PreciseTimer, this, &TimerTest::addItem); } private slots: void addItem() { Akonadi::Item item; m_monitor->addItem(item); } private: AkonadiFakeMonitor *m_monitor; }; class MonitorSpyTest : public QObject { Q_OBJECT public: - explicit MonitorSpyTest(QObject *parent = Q_NULLPTR) + explicit MonitorSpyTest(QObject *parent = nullptr) : QObject(parent) { } private slots: void shouldWaitOneSecondWithoutSignal() { // GIVEN AkonadiFakeMonitor monitor; MonitorSpy monitorSpy(&monitor); Akonadi::Item item; QDateTime now = QDateTime::currentDateTime(); // WHEN monitorSpy.waitForStableState(); // THEN QVERIFY(now.msecsTo(QDateTime::currentDateTime()) >= 1000ll); } void shouldWaitOneSecondAfterLastSignal() { // GIVEN AkonadiFakeMonitor monitor; MonitorSpy monitorSpy(&monitor); QDateTime now = QDateTime::currentDateTime(); // WHEN TimerTest timer(&monitor, 500); monitorSpy.waitForStableState(); // THEN QVERIFY(now.msecsTo(QDateTime::currentDateTime()) >= 1500ll); } }; ZANSHIN_TEST_MAIN(MonitorSpyTest) #include "monitorspytest.moc" diff --git a/tests/units/utils/compositejobtest.cpp b/tests/units/utils/compositejobtest.cpp index 694dca98..b1aba807 100644 --- a/tests/units/utils/compositejobtest.cpp +++ b/tests/units/utils/compositejobtest.cpp @@ -1,186 +1,186 @@ /* This file is part of Zanshin Copyright 2014 Mario Bensi 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/compositejob.h" #include "testlib/fakejob.h" using namespace Utils; class CompositeJobTest : public QObject { Q_OBJECT public: - explicit CompositeJobTest(QObject *parent = Q_NULLPTR) + explicit CompositeJobTest(QObject *parent = nullptr) : QObject(parent) , m_callCount(0) { } private: int m_callCount; private slots: void shouldCallHandlers() { // GIVEN int callCount = 0; auto handler = [&]() { callCount++; }; FakeJob *job1 = new FakeJob(this); FakeJob *job2 = new FakeJob(this); CompositeJob *compositeJob = new CompositeJob(this); compositeJob->setAutoDelete(false); QVERIFY(compositeJob->install(job1, handler)); QVERIFY(compositeJob->install(job2, handler)); // WHEN compositeJob->start(); QTest::qWait(FakeJob::DURATION + 10); // THEN QCOMPARE(callCount, 2); QVERIFY(!compositeJob->error()); delete compositeJob; } void shouldCallHandlersWithJob() { // GIVEN int callCount = 0; QList seenJobs; auto handlerWithJob = [&](KJob *job) { callCount++; seenJobs << job; }; FakeJob *job1 = new FakeJob(this); FakeJob *job2 = new FakeJob(this); CompositeJob *compositeJob = new CompositeJob(this); compositeJob->setAutoDelete(false); QVERIFY(compositeJob->install(job1, handlerWithJob)); QVERIFY(compositeJob->install(job2, handlerWithJob)); // WHEN compositeJob->start(); QTest::qWait(FakeJob::DURATION + 10); // THEN QCOMPARE(callCount, 2); QCOMPARE(seenJobs.toSet(), QSet() << job1 << job2); QVERIFY(!compositeJob->error()); delete compositeJob; } void handleJobResult(KJob*) { m_callCount++; } void shouldCallJobInHandler() { // GIVEN CompositeJob *compositeJob = new CompositeJob(this); compositeJob->setAutoDelete(false); auto handler = [&]() { FakeJob *job2 = new FakeJob(this); QObject::connect(job2, &KJob::result, this, &CompositeJobTest::handleJobResult); compositeJob->addSubjob(job2); job2->start(); }; FakeJob *job1 = new FakeJob(this); QVERIFY(compositeJob->install(job1, handler)); // WHEN compositeJob->start(); QTest::qWait(FakeJob::DURATION*2 + 10); QCOMPARE(m_callCount, 1); QVERIFY(!compositeJob->error()); delete compositeJob; } void shouldEmitErrorFromHandler() { // GIVEN CompositeJob *compositeJob = new CompositeJob(this); compositeJob->setAutoDelete(false); auto handler = [&]() { compositeJob->emitError(QStringLiteral("Error reached")); }; FakeJob *job = new FakeJob(this); QVERIFY(compositeJob->install(job, handler)); // WHEN compositeJob->start(); QTest::qWait(FakeJob::DURATION*2 + 10); QCOMPARE(compositeJob->error(), static_cast(KJob::UserDefinedError)); QCOMPARE(compositeJob->errorText(), QStringLiteral("Error reached")); delete compositeJob; } void shouldNotEmitResultTwiceOnSecondSubJobError() { // GIVEN int callCount = 0; auto handler = [&]() { callCount++; }; FakeJob *job1 = new FakeJob(this); FakeJob *job2 = new FakeJob(this); job2->setExpectedError(KJob::UserDefinedError, "Fake error"); CompositeJob *compositeJob = new CompositeJob(this); compositeJob->setAutoDelete(false); compositeJob->install(job1, handler); compositeJob->install(job2, handler); QSignalSpy spy(compositeJob, &KJob::result); // WHEN compositeJob->start(); QTest::qWait(FakeJob::DURATION*2 + 10); // THEN QCOMPARE(spy.count(), 1); delete compositeJob; } }; ZANSHIN_TEST_MAIN(CompositeJobTest) #include "compositejobtest.moc" diff --git a/tests/units/utils/dependencymanagertest.cpp b/tests/units/utils/dependencymanagertest.cpp index 4e9a7731..2252e040 100644 --- a/tests/units/utils/dependencymanagertest.cpp +++ b/tests/units/utils/dependencymanagertest.cpp @@ -1,367 +1,367 @@ /* 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/dependencymanager.h" using namespace Utils; class Interface0 { public: typedef QSharedPointer Ptr; Interface0() {} virtual ~Interface0() {} virtual void doSomething() = 0; }; class FirstImplementation0 : public Interface0 { public: void doSomething() override { qDebug() << "FirstImplementation"; } }; class SecondImplementation0 : public Interface0 { public: void doSomething() override { qDebug() << "SecondImplementation"; } }; #define DECLARE_IMPLEMENTED_INTERFACE(N) \ class Interface##N \ { \ public: \ typedef QSharedPointer Ptr; \ \ Interface##N() {} \ virtual ~Interface##N() {} \ virtual void doSomething() = 0; \ }; \ \ class Implementation##N : public Interface##N \ { \ public: \ void doSomething() override { qDebug() << "Implementation##N"; } \ }; DECLARE_IMPLEMENTED_INTERFACE(1) DECLARE_IMPLEMENTED_INTERFACE(2) DECLARE_IMPLEMENTED_INTERFACE(3) DECLARE_IMPLEMENTED_INTERFACE(4) DECLARE_IMPLEMENTED_INTERFACE(5) DECLARE_IMPLEMENTED_INTERFACE(6) DECLARE_IMPLEMENTED_INTERFACE(7) DECLARE_IMPLEMENTED_INTERFACE(8) DECLARE_IMPLEMENTED_INTERFACE(9) DECLARE_IMPLEMENTED_INTERFACE(10) DECLARE_IMPLEMENTED_INTERFACE(11) DECLARE_IMPLEMENTED_INTERFACE(12) DECLARE_IMPLEMENTED_INTERFACE(13) DECLARE_IMPLEMENTED_INTERFACE(14) class AnotherInterface { public: AnotherInterface() {} virtual ~AnotherInterface() {} virtual void doSomethingDelegated() = 0; }; class AnotherFirstImplementation : public AnotherInterface { public: explicit AnotherFirstImplementation(const Interface0::Ptr &iface) : m_iface(iface) {} void doSomethingDelegated() override { m_iface->doSomething(); } Interface0::Ptr iface() const { return m_iface; } private: Interface0::Ptr m_iface; }; class AnotherSecondImplementation : public AnotherInterface { public: AnotherSecondImplementation(Interface0::Ptr iface0, Interface1::Ptr iface1, Interface2::Ptr iface2, Interface3::Ptr iface3, Interface4::Ptr iface4, Interface5::Ptr iface5, Interface6::Ptr iface6, Interface7::Ptr iface7, Interface8::Ptr iface8, Interface9::Ptr iface9, Interface10::Ptr iface10, Interface11::Ptr iface11, Interface12::Ptr iface12, Interface13::Ptr iface13, Interface14::Ptr iface14) : m_iface0(iface0), m_iface1(iface1), m_iface2(iface2), m_iface3(iface3), m_iface4(iface4), m_iface5(iface5), m_iface6(iface6), m_iface7(iface7), m_iface8(iface8), m_iface9(iface9), m_iface10(iface10), m_iface11(iface11), m_iface12(iface12), m_iface13(iface13), m_iface14(iface14) { } void doSomethingDelegated() override { m_iface1->doSomething(); } Interface0::Ptr iface0() const { return m_iface0; } Interface1::Ptr iface1() const { return m_iface1; } Interface2::Ptr iface2() const { return m_iface2; } Interface3::Ptr iface3() const { return m_iface3; } Interface4::Ptr iface4() const { return m_iface4; } Interface5::Ptr iface5() const { return m_iface5; } Interface6::Ptr iface6() const { return m_iface6; } Interface7::Ptr iface7() const { return m_iface7; } Interface8::Ptr iface8() const { return m_iface8; } Interface9::Ptr iface9() const { return m_iface9; } Interface10::Ptr iface10() const { return m_iface10; } Interface11::Ptr iface11() const { return m_iface11; } Interface12::Ptr iface12() const { return m_iface12; } Interface13::Ptr iface13() const { return m_iface13; } Interface14::Ptr iface14() const { return m_iface14; } private: Interface0::Ptr m_iface0; Interface1::Ptr m_iface1; Interface2::Ptr m_iface2; Interface3::Ptr m_iface3; Interface4::Ptr m_iface4; Interface5::Ptr m_iface5; Interface6::Ptr m_iface6; Interface7::Ptr m_iface7; Interface8::Ptr m_iface8; Interface9::Ptr m_iface9; Interface10::Ptr m_iface10; Interface11::Ptr m_iface11; Interface12::Ptr m_iface12; Interface13::Ptr m_iface13; Interface14::Ptr m_iface14; }; class DependencyManagerTest : public QObject { Q_OBJECT private: static bool s_firstImplFactoryCalled; static DependencyManager *s_manager; static Interface0 *firstImplFactory(DependencyManager *manager) { s_firstImplFactoryCalled = true; s_manager = manager; return new FirstImplementation0; } private slots: void shouldMemorizeDependency() { DependencyManager deps; deps.add(); auto object1 = deps.create(); QVERIFY(object1.dynamicCast()); auto object2 = deps.create(); QVERIFY(object2.dynamicCast()); QVERIFY(object1 != object2); } void shouldAllowOurOwnFactory() { s_firstImplFactoryCalled = false; - s_manager = Q_NULLPTR; + s_manager = nullptr; DependencyManager deps; deps.add(&DependencyManagerTest::firstImplFactory); auto object = deps.create(); QVERIFY(object.dynamicCast()); QVERIFY(s_firstImplFactoryCalled); QVERIFY(s_manager == &deps); } void shouldAllowUniqueInstances() { DependencyManager deps; deps.add(); auto object1 = deps.create(); QVERIFY(object1.dynamicCast()); auto object2 = deps.create(); QVERIFY(object2.dynamicCast()); QVERIFY(object1 == object2); } void shouldAllowUniqueInstancesWithOurOwnFactory() { s_firstImplFactoryCalled = false; - s_manager = Q_NULLPTR; + s_manager = nullptr; DependencyManager deps; deps.add(&DependencyManagerTest::firstImplFactory); auto object1 = deps.create(); QVERIFY(object1.dynamicCast()); auto object2 = deps.create(); QVERIFY(object2.dynamicCast()); QVERIFY(s_firstImplFactoryCalled); QVERIFY(s_manager == &deps); QVERIFY(object1 == object2); } void shouldAllowOurOwnFactoryAsLambda() { #ifdef Q_COMPILER_LAMBDA bool ownFactoryCalled = false; - DependencyManager *managerCalled = Q_NULLPTR; + DependencyManager *managerCalled = nullptr; DependencyManager deps; deps.add([&](DependencyManager *manager) -> Interface0* { ownFactoryCalled = true; managerCalled = manager; return new FirstImplementation0; }); auto object = deps.create(); QVERIFY(object.dynamicCast()); QVERIFY(ownFactoryCalled); QVERIFY(managerCalled == &deps); #endif } void shouldMakeManagerSpecificDependencies() { DependencyManager deps1; deps1.add(); DependencyManager deps2; deps2.add(); auto object1 = deps1.create(); auto object2 = deps2.create(); QVERIFY(object1.dynamicCast()); QVERIFY(object2.dynamicCast()); } void shouldCleanupProviders() { QCOMPARE(Internal::Supplier::providersCount(), 0); { DependencyManager deps1; deps1.add(); QCOMPARE(Internal::Supplier::providersCount(), 1); { DependencyManager deps2; deps2.add(); QCOMPARE(Internal::Supplier::providersCount(), 2); } QCOMPARE(Internal::Supplier::providersCount(), 1); } QCOMPARE(Internal::Supplier::providersCount(), 0); } void shouldInjectDependencyInConstructor() { DependencyManager deps; deps.add(); deps.add(); auto object = deps.create(); auto impl = object.dynamicCast(); - QVERIFY(impl != Q_NULLPTR); + QVERIFY(impl != nullptr); QVERIFY(impl->iface().dynamicCast()); } void shouldInjectDependenciesInConstructor() { DependencyManager deps; deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); auto object = deps.create(); auto impl = object.dynamicCast(); - QVERIFY(impl != Q_NULLPTR); + QVERIFY(impl != nullptr); QVERIFY(impl->iface0().dynamicCast()); QVERIFY(impl->iface1().dynamicCast()); QVERIFY(impl->iface2().dynamicCast()); QVERIFY(impl->iface3().dynamicCast()); QVERIFY(impl->iface4().dynamicCast()); QVERIFY(impl->iface5().dynamicCast()); QVERIFY(impl->iface6().dynamicCast()); QVERIFY(impl->iface7().dynamicCast()); QVERIFY(impl->iface8().dynamicCast()); QVERIFY(impl->iface9().dynamicCast()); QVERIFY(impl->iface10().dynamicCast()); QVERIFY(impl->iface11().dynamicCast()); QVERIFY(impl->iface12().dynamicCast()); QVERIFY(impl->iface13().dynamicCast()); QVERIFY(impl->iface14().dynamicCast()); } }; bool DependencyManagerTest::s_firstImplFactoryCalled = false; -DependencyManager *DependencyManagerTest::s_manager = Q_NULLPTR; +DependencyManager *DependencyManagerTest::s_manager = nullptr; ZANSHIN_TEST_MAIN(DependencyManagerTest) #include "dependencymanagertest.moc" diff --git a/tests/units/widgets/applicationcomponentstest.cpp b/tests/units/widgets/applicationcomponentstest.cpp index 244e3f17..27cdb65d 100644 --- a/tests/units/widgets/applicationcomponentstest.cpp +++ b/tests/units/widgets/applicationcomponentstest.cpp @@ -1,776 +1,776 @@ /* 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 #include #include #include #include #include #include "utils/mem_fn.h" #include "domain/task.h" #include "presentation/taskfilterproxymodel.h" #include "presentation/querytreemodelbase.h" #include "presentation/runningtaskmodelinterface.h" #include "widgets/applicationcomponents.h" #include "widgets/availablepagesview.h" #include "widgets/availablesourcesview.h" #include "widgets/editorview.h" #include "widgets/filterwidget.h" #include "widgets/pageview.h" #include "widgets/pageviewerrorhandler.h" #include "widgets/quickselectdialog.h" #include "widgets/runningtaskwidget.h" class CustomModelStub : public QStandardItemModel { Q_OBJECT QMimeData *mimeData(const QModelIndexList &indexes) const override { QStringList dataString; std::transform(indexes.begin(), indexes.end(), std::back_inserter(dataString), [] (const QModelIndex &index) { return index.data().toString(); }); auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(dataString)); return data; } bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &destination) override { Q_UNUSED(action); Q_ASSERT(row == -1); Q_ASSERT(column == -1); Q_ASSERT(destination.isValid()); Q_ASSERT(data->hasFormat(QStringLiteral("application/x-zanshin-object"))); auto dataString = data->property("objects").toStringList(); Q_ASSERT(!dataString.isEmpty()); droppedItemDataString = dataString; dropDestination = destination.data().toString(); return true; } public: QStringList droppedItemDataString; QString dropDestination; }; class ApplicationModelStub : public QObject { Q_OBJECT Q_PROPERTY(QObject* currentPage READ currentPage WRITE setCurrentPage) public: typedef QSharedPointer Ptr; - explicit ApplicationModelStub(QObject *parent = Q_NULLPTR) - : QObject(parent), m_currentPage(Q_NULLPTR) {} + explicit ApplicationModelStub(QObject *parent = nullptr) + : QObject(parent), m_currentPage(nullptr) {} QObject *currentPage() { return m_currentPage; } void setCurrentPage(QObject *page) { if (page == m_currentPage) return; m_currentPage = page; emit currentPageChanged(m_currentPage); } signals: void currentPageChanged(QObject *page); private: QObject *m_currentPage; }; class AvailablePagesModelStub : public QObject { Q_OBJECT Q_PROPERTY(QAbstractItemModel* pageListModel READ pageListModel) public: - explicit AvailablePagesModelStub(QObject *parent = Q_NULLPTR) + explicit AvailablePagesModelStub(QObject *parent = nullptr) : QObject(parent) { QStandardItem *inbox = new QStandardItem; inbox->setData("Inbox", Qt::DisplayRole); itemModel.appendRow(inbox); QStandardItem *project = new QStandardItem; project->setData("Project", Qt::DisplayRole); itemModel.appendRow(project); } QAbstractItemModel *pageListModel() { return &itemModel; } Q_SCRIPTABLE QObject *createPageForIndex(const QModelIndex &index) { auto page = new QObject(this); auto model = new QStringListModel(page); model->setStringList(QStringList() << QStringLiteral("Items") << QStringLiteral("from") << index.data().toString()); page->setProperty("centralListModel", QVariant::fromValue(model)); createdPages << page; return page; } public: QList createdPages; CustomModelStub itemModel; }; class PageModelStub : public QObject { Q_OBJECT Q_PROPERTY(QAbstractItemModel* centralListModel READ centralListModel) public: QAbstractItemModel *centralListModel() { return &itemModel; } void addTask(const QString &title) { auto task = Domain::Task::Ptr::create(); task->setTitle(title); addTask(task); } void addTask(const Domain::Task::Ptr &task) { QStandardItem *item = new QStandardItem; item->setData(QVariant::fromValue(task), Presentation::QueryTreeModelBase::ObjectRole); item->setData(task->title(), Qt::DisplayRole); itemModel.appendRow(item); } Domain::Task::Ptr itemAtRow(int row) const { return itemModel.index(row, 0).data(Presentation::QueryTreeModelBase::ObjectRole) .value(); } QModelIndexList selectedIndexes() const { return selectedItems; } public: QModelIndexList selectedItems; CustomModelStub itemModel; }; class EditorModelStub : public QObject { Q_OBJECT public: - explicit EditorModelStub(QObject *parent = Q_NULLPTR) + explicit EditorModelStub(QObject *parent = nullptr) : QObject(parent) { } void setPropertyAndSignal(const QByteArray &name, const QVariant &value) { if (property(name) == value) return; setProperty(name, value); if (name == "text") emit textChanged(value.toString()); else if (name == "title") emit titleChanged(value.toString()); else if (name == "done") emit doneChanged(value.toBool()); else if (name == "startDate") emit startDateChanged(value.toDate()); else if (name == "dueDate") emit dueDateChanged(value.toDate()); else qFatal("Unsupported property %s", name.constData()); } public slots: void setTitle(const QString &title) { setPropertyAndSignal("title", title); } void setText(const QString &text) { setPropertyAndSignal("text", text); } void setDone(bool done) { setPropertyAndSignal("done", done); } void setStartDate(const QDate &start) { setPropertyAndSignal("startDate", start); } void setDueDate(const QDate &due) { setPropertyAndSignal("dueDate", due); } signals: void textChanged(const QString &text); void titleChanged(const QString &title); void doneChanged(bool done); void startDateChanged(const QDate &date); void dueDateChanged(const QDate &due); }; class RunningTaskModelStub : public Presentation::RunningTaskModelInterface { Q_OBJECT public: explicit RunningTaskModelStub(QObject *parent = nullptr) : Presentation::RunningTaskModelInterface(parent) { } Domain::Task::Ptr runningTask() const override { return {}; } void setRunningTask(const Domain::Task::Ptr &) override {} void taskDeleted(const Domain::Task::Ptr &) override {} void stopTask() override {} void doneTask() override {} }; class QuickSelectDialogStub : public Widgets::QuickSelectDialogInterface { public: typedef QSharedPointer Ptr; explicit QuickSelectDialogStub() - : parent(Q_NULLPTR), + : parent(nullptr), execCount(0), - itemModel(Q_NULLPTR) + itemModel(nullptr) { } int exec() override { execCount++; return QDialog::Accepted; } void setModel(QAbstractItemModel *model) override { itemModel = model; } QPersistentModelIndex selectedIndex() const override { return index; } QWidget *parent; int execCount; QAbstractItemModel *itemModel; QPersistentModelIndex index; }; class ApplicationComponentsTest : public QObject { Q_OBJECT public: - explicit ApplicationComponentsTest(QObject *parent = Q_NULLPTR) + explicit ApplicationComponentsTest(QObject *parent = nullptr) : QObject(parent) { qputenv("ZANSHIN_UNIT_TEST_RUN", "1"); } private slots: void shouldHaveApplicationModelAndSetErrorHandler() { // GIVEN Widgets::ApplicationComponents components; auto model = QObjectPtr::create(); // WHEN components.setModel(model); // THEN QCOMPARE(components.model(), model); auto errorHandlerBase = model->property("errorHandler").value(); QVERIFY(errorHandlerBase); auto errorHandler = static_cast(errorHandlerBase); QVERIFY(errorHandler); QVERIFY(!errorHandler->pageView()); // WHEN auto pageView = components.pageView(); // THEN QCOMPARE(errorHandler->pageView(), pageView); } void shouldApplyAvailableSourcesModelToAvailableSourcesView() { // GIVEN Widgets::ApplicationComponents components; auto model = QObjectPtr::create(); QObject availableSources; model->setProperty("availableSources", QVariant::fromValue(&availableSources)); // WHEN components.setModel(model); // THEN QCOMPARE(components.availableSourcesView()->model(), &availableSources); } void shouldApplyAvailableSourcesModelAlsoToCreatedAvailableSourcesView() { // GIVEN Widgets::ApplicationComponents components; // Force creation components.availableSourcesView(); auto model = QObjectPtr::create(); QObject availableSources; model->setProperty("availableSources", QVariant::fromValue(&availableSources)); // WHEN components.setModel(model); // THEN QCOMPARE(components.availableSourcesView()->model(), &availableSources); } void shouldApplyAvailablePagesModelToAvailablePagesView() { // GIVEN Widgets::ApplicationComponents components; auto model = QObjectPtr::create(); QObject availablePages; model->setProperty("availablePages", QVariant::fromValue(&availablePages)); QObject availableSources; QAbstractItemModel *sourcesModel = new QStandardItemModel(model.data()); availableSources.setProperty("sourceListModel", QVariant::fromValue(sourcesModel)); model->setProperty("availableSources", QVariant::fromValue(&availableSources)); // WHEN components.setModel(model); // THEN QCOMPARE(components.availablePagesView()->model(), &availablePages); QCOMPARE(components.availablePagesView()->projectSourcesModel(), sourcesModel); } void shouldApplyAvailablePagesModelAlsoToCreatedAvailablePagesView() { // GIVEN Widgets::ApplicationComponents components; // Force creation components.availablePagesView(); auto model = QObjectPtr::create(); QObject availablePages; QAbstractItemModel *sourcesModel = new QStandardItemModel(model.data()); model->setProperty("dataSourcesModel", QVariant::fromValue(sourcesModel)); model->setProperty("availablePages", QVariant::fromValue(&availablePages)); // WHEN components.setModel(model); // THEN QCOMPARE(components.availablePagesView()->model(), &availablePages); QCOMPARE(components.availablePagesView()->projectSourcesModel(), sourcesModel); } void shouldApplyCurrentPageModelToPageView() { // GIVEN Widgets::ApplicationComponents components; auto model = QObjectPtr::create(); QObject currentPage; model->setProperty("currentPage", QVariant::fromValue(¤tPage)); // WHEN components.setModel(model); // THEN QCOMPARE(components.pageView()->model(), ¤tPage); } void shouldApplyCurrentPageModelAlsoToCreatedPageView() { // GIVEN Widgets::ApplicationComponents components; // Force creation components.pageView(); auto model = QObjectPtr::create(); QObject currentPage; model->setProperty("currentPage", QVariant::fromValue(¤tPage)); // WHEN components.setModel(model); // THEN QCOMPARE(components.pageView()->model(), ¤tPage); } void shouldApplyRunningTaskModelToPageView() { // GIVEN Widgets::ApplicationComponents components; auto model = QObjectPtr::create(); auto runningTaskModel = new RunningTaskModelStub(model.data()); model->setProperty("runningTaskModel", QVariant::fromValue(runningTaskModel)); // WHEN components.setModel(model); // THEN QCOMPARE(components.pageView()->runningTaskModel(), runningTaskModel); } void shouldApplyRunningTaskModelAlsoToCreatedPageView() { // GIVEN Widgets::ApplicationComponents components; // Force creation components.pageView(); auto model = QObjectPtr::create(); auto runningTaskModel = new RunningTaskModelStub(model.data()); model->setProperty("runningTaskModel", QVariant::fromValue(runningTaskModel)); // WHEN components.setModel(model); // THEN QCOMPARE(components.pageView()->runningTaskModel(), runningTaskModel); } void shouldApplyEditorModelToEditorView() { // GIVEN Widgets::ApplicationComponents components; auto model = QObjectPtr::create(); QObject *editorModel = new EditorModelStub(model.data()); model->setProperty("editor", QVariant::fromValue(editorModel)); // WHEN components.setModel(model); // THEN QCOMPARE(components.editorView()->model(), editorModel); } void shouldApplyEditorModelAlsoToCreatedPageView() { // GIVEN Widgets::ApplicationComponents components; // Force creation components.editorView(); auto model = QObjectPtr::create(); QObject *editorModel = new EditorModelStub(model.data()); model->setProperty("editor", QVariant::fromValue(editorModel)); // WHEN components.setModel(model); // THEN QCOMPARE(components.editorView()->model(), editorModel); } void shouldApplyRunningTaskModelToRunningTaskView() { // GIVEN Widgets::ApplicationComponents components; auto model = QObjectPtr::create(); auto runningTaskModel = new RunningTaskModelStub(model.data()); model->setProperty("runningTaskModel", QVariant::fromValue(runningTaskModel)); // WHEN components.setModel(model); // THEN QCOMPARE(components.runningTaskView()->model(), runningTaskModel); } void shouldApplyRunningTaskModelAlsoToCreatedRunningTaskView() { // GIVEN Widgets::ApplicationComponents components; // Force creation components.runningTaskView(); auto model = QObjectPtr::create(); auto runningTaskModel = new RunningTaskModelStub(model.data()); model->setProperty("runningTaskModel", QVariant::fromValue(runningTaskModel)); // WHEN components.setModel(model); // THEN QCOMPARE(components.runningTaskView()->model(), runningTaskModel); } void shouldPropageNullModelsToViews() { // GIVEN Widgets::ApplicationComponents components; auto model = QObjectPtr::create(); auto availableSources = new QObject(model.data()); model->setProperty("availableSources", QVariant::fromValue(availableSources)); auto availablePages = new QObject(model.data()); model->setProperty("availablePages", QVariant::fromValue(availablePages)); auto currentPage = new QObject(model.data()); model->setProperty("currentPage", QVariant::fromValue(currentPage)); auto editorModel = new EditorModelStub(model.data()); model->setProperty("editor", QVariant::fromValue(editorModel)); auto runningTaskModel = new RunningTaskModelStub(model.data()); model->setProperty("runningTaskModel", QVariant::fromValue(runningTaskModel)); components.setModel(model); // WHEN components.setModel(QObjectPtr()); components.availableSourcesView(); components.availablePagesView(); components.pageView(); components.editorView(); components.runningTaskView(); // THEN QVERIFY(!components.availableSourcesView()->model()); QVERIFY(!components.availablePagesView()->model()); QVERIFY(!components.pageView()->model()); QVERIFY(!components.editorView()->model()); QVERIFY(!components.runningTaskView()->model()); } void shouldPropageNullModelsToCreatedViews() { // GIVEN Widgets::ApplicationComponents components; components.availableSourcesView(); components.availablePagesView(); components.pageView(); components.editorView(); components.runningTaskView(); auto model = QObjectPtr::create(); auto availableSources = new QObject(model.data()); model->setProperty("availableSources", QVariant::fromValue(availableSources)); auto availablePages = new QObject(model.data()); model->setProperty("availablePages", QVariant::fromValue(availablePages)); auto currentPage = new QObject(model.data()); model->setProperty("currentPage", QVariant::fromValue(currentPage)); auto editorModel = new EditorModelStub(model.data()); model->setProperty("editor", QVariant::fromValue(editorModel)); auto runningTaskModel = new RunningTaskModelStub(model.data()); model->setProperty("runningTaskModel", QVariant::fromValue(runningTaskModel)); components.setModel(model); // WHEN components.setModel(QObjectPtr()); // THEN QVERIFY(!components.availableSourcesView()->model()); QVERIFY(!components.availablePagesView()->model()); QVERIFY(!components.pageView()->model()); QVERIFY(!components.editorView()->model()); QVERIFY(!components.runningTaskView()->model()); } void shouldApplyAvailablePagesSelectionToApplicationModel() { // GIVEN auto model = ApplicationModelStub::Ptr::create(); AvailablePagesModelStub availablePagesModel; model->setProperty("availablePages", QVariant::fromValue(&availablePagesModel)); - model->setProperty("currentPage", QVariant::fromValue(Q_NULLPTR)); + model->setProperty("currentPage", QVariant::fromValue(nullptr)); QObject editorModel; editorModel.setProperty("task", QVariant::fromValue(Domain::Task::Ptr::create())); model->setProperty("editor", QVariant::fromValue(&editorModel)); Widgets::ApplicationComponents components; components.setModel(model); Widgets::AvailablePagesView *availablePagesView = components.availablePagesView(); auto pagesView = availablePagesView->findChild(QStringLiteral("pagesView")); QVERIFY(pagesView); Widgets::PageView *pageView = components.pageView(); QVERIFY(pageView); QVERIFY(!pageView->model()); QModelIndex index = pagesView->model()->index(0, 0); // WHEN pagesView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); // THEN QCOMPARE(availablePagesModel.createdPages.size(), 1); QCOMPARE(model->property("currentPage").value(), availablePagesModel.createdPages.first()); QCOMPARE(pageView->model(), availablePagesModel.createdPages.first()); QVERIFY(editorModel.property("task").value().isNull()); } void shouldApplyPageViewSelectionToEditorModel() { // GIVEN auto model = QObjectPtr::create(); PageModelStub pageModel; pageModel.addTask(QStringLiteral("0. First task")); pageModel.addTask(QStringLiteral("1. Second task")); pageModel.addTask(QStringLiteral("2. Third task")); pageModel.addTask(QStringLiteral("3. Yet another task")); model->setProperty("currentPage", QVariant::fromValue(&pageModel)); EditorModelStub editorModel; model->setProperty("editor", QVariant::fromValue(&editorModel)); Widgets::ApplicationComponents components; components.setModel(model); Widgets::PageView *pageView = components.pageView(); auto centralView = pageView->findChild(QStringLiteral("centralView")); QModelIndex index = centralView->model()->index(2, 0); // WHEN centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); // THEN QCOMPARE(editorModel.property("task").value(), pageModel.itemAtRow(index.row())); } void shouldHaveDefaultActionsList() { // GIVEN Widgets::ApplicationComponents components; // WHEN auto actions = components.globalActions(); // THEN // availablePages view auto available = components.availablePagesView(); foreach (const auto &key, available->globalActions().keys()) QCOMPARE(actions.value(key), available->globalActions().value(key)); // availableSources view auto availableSources = components.availableSourcesView(); foreach (const auto &key, availableSources->globalActions().keys()) QCOMPARE(actions.value(key), availableSources->globalActions().value(key)); // page view auto page = components.pageView(); foreach (const auto &key, page->globalActions().keys()) QCOMPARE(actions.value(key), page->globalActions().value(key)); // application component own action auto moveAction = components.findChild(QStringLiteral("moveItemAction")); QCOMPARE(actions.value(QStringLiteral("page_view_move")), moveAction); } void shouldMoveItem() { // GIVEN auto model = QObjectPtr::create(); PageModelStub pageModel; pageModel.addTask(QStringLiteral("0. First task")); pageModel.addTask(QStringLiteral("1. Second task")); pageModel.addTask(QStringLiteral("2. Third task")); pageModel.addTask(QStringLiteral("3. Yet another task")); model->setProperty("currentPage", QVariant::fromValue(&pageModel)); AvailablePagesModelStub availablePagesModelStub; model->setProperty("availablePages", QVariant::fromValue(&availablePagesModelStub)); QWidget *mainWidget = new QWidget; Widgets::ApplicationComponents components(mainWidget); components.setModel(model); auto availablePageView = components.availablePagesView(); auto availablePagesTreeView = availablePageView->findChild(QStringLiteral("pagesView")); Q_ASSERT(availablePagesTreeView); auto dialogStub = QuickSelectDialogStub::Ptr::create(); dialogStub->index = availablePagesModelStub.pageListModel()->index(0, 0); // inbox selected components.setQuickSelectDialogFactory([dialogStub] (QWidget *parent) { dialogStub->parent = parent; return dialogStub; }); auto pageView = components.pageView(); auto centralView = pageView->findChild(QStringLiteral("centralView")); QModelIndex index1 = pageModel.itemModel.index(0,0); QModelIndex index2 = pageModel.itemModel.index(2,0); auto filterWidget = pageView->findChild(QStringLiteral("filterWidget")); auto displayedModel = filterWidget->proxyModel(); auto displayedIndex = displayedModel->index(0, 0); auto displayedIndex2 = displayedModel->index(2, 0); auto moveAction = components.findChild(QStringLiteral("moveItemAction")); // WHEN pageModel.selectedItems << index1 << index2; centralView->selectionModel()->setCurrentIndex(displayedIndex, QItemSelectionModel::ClearAndSelect); centralView->selectionModel()->setCurrentIndex(displayedIndex2, QItemSelectionModel::Select); moveAction->trigger(); // THEN QCOMPARE(dialogStub->execCount, 1); QCOMPARE(dialogStub->parent, pageView); QCOMPARE(dialogStub->itemModel, availablePagesModelStub.pageListModel()); QCOMPARE(availablePagesModelStub.itemModel.dropDestination, QStringLiteral("Inbox")); QCOMPARE(availablePagesModelStub.itemModel.droppedItemDataString.size(), 2); QCOMPARE(availablePagesModelStub.itemModel.droppedItemDataString.at(0), index1.data().toString()); QCOMPARE(availablePagesModelStub.itemModel.droppedItemDataString.at(1), index2.data().toString()); } }; ZANSHIN_TEST_MAIN(ApplicationComponentsTest) #include "applicationcomponentstest.moc" diff --git a/tests/units/widgets/availablepagesviewtest.cpp b/tests/units/widgets/availablepagesviewtest.cpp index 24d9ff7e..8bc609a3 100644 --- a/tests/units/widgets/availablepagesviewtest.cpp +++ b/tests/units/widgets/availablepagesviewtest.cpp @@ -1,532 +1,532 @@ /* 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 #include #include #include #include #include #include #include #include "domain/project.h" #include "domain/context.h" #include "presentation/metatypes.h" #include "presentation/querytreemodelbase.h" #include "widgets/availablepagesview.h" #include "widgets/newprojectdialog.h" #include "widgets/quickselectdialog.h" #include "messageboxstub.h" class NewProjectDialogStub : public Widgets::NewProjectDialogInterface { public: typedef QSharedPointer Ptr; explicit NewProjectDialogStub() - : parent(Q_NULLPTR), + : parent(nullptr), execCount(0), - sourceModel(Q_NULLPTR), + sourceModel(nullptr), source(Domain::DataSource::Ptr::create()) { } int exec() override { execCount++; return QDialog::Accepted; } void setDataSourcesModel(QAbstractItemModel *model) override { sourceModel = model; } QString name() const override { return QStringLiteral("name"); } Domain::DataSource::Ptr dataSource() const override { return source; } QWidget *parent; int execCount; QAbstractItemModel *sourceModel; Domain::DataSource::Ptr source; }; class QuickSelectDialogStub : public Widgets::QuickSelectDialogInterface { public: typedef QSharedPointer Ptr; explicit QuickSelectDialogStub() - : parent(Q_NULLPTR), + : parent(nullptr), execCount(0), - itemModel(Q_NULLPTR) + itemModel(nullptr) { } int exec() override { execCount++; return QDialog::Accepted; } void setModel(QAbstractItemModel *model) override { itemModel = model; } QPersistentModelIndex selectedIndex() const override { return index; } QWidget *parent; int execCount; QAbstractItemModel *itemModel; QPersistentModelIndex index; }; class AvailablePagesModelStub : public QObject { Q_OBJECT public: - explicit AvailablePagesModelStub(QObject *parent = Q_NULLPTR) + explicit AvailablePagesModelStub(QObject *parent = nullptr) : QObject(parent) { } public slots: void addProject(const QString &name, const Domain::DataSource::Ptr &source) { projectNames << name; sources << source; } void addContext(const QString &name) { contextNames << name; } void addTag(const QString &name) { tagNames << name; } void removeItem(const QModelIndex &index) { projectRemoved = index.data().toString(); } public Q_SLOTS: - QObject *createPageForIndex(const QModelIndex &) { return Q_NULLPTR; } + QObject *createPageForIndex(const QModelIndex &) { return nullptr; } public: QStringList projectNames; QStringList contextNames; QStringList tagNames; QList sources; QString projectRemoved; }; class AvailablePagesViewTest : public QObject { Q_OBJECT private slots: void shouldHaveDefaultState() { Widgets::AvailablePagesView available; QVERIFY(!available.model()); QVERIFY(!available.projectSourcesModel()); QVERIFY(available.defaultProjectSource().isNull()); auto pagesView = available.findChild(QStringLiteral("pagesView")); QVERIFY(pagesView); QVERIFY(pagesView->isVisibleTo(&available)); QVERIFY(!pagesView->header()->isVisibleTo(&available)); QCOMPARE(pagesView->dragDropMode(), QTreeView::DropOnly); auto actionBar = available.findChild(QStringLiteral("actionBar")); QVERIFY(actionBar); QVERIFY(actionBar->isVisibleTo(&available)); auto addProjectAction = available.findChild(QStringLiteral("addProjectAction")); QVERIFY(addProjectAction); auto addContextAction = available.findChild(QStringLiteral("addContextAction")); QVERIFY(addContextAction); auto removeAction = available.findChild(QStringLiteral("removeAction")); QVERIFY(removeAction); auto goPreviousAction = available.findChild(QStringLiteral("goPreviousAction")); QVERIFY(goPreviousAction); auto goNextAction = available.findChild(QStringLiteral("goNextAction")); QVERIFY(goNextAction); auto goToAction = available.findChild(QStringLiteral("goToAction")); QVERIFY(goToAction); auto projectDialogFactory = available.projectDialogFactory(); QVERIFY(projectDialogFactory(&available).dynamicCast()); auto quickSelectDialogFactory = available.quickSelectDialogFactory(); QVERIFY(quickSelectDialogFactory(&available).dynamicCast()); auto actions = available.globalActions(); QCOMPARE(actions.value(QStringLiteral("pages_project_add")), addProjectAction); QCOMPARE(actions.value(QStringLiteral("pages_context_add")), addContextAction); QCOMPARE(actions.value(QStringLiteral("pages_remove")), removeAction); QCOMPARE(actions.value(QStringLiteral("pages_go_previous")), goPreviousAction); QCOMPARE(actions.value(QStringLiteral("pages_go_next")), goNextAction); QCOMPARE(actions.value(QStringLiteral("pages_go_to")), goToAction); } void shouldShowOnlyAddActionsNeededByTheModel_data() { QTest::addColumn("hasProjects"); QTest::addColumn("hasContexts"); QTest::addColumn("hasTags"); QTest::newRow("!projects !contexts !tags") << false << false << false; QTest::newRow("!projects !contexts tags") << false << false << true; QTest::newRow("!projects contexts !tags") << false << true << false; QTest::newRow("!projects contexts tags") << false << true << true; QTest::newRow("projects !contexts !tags") << true << false << false; QTest::newRow("projects !contexts tags") << true << false << true; QTest::newRow("projects contexts !tags") << true << true << false; QTest::newRow("projects contexts tags") << true << true << true; } void shouldDisplayListFromPageModel() { // GIVEN QStringListModel model(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") ); AvailablePagesModelStub stubPagesModel; stubPagesModel.setProperty("pageListModel", QVariant::fromValue(static_cast(&model))); Widgets::AvailablePagesView available; auto pagesView = available.findChild(QStringLiteral("pagesView")); QVERIFY(pagesView); QVERIFY(!pagesView->model()); // WHEN available.setModel(&stubPagesModel); QTest::qWait(10); // THEN QCOMPARE(pagesView->model(), &model); QCOMPARE(pagesView->selectionModel()->currentIndex(), model.index(0, 0)); } void shouldNotCrashWithNullModel() { // GIVEN QStringListModel model(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") ); AvailablePagesModelStub stubPagesModel; stubPagesModel.setProperty("pageListModel", QVariant::fromValue(static_cast(&model))); Widgets::AvailablePagesView available; available.setModel(&stubPagesModel); QTest::qWait(10); auto pagesView = available.findChild(QStringLiteral("pagesView")); QVERIFY(pagesView); QCOMPARE(pagesView->model(), &model); // WHEN - available.setModel(Q_NULLPTR); + available.setModel(nullptr); QTest::qWait(10); // THEN QVERIFY(!available.isEnabled()); QVERIFY(!pagesView->model()); } void shouldAddNewProjects() { // GIVEN AvailablePagesModelStub model; QStringListModel sourceModel; auto dialogStub = NewProjectDialogStub::Ptr::create(); auto source = Domain::DataSource::Ptr::create(); Widgets::AvailablePagesView available; available.setModel(&model); available.setProjectSourcesModel(&sourceModel); available.setDefaultProjectSource(source); available.setProjectDialogFactory([dialogStub] (QWidget *parent) { dialogStub->parent = parent; return dialogStub; }); auto addProjectAction = available.findChild(QStringLiteral("addProjectAction")); // WHEN addProjectAction->trigger(); // THEN QCOMPARE(dialogStub->execCount, 1); QCOMPARE(dialogStub->parent, &available); QCOMPARE(dialogStub->sourceModel, &sourceModel); QCOMPARE(model.projectNames.size(), 1); QCOMPARE(model.projectNames.first(), dialogStub->name()); QCOMPARE(model.sources.size(), 1); QCOMPARE(model.sources.first(), dialogStub->dataSource()); QCOMPARE(available.defaultProjectSource(), dialogStub->dataSource()); } void shouldAddNewContexts() { // GIVEN AvailablePagesModelStub model; QStringListModel sourceModel; auto dialogStub = NewProjectDialogStub::Ptr::create(); auto source = Domain::DataSource::Ptr::create(); auto msgBoxStub = MessageBoxStub::Ptr::create(); msgBoxStub->setTextInput(QStringLiteral("Foo")); Widgets::AvailablePagesView available; available.setModel(&model); available.setProjectSourcesModel(&sourceModel); available.setDefaultProjectSource(source); available.setMessageBoxInterface(msgBoxStub); auto addContextAction = available.findChild(QStringLiteral("addContextAction")); // WHEN addContextAction->trigger(); // THEN QVERIFY(msgBoxStub->called()); QCOMPARE(model.contextNames.size(), 1); QCOMPARE(model.contextNames.first(), QStringLiteral("Foo")); } void shouldRemoveAPage_data() { QTest::addColumn("object"); QTest::addColumn("actionEnabled"); auto project1 = Domain::Project::Ptr::create(); project1->setName(QStringLiteral("Project 1")); QTest::newRow("project") << QObjectPtr(project1) << true; auto context1 = Domain::Context::Ptr::create(); context1->setName(QStringLiteral("Context 1")); QTest::newRow("context") << QObjectPtr(context1) << true; QTest::newRow("non removable") << QObjectPtr::create() << false; } void shouldRemoveAPage() { QFETCH(QObjectPtr, object); QFETCH(bool, actionEnabled); // GIVEN QStringList list; list << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C"); QStandardItemModel model; for (int row = 0; row < list.count(); ++row) { model.setItem(row, new QStandardItem(list.at(row))); } QVERIFY(model.setData(model.index(0, 0), QVariant::fromValue(object), Presentation::QueryTreeModelBase::ObjectRole)); AvailablePagesModelStub stubPagesModel; stubPagesModel.setProperty("pageListModel", QVariant::fromValue(static_cast(&model))); Widgets::AvailablePagesView available; auto pagesView = available.findChild(QStringLiteral("pagesView")); QVERIFY(pagesView); QVERIFY(!pagesView->model()); available.setModel(&stubPagesModel); QTest::qWait(10); auto removeAction = available.findChild(QStringLiteral("removeAction")); auto msgbox = MessageBoxStub::Ptr::create(); available.setMessageBoxInterface(msgbox); // WHEN if (actionEnabled) removeAction->trigger(); // THEN QCOMPARE(removeAction->isEnabled(), actionEnabled); if (actionEnabled) { QCOMPARE(stubPagesModel.projectRemoved, list.first()); } } void shouldGoToPreviousSelectablePage() { // GIVEN QStandardItemModel model; model.appendRow(new QStandardItem(QStringLiteral("Inbox"))); auto projects = new QStandardItem(QStringLiteral("Projects")); projects->setFlags(Qt::NoItemFlags); model.appendRow(projects); projects->appendRow(new QStandardItem(QStringLiteral("Project 1"))); projects->appendRow(new QStandardItem(QStringLiteral("Project 2"))); AvailablePagesModelStub stubPagesModel; stubPagesModel.setProperty("pageListModel", QVariant::fromValue(static_cast(&model))); Widgets::AvailablePagesView available; auto pagesView = available.findChild(QStringLiteral("pagesView")); QVERIFY(pagesView); QVERIFY(!pagesView->model()); available.setModel(&stubPagesModel); QTest::qWait(10); auto goPreviousAction = available.findChild(QStringLiteral("goPreviousAction")); pagesView->setCurrentIndex(model.index(1, 0, model.indexFromItem(projects))); // WHEN goPreviousAction->trigger(); // THEN QCOMPARE(pagesView->currentIndex(), model.index(0, 0, model.indexFromItem(projects))); // WHEN goPreviousAction->trigger(); // THEN QCOMPARE(pagesView->currentIndex(), model.index(0, 0)); // WHEN goPreviousAction->trigger(); // THEN QCOMPARE(pagesView->currentIndex(), model.index(0, 0)); } void shouldGoToNextSelectablePage() { // GIVEN QStandardItemModel model; model.appendRow(new QStandardItem(QStringLiteral("Inbox"))); auto projects = new QStandardItem(QStringLiteral("Projects")); projects->setFlags(Qt::NoItemFlags); model.appendRow(projects); projects->appendRow(new QStandardItem(QStringLiteral("Project 1"))); projects->appendRow(new QStandardItem(QStringLiteral("Project 2"))); AvailablePagesModelStub stubPagesModel; stubPagesModel.setProperty("pageListModel", QVariant::fromValue(static_cast(&model))); Widgets::AvailablePagesView available; auto pagesView = available.findChild(QStringLiteral("pagesView")); QVERIFY(pagesView); QVERIFY(!pagesView->model()); available.setModel(&stubPagesModel); QTest::qWait(10); auto goNextAction = available.findChild(QStringLiteral("goNextAction")); pagesView->setCurrentIndex(model.index(0, 0)); // WHEN goNextAction->trigger(); // THEN QCOMPARE(pagesView->currentIndex(), model.index(0, 0, model.indexFromItem(projects))); // WHEN goNextAction->trigger(); // THEN QCOMPARE(pagesView->currentIndex(), model.index(1, 0, model.indexFromItem(projects))); // WHEN goNextAction->trigger(); // THEN QCOMPARE(pagesView->currentIndex(), model.index(1, 0, model.indexFromItem(projects))); } void shouldGoToUserSelectedIndex() { // GIVEN QStandardItemModel model; model.appendRow(new QStandardItem(QStringLiteral("Inbox"))); auto projects = new QStandardItem(QStringLiteral("Projects")); projects->setFlags(Qt::NoItemFlags); model.appendRow(projects); projects->appendRow(new QStandardItem(QStringLiteral("Project 1"))); projects->appendRow(new QStandardItem(QStringLiteral("Project 2"))); AvailablePagesModelStub stubPagesModel; stubPagesModel.setProperty("pageListModel", QVariant::fromValue(static_cast(&model))); auto dialogStub = QuickSelectDialogStub::Ptr::create(); // Project 2 will be selected dialogStub->index = model.index(1, 0, model.index(1, 0)); Widgets::AvailablePagesView available; available.setModel(&stubPagesModel); available.setQuickSelectDialogFactory([dialogStub] (QWidget *parent) { dialogStub->parent = parent; return dialogStub; }); auto pagesView = available.findChild(QStringLiteral("pagesView")); QVERIFY(pagesView); QCOMPARE(pagesView->model(), &model); auto goToAction = available.findChild(QStringLiteral("goToAction")); // WHEN goToAction->trigger(); // THEN QCOMPARE(dialogStub->execCount, 1); QCOMPARE(dialogStub->parent, &available); QCOMPARE(dialogStub->itemModel, &model); QCOMPARE(QPersistentModelIndex(pagesView->currentIndex()), dialogStub->index); } }; ZANSHIN_TEST_MAIN(AvailablePagesViewTest) #include "availablepagesviewtest.moc" diff --git a/tests/units/widgets/availablesourcesviewtest.cpp b/tests/units/widgets/availablesourcesviewtest.cpp index 48572c3b..ffb110b3 100644 --- a/tests/units/widgets/availablesourcesviewtest.cpp +++ b/tests/units/widgets/availablesourcesviewtest.cpp @@ -1,241 +1,241 @@ /* 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 #include #include #include #include #include #include #include #include "presentation/metatypes.h" #include "presentation/querytreemodelbase.h" #include "widgets/availablesourcesview.h" #include "widgets/datasourcedelegate.h" class AvailableSourcesModelStub : public QObject { Q_OBJECT public: - explicit AvailableSourcesModelStub(QObject *parent = Q_NULLPTR) + explicit AvailableSourcesModelStub(QObject *parent = nullptr) : QObject(parent), settingsCalled(false) { } public slots: void showConfigDialog() { settingsCalled = true; } void setDefaultItem(const QModelIndex &index) { defaultIndex = index; } public: bool settingsCalled; QPersistentModelIndex defaultIndex; }; class AvailableSourcesViewTest : public QObject { Q_OBJECT private slots: void shouldHaveDefaultState() { Widgets::AvailableSourcesView available; QVERIFY(!available.model()); auto sourcesView = available.findChild(QStringLiteral("sourcesView")); QVERIFY(sourcesView); QVERIFY(sourcesView->isVisibleTo(&available)); QVERIFY(!sourcesView->header()->isVisibleTo(&available)); auto delegate = qobject_cast(sourcesView->itemDelegate()); QVERIFY(delegate); auto proxy = qobject_cast(sourcesView->model()); QVERIFY(proxy); QVERIFY(proxy->dynamicSortFilter()); QCOMPARE(proxy->sortColumn(), 0); QCOMPARE(proxy->sortOrder(), Qt::AscendingOrder); auto actionBar = available.findChild(QStringLiteral("actionBar")); QVERIFY(actionBar); QVERIFY(actionBar->isVisibleTo(&available)); auto defaultAction = available.findChild(QStringLiteral("defaultAction")); QVERIFY(defaultAction); auto settingsAction = available.findChild(QStringLiteral("settingsAction")); QVERIFY(settingsAction); auto actions = available.globalActions(); QCOMPARE(actions.value(QStringLiteral("options_configure")), settingsAction); } void shouldDisplayListFromPageModel() { // GIVEN QStringListModel model(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") ); QObject stubPagesModel; stubPagesModel.setProperty("sourceListModel", QVariant::fromValue(static_cast(&model))); Widgets::AvailableSourcesView available; auto sourcesView = available.findChild(QStringLiteral("sourcesView")); QVERIFY(sourcesView); auto proxy = qobject_cast(sourcesView->model()); QVERIFY(proxy); QVERIFY(!proxy->sourceModel()); // WHEN available.setModel(&stubPagesModel); QTest::qWait(10); // THEN QCOMPARE(proxy->sourceModel(), &model); } void shouldNotCrashWithNullModel() { // GIVEN QStringListModel model(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") ); QObject stubPagesModel; stubPagesModel.setProperty("sourceListModel", QVariant::fromValue(static_cast(&model))); Widgets::AvailableSourcesView available; auto sourcesView = available.findChild(QStringLiteral("sourcesView")); QVERIFY(sourcesView); auto proxy = qobject_cast(sourcesView->model()); QVERIFY(proxy); QVERIFY(!proxy->sourceModel()); available.setModel(&stubPagesModel); QTest::qWait(10); // WHEN - available.setModel(Q_NULLPTR); + available.setModel(nullptr); QTest::qWait(10); // THEN QVERIFY(!available.isEnabled()); QVERIFY(!proxy->sourceModel()); } void shouldSetSelectedAsDefault() { // GIVEN QStandardItemModel model; auto itemA = new QStandardItem(QStringLiteral("A")); auto sourceA = Domain::DataSource::Ptr::create(); sourceA->setContentTypes(Domain::DataSource::Tasks); itemA->setData(QVariant::fromValue(sourceA), Presentation::QueryTreeModelBase::ObjectRole); model.appendRow(itemA); auto itemB = new QStandardItem(QStringLiteral("B")); auto sourceB = Domain::DataSource::Ptr::create(); sourceB->setContentTypes(Domain::DataSource::Tasks); itemB->setData(QVariant::fromValue(sourceB), Presentation::QueryTreeModelBase::ObjectRole); model.appendRow(itemB); auto itemC = new QStandardItem(QStringLiteral("C")); auto sourceC = Domain::DataSource::Ptr::create(); sourceC->setContentTypes(Domain::DataSource::NoContent); itemC->setData(QVariant::fromValue(sourceC), Presentation::QueryTreeModelBase::ObjectRole); model.appendRow(itemC); AvailableSourcesModelStub stubSourcesModel; stubSourcesModel.setProperty("sourceListModel", QVariant::fromValue(static_cast(&model))); Widgets::AvailableSourcesView available; available.setModel(&stubSourcesModel); auto sourcesView = available.findChild(QStringLiteral("sourcesView")); QVERIFY(sourcesView); auto proxy = qobject_cast(sourcesView->model()); QVERIFY(proxy); auto defaultAction = available.findChild(QStringLiteral("defaultAction")); QVERIFY(defaultAction); // WHEN auto selectedIndex = QPersistentModelIndex(model.index(1, 0)); sourcesView->setCurrentIndex(proxy->mapFromSource(selectedIndex)); // THEN QVERIFY(defaultAction->isEnabled()); // WHEN defaultAction->trigger(); // THEN QCOMPARE(stubSourcesModel.defaultIndex, selectedIndex); // WHEN sourcesView->selectionModel()->clear(); // THEN QVERIFY(!defaultAction->isEnabled()); // WHEN selectedIndex = QPersistentModelIndex(model.index(2, 0)); sourcesView->setCurrentIndex(proxy->mapFromSource(selectedIndex)); // THEN QVERIFY(!defaultAction->isEnabled()); } void shouldInvokeModelSettingsDialog() { // GIVEN AvailableSourcesModelStub stubSourcesModel; QVERIFY(!stubSourcesModel.settingsCalled); Widgets::AvailableSourcesView available; available.setModel(&stubSourcesModel); auto settingsAction = available.findChild(QStringLiteral("settingsAction")); QVERIFY(settingsAction); // WHEN settingsAction->trigger(); // THEN QVERIFY(stubSourcesModel.settingsCalled); } }; ZANSHIN_TEST_MAIN(AvailableSourcesViewTest) #include "availablesourcesviewtest.moc" diff --git a/tests/units/widgets/editorviewtest.cpp b/tests/units/widgets/editorviewtest.cpp index b6cb1e38..8c9a5be8 100644 --- a/tests/units/widgets/editorviewtest.cpp +++ b/tests/units/widgets/editorviewtest.cpp @@ -1,646 +1,646 @@ /* 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 #include #include #include #include #include #include #include "domain/task.h" #include "widgets/editorview.h" #include "kdateedit.h" class EditorModelStub : public QObject { Q_OBJECT public: EditorModelStub() { setProperty("editingInProgress", false); setProperty("attachmentModel", QVariant::fromValue(&attachmentModel)); } void setPropertyAndSignal(const QByteArray &name, const QVariant &value) { if (property(name) == value) return; if (property("editingInProgress").toBool()) return; setProperty(name, value); if (name == "task") emit taskChanged(value.value()); else if (name == "text") emit textChanged(value.toString()); else if (name == "title") emit titleChanged(value.toString()); else if (name == "done") emit doneChanged(value.toBool()); else if (name == "startDate") emit startDateChanged(value.toDate()); else if (name == "dueDate") emit dueDateChanged(value.toDate()); else if (name == "recurrence") emit recurrenceChanged(value.value()); else qFatal("Unsupported property %s", name.constData()); } public slots: void setTask(const Domain::Task::Ptr &task) { setPropertyAndSignal("task", QVariant::fromValue(task)); } void setTitle(const QString &title) { setPropertyAndSignal("title", title); } void setText(const QString &text) { setPropertyAndSignal("text", text); } void setDone(bool done) { setPropertyAndSignal("done", done); } void setStartDate(const QDate &start) { setPropertyAndSignal("startDate", start); } void setDueDate(const QDate &due) { setPropertyAndSignal("dueDate", due); } void setRecurrence(Domain::Task::Recurrence recurrence) { setPropertyAndSignal("recurrence", QVariant::fromValue(recurrence)); } void makeTaskAvailable() { setTask(Domain::Task::Ptr(new Domain::Task)); } void addAttachment(const QString &fileName) { auto item = new QStandardItem(fileName); attachmentModel.appendRow(QList() << item); } void removeAttachment(const QModelIndex &index) { if (index.isValid()) attachmentModel.removeRows(index.row(), 1, QModelIndex()); } signals: void taskChanged(const Domain::Task::Ptr &task); void textChanged(const QString &text); void titleChanged(const QString &title); void doneChanged(bool done); void startDateChanged(const QDate &date); void dueDateChanged(const QDate &due); void recurrenceChanged(Domain::Task::Recurrence recurrence); public: QStandardItemModel attachmentModel; }; class EditorViewTest : public QObject { Q_OBJECT public: - explicit EditorViewTest(QObject *parent = Q_NULLPTR) + explicit EditorViewTest(QObject *parent = nullptr) : QObject(parent) { qputenv("ZANSHIN_UNIT_TEST_RUN", "1"); } private slots: void shouldHaveDefaultState() { Widgets::EditorView editor; QVERIFY(!editor.isEnabled()); auto textEdit = editor.findChild(QStringLiteral("textEdit")); QVERIFY(textEdit); QVERIFY(textEdit->isVisibleTo(&editor)); auto startDateEdit = editor.findChild(QStringLiteral("startDateEdit")); QVERIFY(startDateEdit); QVERIFY(startDateEdit->isVisibleTo(&editor)); auto dueDateEdit = editor.findChild(QStringLiteral("dueDateEdit")); QVERIFY(dueDateEdit); QVERIFY(dueDateEdit->isVisibleTo(&editor)); auto recurrenceCombo = editor.findChild(QStringLiteral("recurrenceCombo")); QVERIFY(recurrenceCombo); QVERIFY(recurrenceCombo->isVisibleTo(&editor)); auto doneButton = editor.findChild(QStringLiteral("doneButton")); QVERIFY(doneButton); QVERIFY(doneButton->isVisibleTo(&editor)); auto attachmentList = editor.findChild(QStringLiteral("attachmentList")); QVERIFY(attachmentList); QVERIFY(attachmentList->isVisibleTo(&editor)); auto addAttachmentButton = editor.findChild(QStringLiteral("addAttachmentButton")); QVERIFY(addAttachmentButton); QVERIFY(addAttachmentButton->isVisibleTo(&editor)); auto removeAttachmentButton = editor.findChild(QStringLiteral("removeAttachmentButton")); QVERIFY(removeAttachmentButton); QVERIFY(removeAttachmentButton->isVisibleTo(&editor)); } void shouldNotCrashForNullModel() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.setTitle(QStringLiteral("Foo")); model.setText(QStringLiteral("Bar")); editor.setModel(&model); auto textEdit = editor.findChild(QStringLiteral("textEdit")); QVERIFY(textEdit); auto startDateEdit = editor.findChild(QStringLiteral("startDateEdit")); QVERIFY(startDateEdit); auto dueDateEdit = editor.findChild(QStringLiteral("dueDateEdit")); QVERIFY(dueDateEdit); auto recurrenceCombo = editor.findChild(QStringLiteral("recurrenceCombo")); QVERIFY(recurrenceCombo); auto doneButton = editor.findChild(QStringLiteral("doneButton")); QVERIFY(doneButton); auto attachmentList = editor.findChild(QStringLiteral("attachmentList")); QVERIFY(attachmentList); QCOMPARE(attachmentList->model(), &model.attachmentModel); auto addAttachmentButton = editor.findChild(QStringLiteral("addAttachmentButton")); QVERIFY(addAttachmentButton); auto removeAttachmentButton = editor.findChild(QStringLiteral("removeAttachmentButton")); QVERIFY(removeAttachmentButton); // WHEN - editor.setModel(Q_NULLPTR); + editor.setModel(nullptr); // THEN QVERIFY(!editor.isEnabled()); QVERIFY(textEdit->toPlainText().isEmpty()); QVERIFY(!startDateEdit->isVisibleTo(&editor)); QVERIFY(!dueDateEdit->isVisibleTo(&editor)); QVERIFY(!recurrenceCombo->isVisibleTo(&editor)); QVERIFY(!doneButton->isVisibleTo(&editor)); QVERIFY(!attachmentList->isVisibleTo(&editor)); QVERIFY(attachmentList->model() == nullptr); QVERIFY(!addAttachmentButton->isVisibleTo(&editor)); QVERIFY(!removeAttachmentButton->isVisibleTo(&editor)); } void shouldBeEnabledOnlyWhenAnTaskIsAvailable() { // GIVEN Widgets::EditorView editor; EditorModelStub model; // WHEN editor.setModel(&model); // THEN QVERIFY(!editor.isEnabled()); // WHEN // like model.makeTaskAvailable() does: Domain::Task::Ptr task(new Domain::Task); model.setPropertyAndSignal("task", QVariant::fromValue(task)); // THEN QVERIFY(editor.isEnabled()); // WHEN model.setPropertyAndSignal("task", QVariant::fromValue(Domain::Task::Ptr())); // THEN QVERIFY(!editor.isEnabled()); // GIVEN EditorModelStub model2; model2.setPropertyAndSignal("task", QVariant::fromValue(task)); // WHEN editor.setModel(&model2); // THEN QVERIFY(editor.isEnabled()); } void shouldDisplayModelProperties() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); model.setProperty("title", "My title"); model.setProperty("text", "\nMy text"); model.setProperty("startDate", QDate::currentDate()); model.setProperty("dueDate", QDate::currentDate().addDays(2)); model.setProperty("recurrence", QVariant::fromValue(Domain::Task::RecursWeekly)); model.setProperty("done", true); auto textEdit = editor.findChild(QStringLiteral("textEdit")); auto startDateEdit = editor.findChild(QStringLiteral("startDateEdit")); auto dueDateEdit = editor.findChild(QStringLiteral("dueDateEdit")); auto recurrenceCombo = editor.findChild(QStringLiteral("recurrenceCombo")); auto doneButton = editor.findChild(QStringLiteral("doneButton")); // WHEN editor.setModel(&model); // THEN QCOMPARE(textEdit->toPlainText(), QString(model.property("title").toString() + '\n' + model.property("text").toString())); QCOMPARE(startDateEdit->date(), model.property("startDate").toDate()); QCOMPARE(dueDateEdit->date(), model.property("dueDate").toDate()); QCOMPARE(recurrenceCombo->currentData().value(), model.property("recurrence").value()); QCOMPARE(doneButton->isChecked(), model.property("done").toBool()); } void shouldNotReactToChangesWhileEditing() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); model.setProperty("title", "My title"); model.setProperty("text", "\nMy text"); model.setProperty("startDate", QDate::currentDate()); model.setProperty("dueDate", QDate::currentDate().addDays(2)); model.setProperty("recurrence", QVariant::fromValue(Domain::Task::RecursWeekly)); model.setProperty("done", true); editor.setModel(&model); auto textEdit = editor.findChild(QStringLiteral("textEdit")); auto startDateEdit = editor.findChild(QStringLiteral("startDateEdit")); auto dueDateEdit = editor.findChild(QStringLiteral("dueDateEdit")); auto recurrenceCombo = editor.findChild(QStringLiteral("recurrenceCombo")); auto doneButton = editor.findChild(QStringLiteral("doneButton")); editor.setModel(&model); // WHEN editor.show(); QVERIFY(QTest::qWaitForWindowShown(&editor)); editor.activateWindow(); textEdit->setFocus(); model.setTitle("New title"); model.setText("New text"); startDateEdit->setFocus(); model.setStartDate(QDate::currentDate().addDays(1)); dueDateEdit->setFocus(); model.setDueDate(QDate::currentDate().addDays(3)); recurrenceCombo->setFocus(); model.setRecurrence(Domain::Task::RecursDaily); doneButton->setFocus(); model.setDone(false); // THEN (nothing changed) QCOMPARE(textEdit->toPlainText(), QStringLiteral("My title\n\nMy text")); QCOMPARE(startDateEdit->date(), QDate::currentDate()); QCOMPARE(dueDateEdit->date(), QDate::currentDate().addDays(2)); QCOMPARE(recurrenceCombo->currentData().value(), Domain::Task::RecursWeekly); QVERIFY(doneButton->isChecked()); } void shouldReactToTitleChanges() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); model.setProperty("title", "My title"); model.setProperty("text", "\nMy text"); model.setProperty("startDate", QDate::currentDate()); model.setProperty("dueDate", QDate::currentDate().addDays(2)); model.setProperty("done", true); editor.setModel(&model); auto textEdit = editor.findChild(QStringLiteral("textEdit")); // WHEN model.setPropertyAndSignal("title", "New title"); // THEN QCOMPARE(textEdit->toPlainText(), QString(model.property("title").toString() + '\n' + model.property("text").toString())); } void shouldReactToTextChanges() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); model.setProperty("title", "My title"); model.setProperty("text", "\nMy text"); model.setProperty("startDate", QDate::currentDate()); model.setProperty("dueDate", QDate::currentDate().addDays(2)); model.setProperty("done", true); editor.setModel(&model); auto textEdit = editor.findChild(QStringLiteral("textEdit")); // WHEN model.setPropertyAndSignal("text", "\nNew text"); // THEN QCOMPARE(textEdit->toPlainText(), QString(model.property("title").toString() + '\n' + model.property("text").toString())); } void shouldApplyTextEditChanges_data() { QTest::addColumn("plainText"); QTest::addColumn("expectedTitle"); QTest::addColumn("expectedText"); QTest::newRow("nominal case") << "Title\n\nText" << "Title" << "\nText"; QTest::newRow("single line") << "Title" << "Title" << ""; } void shouldApplyTextEditChanges() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); editor.setModel(&model); auto textEdit = editor.findChild(QStringLiteral("textEdit")); // WHEN QFETCH(QString, plainText); textEdit->setPlainText(plainText); // THEN QFETCH(QString, expectedTitle); QCOMPARE(model.property("title").toString(), expectedTitle); QFETCH(QString, expectedText); QCOMPARE(model.property("text").toString(), expectedText); } void shouldReactToDoneChanges() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); model.setProperty("title", "My title"); model.setProperty("text", "\nMy text"); model.setProperty("startDate", QDate::currentDate()); model.setProperty("dueDate", QDate::currentDate().addDays(2)); model.setProperty("done", false); editor.setModel(&model); auto doneButton = editor.findChild(QStringLiteral("doneButton")); QVERIFY(!doneButton->isChecked()); // WHEN model.setPropertyAndSignal("done", true); // THEN QVERIFY(doneButton->isChecked()); } void shouldApplyDoneButtonChanges() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); editor.setModel(&model); auto doneButton = editor.findChild(QStringLiteral("doneButton")); // WHEN doneButton->setChecked(true); // THEN QCOMPARE(model.property("done").toBool(), true); } void shouldReactToStartDateChanges() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); model.setProperty("title", "My title"); model.setProperty("text", "\nMy text"); model.setProperty("startDate", QDate::currentDate()); model.setProperty("dueDate", QDate::currentDate().addDays(2)); model.setProperty("done", false); editor.setModel(&model); auto startDateEdit = editor.findChild(QStringLiteral("startDateEdit")); // WHEN model.setPropertyAndSignal("startDate", QDate::currentDate().addDays(-2)); // THEN QCOMPARE(startDateEdit->date(), model.property("startDate").toDate()); } void shouldApplyStartDateEditChanges() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); editor.setModel(&model); auto startDateEdit = editor.findChild(QStringLiteral("startDateEdit")); auto today = QDate::currentDate(); // WHEN startDateEdit->setEditText(today.toString(QStringLiteral("dd/MM/yyyy"))); QTest::keyClick(startDateEdit, Qt::Key_Enter); // THEN const QDate newStartDate = model.property("startDate").toDate(); QCOMPARE(newStartDate, today); } void shouldReactToDueDateChanges() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.setProperty("title", "My title"); model.setProperty("text", "\nMy text"); model.setProperty("startDate", QDate::currentDate()); model.setProperty("dueDate", QDate::currentDate().addDays(2)); model.setProperty("done", false); editor.setModel(&model); auto dueDateEdit = editor.findChild(QStringLiteral("dueDateEdit")); // WHEN model.setPropertyAndSignal("dueDate", QDate::currentDate().addDays(-2)); // THEN QCOMPARE(dueDateEdit->date(), model.property("dueDate").toDate()); } void shouldApplyDueDateEditChanges() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); editor.setModel(&model); auto dueDateEdit = editor.findChild(QStringLiteral("dueDateEdit")); auto today = QDate::currentDate(); // WHEN QVERIFY(dueDateEdit->isEnabled()); dueDateEdit->setEditText(today.toString(QStringLiteral("dd/MM/yyyy"))); QTest::keyClick(dueDateEdit, Qt::Key_Enter); // THEN QCOMPARE(model.property("dueDate").toDate(), today); } void shouldApplyStartTodayChanges() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); editor.setModel(&model); QAbstractButton *startTodayButton = editor.findChild(QStringLiteral("startTodayButton")); QVERIFY(startTodayButton); auto startDateEdit = editor.findChild(QStringLiteral("startDateEdit")); auto today = QDate::currentDate(); // WHEN QVERIFY(startTodayButton->isEnabled()); startTodayButton->click(); // THEN QCOMPARE(startDateEdit->currentText(), today.toString(QStringLiteral("dd/MM/yyyy"))); QCOMPARE(model.property("startDate").toDate(), today); } void shouldReactToRecurrenceChanges() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); model.setProperty("title", "My title"); model.setProperty("text", "\nMy text"); model.setProperty("startDate", QDate::currentDate()); model.setProperty("dueDate", QDate::currentDate().addDays(2)); model.setProperty("recurrence", QVariant::fromValue(Domain::Task::RecursWeekly)); model.setProperty("done", false); editor.setModel(&model); auto recurrenceCombo = editor.findChild(QStringLiteral("recurrenceCombo")); // WHEN model.setPropertyAndSignal("recurrence", Domain::Task::RecursMonthly); // THEN QCOMPARE(recurrenceCombo->currentData().value(), model.property("recurrence").value()); } void shouldApplyRecurrenceComboChanges() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); editor.setModel(&model); auto recurrenceCombo = editor.findChild(QStringLiteral("recurrenceCombo")); // WHEN recurrenceCombo->setCurrentIndex(2); // Weekly // THEN QCOMPARE(model.property("recurrence").value(), Domain::Task::RecursWeekly); } void shouldAddAttachments() { // GIVEN Widgets::EditorView editor; editor.setRequestFileNameFunction([](QWidget*) { return "/tmp/foobar"; }); EditorModelStub model; model.makeTaskAvailable(); editor.setModel(&model); auto addAttachmentButton = editor.findChild(QStringLiteral("addAttachmentButton")); // WHEN addAttachmentButton->click(); // THEN QCOMPARE(model.attachmentModel.rowCount(), 1); QCOMPARE(model.attachmentModel.data(model.attachmentModel.index(0, 0)).toString(), QStringLiteral("/tmp/foobar")); } void shouldRemoveAttachments() { // GIVEN Widgets::EditorView editor; EditorModelStub model; model.makeTaskAvailable(); model.addAttachment("/tmp/foo"); model.addAttachment("/tmp/bar"); editor.setModel(&model); auto attachmentList = editor.findChild(QStringLiteral("attachmentList")); auto removeAttachmentButton = editor.findChild(QStringLiteral("removeAttachmentButton")); // THEN QVERIFY(!removeAttachmentButton->isEnabled()); // WHEN attachmentList->selectionModel()->select(model.attachmentModel.index(0, 0), QItemSelectionModel::ClearAndSelect); // THEN QVERIFY(removeAttachmentButton->isEnabled()); // WHEN removeAttachmentButton->click(); // THEN QCOMPARE(model.attachmentModel.rowCount(), 1); QCOMPARE(model.attachmentModel.data(model.attachmentModel.index(0, 0)).toString(), QStringLiteral("/tmp/bar")); } }; ZANSHIN_TEST_MAIN(EditorViewTest) #include "editorviewtest.moc" diff --git a/tests/units/widgets/newprojectdialogtest.cpp b/tests/units/widgets/newprojectdialogtest.cpp index 767d81ea..26c1da60 100644 --- a/tests/units/widgets/newprojectdialogtest.cpp +++ b/tests/units/widgets/newprojectdialogtest.cpp @@ -1,264 +1,264 @@ /* 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 #include #include #include #include #include #include #include "presentation/querytreemodelbase.h" #include "widgets/newprojectdialog.h" class UserInputSimulator : public QObject { Q_OBJECT public: - explicit UserInputSimulator(QObject *parent = Q_NULLPTR) - : QObject(parent), dialog(Q_NULLPTR), reject(false), sourceComboIndex(-1) {} + explicit UserInputSimulator(QObject *parent = nullptr) + : QObject(parent), dialog(nullptr), reject(false), sourceComboIndex(-1) {} void exec() { Q_ASSERT(dialog); QTimer::singleShot(50, Qt::PreciseTimer, this, &UserInputSimulator::onTimeout); dialog->exec(); } private slots: void onTimeout() { if (!nameInput.isEmpty()) { auto nameEdit = dialog->findChild(QStringLiteral("nameEdit")); QTest::keyClicks(nameEdit, nameInput); } auto sourceCombo = dialog->findChild(QStringLiteral("sourceCombo")); sourceCombo->setCurrentIndex(sourceComboIndex); auto buttonBox = dialog->findChild(QStringLiteral("buttonBox")); if (reject) buttonBox->button(QDialogButtonBox::Cancel)->click(); else buttonBox->button(QDialogButtonBox::Ok)->click(); } public: Widgets::NewProjectDialog *dialog; bool reject; QString nameInput; int sourceComboIndex; }; class NewProjectDialogTest : public QObject { Q_OBJECT private: - QStandardItem *createSourceItem(const QString &name, QStandardItem *parent = Q_NULLPTR) + QStandardItem *createSourceItem(const QString &name, QStandardItem *parent = nullptr) { auto source = Domain::DataSource::Ptr::create(); source->setName(name); auto item = new QStandardItem(name); item->setData(QVariant::fromValue(source), Presentation::QueryTreeModelBase::ObjectRole); if (parent) parent->appendRow(item); return item; } - QStandardItem *createTaskSourceItem(const QString &name, QStandardItem *parent = Q_NULLPTR) + QStandardItem *createTaskSourceItem(const QString &name, QStandardItem *parent = nullptr) { auto item = createSourceItem(name, parent); auto source = item->data(Presentation::QueryTreeModelBase::ObjectRole).value(); source->setContentTypes(Domain::DataSource::Tasks); return item; } - QStandardItem *createDefaultSourceItem(const QString &name, QStandardItem *parent = Q_NULLPTR) + QStandardItem *createDefaultSourceItem(const QString &name, QStandardItem *parent = nullptr) { auto item = createTaskSourceItem(name, parent); item->setData(true, Presentation::QueryTreeModelBase::IsDefaultRole); return item; } QStandardItemModel *createSourceModel() { auto model = new QStandardItemModel(this); auto root1 = createSourceItem(QStringLiteral("Root 1")); createSourceItem(QStringLiteral("Null"), root1); createTaskSourceItem(QStringLiteral("Task 1.1"), root1); createTaskSourceItem(QStringLiteral("Task 1.2"), root1); model->appendRow(root1); auto root2 = createSourceItem(QStringLiteral("Root 2")); createDefaultSourceItem(QStringLiteral("Task 2.1"), root2); createTaskSourceItem(QStringLiteral("Task 2.2"), root2); model->appendRow(root2); return model; } private slots: void shouldHaveDefaultState() { Widgets::NewProjectDialog dialog; QVERIFY(dialog.name().isEmpty()); QVERIFY(dialog.dataSource().isNull()); auto nameEdit = dialog.findChild(QStringLiteral("nameEdit")); QVERIFY(nameEdit); QVERIFY(nameEdit->isVisibleTo(&dialog)); auto sourceCombo = dialog.findChild(QStringLiteral("sourceCombo")); QVERIFY(sourceCombo); QVERIFY(sourceCombo->isVisibleTo(&dialog)); auto buttonBox = dialog.findChild(QStringLiteral("buttonBox")); QVERIFY(buttonBox); QVERIFY(buttonBox->isVisibleTo(&dialog)); QVERIFY(buttonBox->button(QDialogButtonBox::Ok)); QVERIFY(buttonBox->button(QDialogButtonBox::Cancel)); } void shouldPositionDefaultProperties() { // GIVEN Widgets::NewProjectDialog dialog; auto sourceModel = createSourceModel(); auto sourceCombo = dialog.findChild(QStringLiteral("sourceCombo")); // WHEN dialog.setDataSourcesModel(sourceModel); // THEN QCOMPARE(sourceCombo->currentIndex(), 2); QCOMPARE(sourceCombo->currentText(), QStringLiteral("Root 2 / Task 2.1")); } void shouldProvideUserInputWhenAccepted() { // GIVEN Widgets::NewProjectDialog dialog; auto sourceModel = createSourceModel(); dialog.setDataSourcesModel(sourceModel); UserInputSimulator userInput; userInput.dialog = &dialog; userInput.sourceComboIndex = 1; userInput.nameInput = QStringLiteral("name"); auto expectedSource = sourceModel->item(0) ->child(2) ->data(Presentation::QueryTreeModelBase::ObjectRole) .value(); // WHEN userInput.exec(); // THEN QCOMPARE(dialog.name(), userInput.nameInput); QVERIFY(dialog.dataSource()); QCOMPARE(dialog.dataSource(), expectedSource); } void shouldNotProvideUserInputWhenReject() { // GIVEN Widgets::NewProjectDialog dialog; auto sourceModel = createSourceModel(); dialog.setDataSourcesModel(sourceModel); UserInputSimulator userInput; userInput.dialog = &dialog; userInput.sourceComboIndex = 1; userInput.nameInput = QStringLiteral("name"); userInput.reject = true; // WHEN userInput.exec(); // THEN QCOMPARE(dialog.name(), QString()); QCOMPARE(dialog.dataSource(), Domain::DataSource::Ptr()); } void shouldNotAllowEmptyName() { // GIVEN Widgets::NewProjectDialog dialog; auto sourceModel = createSourceModel(); dialog.setDataSourcesModel(sourceModel); UserInputSimulator userInput; userInput.dialog = &dialog; userInput.sourceComboIndex = 0; userInput.nameInput = QString(); userInput.reject = true; // WHEN userInput.exec(); // THEN auto buttonOk = dialog.findChild(QStringLiteral("buttonBox"))->button(QDialogButtonBox::Ok); QVERIFY(!buttonOk->isEnabled()); QCOMPARE(dialog.name(), QString()); QCOMPARE(dialog.dataSource(), Domain::DataSource::Ptr()); } void shouldNotAllowNoSelectedSource() { // GIVEN Widgets::NewProjectDialog dialog; auto sourceModel = createSourceModel(); dialog.setDataSourcesModel(sourceModel); UserInputSimulator userInput; userInput.dialog = &dialog; userInput.sourceComboIndex = -1; userInput.nameInput = QStringLiteral("name"); userInput.reject = true; // WHEN userInput.exec(); // THEN auto buttonOk = dialog.findChild(QStringLiteral("buttonBox"))->button(QDialogButtonBox::Ok); QVERIFY(!buttonOk->isEnabled()); QCOMPARE(dialog.name(), QString()); QCOMPARE(dialog.dataSource(), Domain::DataSource::Ptr()); } }; ZANSHIN_TEST_MAIN(NewProjectDialogTest) #include "newprojectdialogtest.moc" diff --git a/tests/units/widgets/pageviewtest.cpp b/tests/units/widgets/pageviewtest.cpp index 59a07e25..7b347bd7 100644 --- a/tests/units/widgets/pageviewtest.cpp +++ b/tests/units/widgets/pageviewtest.cpp @@ -1,899 +1,899 @@ /* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "domain/task.h" #include "presentation/taskfilterproxymodel.h" #include "presentation/metatypes.h" #include "presentation/querytreemodelbase.h" #include "widgets/filterwidget.h" #include "widgets/itemdelegate.h" #include "widgets/pageview.h" #include "messageboxstub.h" class PageModelStub : public QObject { Q_OBJECT Q_PROPERTY(QAbstractItemModel* centralListModel READ centralListModel) public: void setProxyModel(QAbstractProxyModel *proxy) { proxyModel = proxy; proxyModel->setSourceModel(&itemModel); } QAbstractItemModel *centralListModel() { if (proxyModel) return proxyModel; return &itemModel; } - QStandardItem *addStubItem(const QString &title, QStandardItem *parentItem = Q_NULLPTR) + QStandardItem *addStubItem(const QString &title, QStandardItem *parentItem = nullptr) { QStandardItem *item = new QStandardItem; item->setData(title, Qt::DisplayRole); if (!parentItem) itemModel.appendRow(item); else parentItem->appendRow(item); taskNames << title; return item; } - Domain::Task::Ptr addTaskItem(const QString &title, QStandardItem *parentItem = Q_NULLPTR) + Domain::Task::Ptr addTaskItem(const QString &title, QStandardItem *parentItem = nullptr) { auto item = addStubItem(title, parentItem); auto task = Domain::Task::Ptr::create(); task->setTitle(title); item->setData(QVariant::fromValue(task), Presentation::QueryTreeModelBase::ObjectRole); return task; } void addStubItems(const QStringList &list) { foreach (const QString &title, list) { addStubItem(title); } } public slots: void addItem(const QString &name, const QModelIndex &parentIndex) { taskNames << name; parentIndices << parentIndex; } void removeItem(const QModelIndex &index) { removedIndices << index; } void promoteItem(const QModelIndex &index) { promotedIndices << index; } public: QStringList taskNames; QList parentIndices; QList removedIndices; QList promotedIndices; QStandardItemModel itemModel; QAbstractProxyModel *proxyModel = nullptr; }; class RunningTaskModelStub : public Presentation::RunningTaskModelInterface { Q_OBJECT public: Domain::Task::Ptr runningTask() const override { return m_runningTask; } void setRunningTask(const Domain::Task::Ptr &task) override { m_runningTask = task; } void taskDeleted(const Domain::Task::Ptr &task) override { m_deletedTask = task; } void stopTask() override {} void doneTask() override {} private: Domain::Task::Ptr m_runningTask; Domain::Task::Ptr m_deletedTask; }; class PageViewTest : public QObject { Q_OBJECT private: KConfigGroup configGroup() { return KConfigGroup(KSharedConfig::openConfig(), "General"); } private slots: void shouldHaveDefaultState() { Widgets::PageView page; QCOMPARE(page.contentsMargins(), QMargins(0, 0, 0, 0)); QCOMPARE(page.layout()->contentsMargins(), QMargins(0, 0, 0, 3)); auto messageWidget = page.findChild(QStringLiteral("messageWidget")); QVERIFY(messageWidget); QVERIFY(!messageWidget->isVisibleTo(&page)); QVERIFY(!messageWidget->isCloseButtonVisible()); QVERIFY(messageWidget->wordWrap()); QVERIFY(messageWidget->text().isEmpty()); QVERIFY(messageWidget->icon().isNull()); QCOMPARE(messageWidget->messageType(), KMessageWidget::Error); QVERIFY(!messageWidget->isShowAnimationRunning()); QVERIFY(!messageWidget->isHideAnimationRunning()); auto centralView = page.findChild(QStringLiteral("centralView")); QVERIFY(centralView); QVERIFY(centralView->isVisibleTo(&page)); QVERIFY(!centralView->header()->isVisibleTo(&page)); QVERIFY(qobject_cast(centralView->itemDelegate())); QVERIFY(centralView->alternatingRowColors()); QCOMPARE(centralView->dragDropMode(), QTreeView::DragDrop); auto filter = page.findChild(QStringLiteral("filterWidget")); QVERIFY(filter); QVERIFY(!filter->isVisibleTo(&page)); QVERIFY(filter->proxyModel()); QCOMPARE(filter->proxyModel(), centralView->model()); QLineEdit *quickAddEdit = page.findChild(QStringLiteral("quickAddEdit")); QVERIFY(quickAddEdit); QVERIFY(quickAddEdit->isVisibleTo(&page)); QVERIFY(quickAddEdit->text().isEmpty()); QCOMPARE(quickAddEdit->placeholderText(), i18n("Type and press enter to add a task")); auto addAction = page.findChild(QStringLiteral("addItemAction")); QVERIFY(addAction); auto cancelAddAction = page.findChild(QStringLiteral("cancelAddItemAction")); QVERIFY(cancelAddAction); auto removeAction = page.findChild(QStringLiteral("removeItemAction")); QVERIFY(removeAction); auto promoteAction = page.findChild(QStringLiteral("promoteItemAction")); QVERIFY(promoteAction); auto filterAction = page.findChild(QStringLiteral("filterViewAction")); QVERIFY(filterAction); QVERIFY(filterAction->isCheckable()); QVERIFY(!filterAction->isChecked()); auto futureAction = page.findChild(QStringLiteral("futureViewAction")); QVERIFY(futureAction); QVERIFY(futureAction->isCheckable()); QVERIFY(futureAction->isChecked()); auto runTaskAction = page.findChild(QStringLiteral("runTaskAction")); QVERIFY(runTaskAction); QVERIFY(!runTaskAction->isEnabled()); auto actions = page.globalActions(); QCOMPARE(actions.value(QStringLiteral("page_view_add")), addAction); QCOMPARE(actions.value(QStringLiteral("page_view_remove")), removeAction); QCOMPARE(actions.value(QStringLiteral("page_view_promote")), promoteAction); QCOMPARE(actions.value(QStringLiteral("page_view_filter")), filterAction); QCOMPARE(actions.value(QStringLiteral("page_view_future")), futureAction); QCOMPARE(actions.value(QStringLiteral("page_run_task")), runTaskAction); } void shouldDisplayListFromPageModel() { // GIVEN QStandardItemModel model; QObject stubPageModel; stubPageModel.setProperty("centralListModel", QVariant::fromValue(static_cast(&model))); Widgets::PageView page; auto centralView = page.findChild(QStringLiteral("centralView")); QVERIFY(centralView); auto proxyModel = qobject_cast(centralView->model()); QVERIFY(proxyModel); QVERIFY(!proxyModel->sourceModel()); // WHEN page.setModel(&stubPageModel); // THEN QCOMPARE(page.model(), &stubPageModel); QVERIFY(page.isEnabled()); QCOMPARE(proxyModel->sourceModel(), &model); } void shouldNotCrashWithNullModel() { // GIVEN QStandardItemModel model; QObject stubPageModel; stubPageModel.setProperty("centralListModel", QVariant::fromValue(static_cast(&model))); Widgets::PageView page; page.setModel(&stubPageModel); auto centralView = page.findChild(QStringLiteral("centralView")); QVERIFY(centralView); auto proxyModel = qobject_cast(centralView->model()); QVERIFY(proxyModel); QCOMPARE(proxyModel->sourceModel(), &model); // WHEN - page.setModel(Q_NULLPTR); + page.setModel(nullptr); // THEN QVERIFY(!page.model()); QVERIFY(!page.isEnabled()); QVERIFY(!proxyModel->sourceModel()); } void shouldManageFocusThroughActions() { // GIVEN Widgets::PageView page; auto centralView = page.findChild(QStringLiteral("centralView")); auto quickAddEdit = page.findChild(QStringLiteral("quickAddEdit")); auto filter = page.findChild(QStringLiteral("filterWidget")); auto filterEdit = filter->findChild(); QVERIFY(filterEdit); page.show(); QVERIFY(QTest::qWaitForWindowShown(&page)); centralView->setFocus(); QVERIFY(centralView->hasFocus()); QVERIFY(!quickAddEdit->hasFocus()); QVERIFY(!filter->isVisibleTo(&page)); QVERIFY(!filterEdit->hasFocus()); auto addAction = page.findChild(QStringLiteral("addItemAction")); auto cancelAddAction = page.findChild(QStringLiteral("cancelAddItemAction")); auto filterAction = page.findChild(QStringLiteral("filterViewAction")); // WHEN addAction->trigger(); // THEN QVERIFY(!centralView->hasFocus()); QVERIFY(quickAddEdit->hasFocus()); QVERIFY(!filter->isVisibleTo(&page)); QVERIFY(!filterEdit->hasFocus()); // WHEN cancelAddAction->trigger(); // THEN QVERIFY(centralView->hasFocus()); QVERIFY(!quickAddEdit->hasFocus()); QVERIFY(!filter->isVisibleTo(&page)); QVERIFY(!filterEdit->hasFocus()); // WHEN filterAction->trigger(); // THEN QVERIFY(!centralView->hasFocus()); QVERIFY(!quickAddEdit->hasFocus()); QVERIFY(filter->isVisibleTo(&page)); QVERIFY(filterEdit->hasFocus()); // WHEN cancelAddAction->trigger(); // THEN QVERIFY(centralView->hasFocus()); QVERIFY(!quickAddEdit->hasFocus()); QVERIFY(filter->isVisibleTo(&page)); QVERIFY(!filterEdit->hasFocus()); } void shouldManageFilterVisibilityThroughAction() { // GIVEN Widgets::PageView page; auto centralView = page.findChild(QStringLiteral("centralView")); auto filter = page.findChild(QStringLiteral("filterWidget")); auto filterEdit = filter->findChild(); QVERIFY(filterEdit); page.show(); QVERIFY(QTest::qWaitForWindowShown(&page)); centralView->setFocus(); QVERIFY(centralView->hasFocus()); QVERIFY(!filter->isVisibleTo(&page)); QVERIFY(!filterEdit->hasFocus()); auto filterAction = page.findChild(QStringLiteral("filterViewAction")); // WHEN filterAction->trigger(); // THEN QVERIFY(!centralView->hasFocus()); QVERIFY(filter->isVisibleTo(&page)); QVERIFY(filterEdit->hasFocus()); // WHEN filterEdit->setText("Foo"); // THEN QCOMPARE(filterEdit->text(), QString("Foo")); // WHEN filterAction->trigger(); // THEN QVERIFY(centralView->hasFocus()); QVERIFY(!filter->isVisibleTo(&page)); QVERIFY(!filterEdit->hasFocus()); QVERIFY(filterEdit->text().isEmpty()); } void shouldManageFutureTasksVisibilityThroughAction() { // GIVEN Widgets::PageView page; auto filter = page.findChild(QStringLiteral("filterWidget")); auto filterProxy = filter->proxyModel(); QVERIFY(filterProxy); QVERIFY(filterProxy->showFutureTasks()); auto futureAction = page.findChild(QStringLiteral("futureViewAction")); // WHEN futureAction->trigger(); // THEN QVERIFY(!filterProxy->showFutureTasks()); // WHEN futureAction->trigger(); // THEN QVERIFY(filterProxy->showFutureTasks()); } void shouldStoreFutureTasksVisibilityDefaultState() { // GIVEN configGroup().deleteEntry("ShowFuture"); { Widgets::PageView page; auto futureAction = page.findChild(QStringLiteral("futureViewAction")); // THEN QVERIFY(futureAction->isChecked()); } // WHEN configGroup().writeEntry("ShowFuture", false); { Widgets::PageView page; auto futureAction = page.findChild(QStringLiteral("futureViewAction")); // THEN QVERIFY(!futureAction->isChecked()); } // WHEN configGroup().writeEntry("ShowFuture", true); { Widgets::PageView page; auto futureAction = page.findChild(QStringLiteral("futureViewAction")); // THEN QVERIFY(futureAction->isChecked()); } // WHEN configGroup().deleteEntry("ShowFuture"); { Widgets::PageView page; auto futureAction = page.findChild(QStringLiteral("futureViewAction")); // THEN QVERIFY(futureAction->isChecked()); } // WHEN Widgets::PageView page; auto futureAction = page.findChild(QStringLiteral("futureViewAction")); futureAction->trigger(); // THEN QVERIFY(configGroup().hasKey("ShowFuture")); QVERIFY(!configGroup().readEntry("ShowFuture", true)); // WHEN futureAction->trigger(); // THEN QVERIFY(configGroup().hasKey("ShowFuture")); QVERIFY(configGroup().readEntry("ShowFuture", false)); } void shouldCreateTasksWithNoParentWhenHittingReturnWithoutSelectedIndex() { // GIVEN PageModelStub stubPageModel; Widgets::PageView page; page.setModel(&stubPageModel); auto quickAddEdit = page.findChild(QStringLiteral("quickAddEdit")); // WHEN QTest::keyClick(quickAddEdit, Qt::Key_Return); // Does nothing (edit empty) QTest::keyClicks(quickAddEdit, QStringLiteral("Foo")); QTest::keyClick(quickAddEdit, Qt::Key_Return); QTest::keyClick(quickAddEdit, Qt::Key_Return); // Does nothing (edit empty) QTest::keyClicks(quickAddEdit, QStringLiteral("Bar")); QTest::keyClick(quickAddEdit, Qt::Key_Return); QTest::keyClick(quickAddEdit, Qt::Key_Return); // Does nothing (edit empty) // THEN QCOMPARE(stubPageModel.taskNames, QStringList() << QStringLiteral("Foo") << QStringLiteral("Bar")); QCOMPARE(stubPageModel.parentIndices.size(), 2); QCOMPARE(stubPageModel.parentIndices.first(), QPersistentModelIndex()); QCOMPARE(stubPageModel.parentIndices.last(), QPersistentModelIndex()); } void shouldCreateTasksWithNoParentWhenHittingReturnWithSeveralSelectedIndices() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); QPersistentModelIndex index0 = stubPageModel.itemModel.index(0, 0); QPersistentModelIndex index1 = stubPageModel.itemModel.index(1, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->select(index0, QItemSelectionModel::ClearAndSelect); centralView->selectionModel()->select(index1, QItemSelectionModel::Select); auto quickAddEdit = page.findChild(QStringLiteral("quickAddEdit")); // WHEN QTest::keyClicks(quickAddEdit, QStringLiteral("Foo")); QTest::keyClick(quickAddEdit, Qt::Key_Return); // THEN QCOMPARE(stubPageModel.taskNames, QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") << QStringLiteral("Foo")); QCOMPARE(stubPageModel.parentIndices.size(), 1); QCOMPARE(stubPageModel.parentIndices.first(), QPersistentModelIndex()); } void shouldCreateTasksWithParentWhenHittingReturnWithOneSelectedIndex() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); QPersistentModelIndex index = stubPageModel.itemModel.index(1, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); auto quickAddEdit = page.findChild(QStringLiteral("quickAddEdit")); // WHEN QTest::keyClicks(quickAddEdit, QStringLiteral("Foo")); QTest::keyClick(quickAddEdit, Qt::Key_Return); // THEN QCOMPARE(stubPageModel.taskNames, QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") << QStringLiteral("Foo")); QCOMPARE(stubPageModel.parentIndices.size(), 1); QCOMPARE(stubPageModel.parentIndices.first(), index); } void shouldDeleteItemWhenHittingTheDeleteKey() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); QPersistentModelIndex index = stubPageModel.itemModel.index(1, 0); Widgets::PageView page; page.setModel(&stubPageModel); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); centralView->setFocus(); // Needed for shortcuts to work page.show(); QVERIFY(QTest::qWaitForWindowShown(&page)); QTest::qWait(100); // WHEN QTest::keyPress(centralView, Qt::Key_Delete); // THEN QCOMPARE(stubPageModel.removedIndices.size(), 1); QCOMPARE(stubPageModel.removedIndices.first(), index); } void shouldNotTryToDeleteIfThereIsNoSelection() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); Widgets::PageView page; page.setModel(&stubPageModel); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->clearSelection(); page.findChild(QStringLiteral("quickAddEdit"))->setFocus(); // Needed for shortcuts to work page.show(); QVERIFY(QTest::qWaitForWindowShown(&page)); QTest::qWait(100); // WHEN QTest::keyPress(centralView, Qt::Key_Delete); // THEN QVERIFY(stubPageModel.removedIndices.isEmpty()); } void shouldDisplayNotificationWhenHittingTheDeleteKeyOnAnItemWithChildren() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B")); QStandardItem *parentIndex = stubPageModel.itemModel.item(1, 0); stubPageModel.addStubItem(QStringLiteral("C"), parentIndex); QPersistentModelIndex index = stubPageModel.itemModel.index(1, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto msgbox = MessageBoxStub::Ptr::create(); page.setMessageBoxInterface(msgbox); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); QVERIFY(centralView->selectionModel()->currentIndex().isValid()); centralView->setFocus(); // Needed for shortcuts to work page.show(); QVERIFY(QTest::qWaitForWindowShown(&page)); QTest::qWait(100); // WHEN QTest::keyPress(centralView, Qt::Key_Delete); // THEN QVERIFY(msgbox->called()); QCOMPARE(stubPageModel.removedIndices.size(), 1); QCOMPARE(stubPageModel.removedIndices.first(), index); } void shouldDisplayNotificationWhenHittingTheDeleteKeyOnAnItemWithHiddenChildren() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B")); QStandardItem *parentIndex = stubPageModel.itemModel.item(1, 0); stubPageModel.addStubItem(QStringLiteral("C"), parentIndex); QSortFilterProxyModel proxyModel; stubPageModel.setProxyModel(&proxyModel); proxyModel.setFilterFixedString("B"); QPersistentModelIndex index = stubPageModel.centralListModel()->index(0, 0); QCOMPARE(index.data().toString(), QLatin1String("B")); Widgets::PageView page; page.setModel(&stubPageModel); auto msgbox = MessageBoxStub::Ptr::create(); page.setMessageBoxInterface(msgbox); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); QVERIFY(centralView->selectionModel()->currentIndex().isValid()); centralView->setFocus(); // Needed for shortcuts to work page.show(); QVERIFY(QTest::qWaitForWindowShown(&page)); QTest::qWait(100); // WHEN QTest::keyPress(centralView, Qt::Key_Delete); // THEN QVERIFY(msgbox->called()); QCOMPARE(stubPageModel.removedIndices.size(), 1); QCOMPARE(stubPageModel.removedIndices.first(), index); } void shouldDeleteItemsWhenHittingTheDeleteKey() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); QPersistentModelIndex index = stubPageModel.itemModel.index(1, 0); QPersistentModelIndex index2 = stubPageModel.itemModel.index(2, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto msgbox = MessageBoxStub::Ptr::create(); page.setMessageBoxInterface(msgbox); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); centralView->selectionModel()->setCurrentIndex(index2, QItemSelectionModel::Select); centralView->setFocus(); // Needed for shortcuts to work page.show(); QVERIFY(QTest::qWaitForWindowShown(&page)); QTest::qWait(100); // WHEN QTest::keyPress(centralView, Qt::Key_Delete); // THEN QVERIFY(msgbox->called()); QCOMPARE(stubPageModel.removedIndices.size(), 2); QCOMPARE(stubPageModel.removedIndices.first(), index); QCOMPARE(stubPageModel.removedIndices.at(1), index2); } void shouldPromoteItem() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); QPersistentModelIndex index = stubPageModel.itemModel.index(1, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto promoteAction = page.findChild(QStringLiteral("promoteItemAction")); QVERIFY(promoteAction); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); centralView->setFocus(); // WHEN promoteAction->trigger(); // THEN QCOMPARE(stubPageModel.promotedIndices.size(), 1); QCOMPARE(stubPageModel.promotedIndices.first(), index); } void shouldNotTryToPromoteItemIfThereIsNoSelection() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); Widgets::PageView page; page.setModel(&stubPageModel); auto promoteAction = page.findChild(QStringLiteral("promoteItemAction")); QVERIFY(promoteAction); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->clear(); centralView->setFocus(); // WHEN promoteAction->trigger(); // THEN QVERIFY(stubPageModel.promotedIndices.isEmpty()); } void shouldClearCentralViewSelectionOnEscape() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); QPersistentModelIndex index = stubPageModel.itemModel.index(1, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); QVERIFY(!centralView->selectionModel()->selectedIndexes().isEmpty()); // WHEN QTest::keyClick(centralView, Qt::Key_Escape); // THEN QVERIFY(centralView->selectionModel()->selectedIndexes().isEmpty()); } void shouldReturnSelectedIndexes() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); auto index = stubPageModel.itemModel.index(1, 0); auto index2 = stubPageModel.itemModel.index(2, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto centralView = page.findChild(QStringLiteral("centralView")); auto filterWidget = page.findChild(QStringLiteral("filterWidget")); auto displayedModel = filterWidget->proxyModel(); auto displayedIndex = displayedModel->index(1, 0); auto displayedIndex2 = displayedModel->index(2, 0); // WHEN centralView->selectionModel()->setCurrentIndex(displayedIndex, QItemSelectionModel::ClearAndSelect); centralView->selectionModel()->setCurrentIndex(displayedIndex2, QItemSelectionModel::Select); // THEN auto selectedIndexes = page.selectedIndexes(); QCOMPARE(selectedIndexes.size(), 2); QCOMPARE(selectedIndexes.at(0), index); QCOMPARE(selectedIndexes.at(1), index2); QCOMPARE(selectedIndexes.at(0).model(), index.model()); QCOMPARE(selectedIndexes.at(1).model(), index2.model()); } void shouldDisplayMessageOnError() { // GIVEN Widgets::PageView page; page.show(); QVERIFY(QTest::qWaitForWindowShown(&page)); QTest::qWait(100); auto messageWidget = page.findChild(QStringLiteral("messageWidget")); QVERIFY(messageWidget); QVERIFY(!messageWidget->isVisibleTo(&page)); QCOMPARE(messageWidget->findChildren().size(), 1); auto closeButton = messageWidget->findChildren().first(); QVERIFY(closeButton); // WHEN page.displayErrorMessage(QStringLiteral("Foo Error")); // THEN QVERIFY(messageWidget->isVisibleTo(&page)); QVERIFY(messageWidget->isCloseButtonVisible()); QCOMPARE(messageWidget->text(), QStringLiteral("Foo Error")); QVERIFY(messageWidget->icon().isNull()); QCOMPARE(messageWidget->messageType(), KMessageWidget::Error); QVERIFY(messageWidget->isShowAnimationRunning()); QVERIFY(!messageWidget->isHideAnimationRunning()); // WHEN QTest::qWait(800); // THEN QVERIFY(!messageWidget->isShowAnimationRunning()); QVERIFY(!messageWidget->isHideAnimationRunning()); // WHEN closeButton->click(); // THEN QVERIFY(!messageWidget->isShowAnimationRunning()); QVERIFY(messageWidget->isHideAnimationRunning()); // WHEN QTest::qWait(800); // THEN QVERIFY(!messageWidget->isShowAnimationRunning()); QVERIFY(!messageWidget->isHideAnimationRunning()); } void shouldRunTask() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); auto task1 = stubPageModel.addTaskItem(QStringLiteral("Task1")); auto task2 = stubPageModel.addTaskItem(QStringLiteral("Task2")); Widgets::PageView page; page.setModel(&stubPageModel); RunningTaskModelStub stubRunningTaskModel; page.setRunningTaskModel(&stubRunningTaskModel); auto centralView = page.findChild(QStringLiteral("centralView")); QModelIndex index = stubPageModel.itemModel.index(0, 0); centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); QVERIFY(centralView->selectionModel()->currentIndex().isValid()); auto runTaskAction = page.findChild(QStringLiteral("runTaskAction")); QVERIFY(runTaskAction); QVERIFY(runTaskAction->isEnabled()); // WHEN starting the first task runTaskAction->trigger(); // THEN QCOMPARE(stubRunningTaskModel.property("runningTask").value(), task1); QCOMPARE(task1->startDate(), QDate::currentDate()); // WHEN starting the second task QModelIndex index2 = stubPageModel.itemModel.index(1, 0); centralView->selectionModel()->setCurrentIndex(index2, QItemSelectionModel::ClearAndSelect); runTaskAction->trigger(); // THEN QCOMPARE(stubRunningTaskModel.property("runningTask").value(), task2); QCOMPARE(task2->startDate(), QDate::currentDate()); } }; ZANSHIN_TEST_MAIN(PageViewTest) #include "pageviewtest.moc"