diff --git a/src/akonadi/akonadidatasourcequeries.cpp b/src/akonadi/akonadidatasourcequeries.cpp index e72ef8bf..9fc8a081 100644 --- a/src/akonadi/akonadidatasourcequeries.cpp +++ b/src/akonadi/akonadidatasourcequeries.cpp @@ -1,103 +1,103 @@ /* 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 "akonadidatasourcequeries.h" #include "akonadistoragesettings.h" using namespace Akonadi; DataSourceQueries::DataSourceQueries(const StorageInterface::Ptr &storage, const SerializerInterface::Ptr &serializer, const MonitorInterface::Ptr &monitor) : m_serializer(serializer), m_helpers(new LiveQueryHelpers(serializer, storage)), m_integrator(new LiveQueryIntegrator(serializer, monitor)) { m_integrator->addRemoveHandler([this] (const Collection &collection) { m_findChildren.remove(collection.id()); }); } bool DataSourceQueries::isDefaultSource(Domain::DataSource::Ptr source) const { auto sourceCollection = m_serializer->createCollectionFromDataSource(source); return sourceCollection == StorageSettings::instance().defaultCollection(); } void DataSourceQueries::changeDefaultSource(Domain::DataSource::Ptr source) { auto sourceCollection = m_serializer->createCollectionFromDataSource(source); StorageSettings::instance().setDefaultCollection(sourceCollection); } DataSourceQueries::DataSourceResult::Ptr DataSourceQueries::findTopLevel() const { auto fetch = m_helpers->fetchCollections(Collection::root()); auto predicate = createFetchPredicate(Collection::root()); m_integrator->bind("DataSourceQueries::findTopLevel", m_findTopLevel, fetch, predicate); return m_findTopLevel->result(); } DataSourceQueries::DataSourceResult::Ptr DataSourceQueries::findChildren(Domain::DataSource::Ptr source) const { Collection root = m_serializer->createCollectionFromDataSource(source); auto &query = m_findChildren[root.id()]; auto fetch = m_helpers->fetchCollections(root); auto predicate = createFetchPredicate(root); m_integrator->bind("DataSourceQueries::findChildren", query, fetch, predicate); return query->result(); } DataSourceQueries::DataSourceResult::Ptr DataSourceQueries::findAllSelected() const { auto fetch = m_helpers->fetchAllCollections(); auto predicate = [this] (const Akonadi::Collection &collection) { return collection.isValid() && m_serializer->isSelectedCollection(collection); }; m_integrator->bind("DataSourceQueries::findAllSelected", m_findAllSelected, fetch, predicate, Akonadi::SerializerInterface::FullPath); return m_findAllSelected->result(); } DataSourceQueries::ProjectResult::Ptr DataSourceQueries::findProjects(Domain::DataSource::Ptr source) const { Collection root = m_serializer->createCollectionFromDataSource(source); auto &query = m_findProjects[root.id()]; auto fetch = m_helpers->fetchItems(root); auto predicate = [this, root] (const Akonadi::Item &item) { return root == item.parentCollection() && m_serializer->isProjectItem(item); }; m_integrator->bind("DataSourceQueries::findProjects", query, fetch, predicate); return query->result(); } DataSourceQueries::CollectionInputQuery::PredicateFunction DataSourceQueries::createFetchPredicate(const Collection &root) const { - return [this, root] (const Collection &collection) { + return [root] (const Collection &collection) { return collection.isValid() && collection.parentCollection() == root; }; } diff --git a/src/akonadi/akonadistorage.cpp b/src/akonadi/akonadistorage.cpp index 5715f421..05237a27 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 = 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(); + return !mimeTypes.intersects(allowedMimeTypes); }), collections.end()); } if (m_type != Base) { // Replace the dummy parents in the ancestor chain with proper ones // full of juicy data std::function reconstructAncestors = [collectionsMap, &reconstructAncestors, this] (const Collection &collection) -> Collection { Q_ASSERT(collection.isValid()); if (collection == m_collection) return collection; auto parent = collection.parentCollection(); auto reconstructedParent = reconstructAncestors(collectionsMap[parent.id()]); auto result = collection; result.setParentCollection(reconstructedParent); return result; }; std::transform(collections.begin(), collections.end(), collections.begin(), reconstructAncestors); } return collections; } void setResource(const QString &resource) override { fetchScope().setResource(resource); } private: const Collection m_collection; const Type m_type; }; class ItemJob : public ItemFetchJob, public ItemFetchJobInterface { Q_OBJECT public: using ItemFetchJob::ItemFetchJob; Item::List items() const override { return ItemFetchJob::items(); } void setCollection(const Collection &collection) override { ItemFetchJob::setCollection(collection); } }; 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/domain/datasource.h b/src/domain/datasource.h index ac363b01..51d02b7f 100644 --- a/src/domain/datasource.h +++ b/src/domain/datasource.h @@ -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. */ #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, }; + Q_ENUM(ContentType) Q_DECLARE_FLAGS(ContentTypes, ContentType) 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/presentation/availablepagessortfilterproxymodel.h b/src/presentation/availablepagessortfilterproxymodel.h index d9dd1ae6..f2248ac0 100644 --- a/src/presentation/availablepagessortfilterproxymodel.h +++ b/src/presentation/availablepagessortfilterproxymodel.h @@ -1,45 +1,44 @@ /* This file is part of Zanshin 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. */ #ifndef PRESENTATION_AVAILABLEPAGESSORTFILTERPROXYMODEL_H #define PRESENTATION_AVAILABLEPAGESSORTFILTERPROXYMODEL_H #include namespace Presentation { class AvailablePagesSortFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT - Q_ENUMS(SortType) public: explicit AvailablePagesSortFilterProxyModel(QObject *parent = 0); protected: bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; }; } #endif // PRESENTATION_AVAILABLEPAGESSORTFILTERPROXYMODEL_H diff --git a/src/presentation/taskfilterproxymodel.h b/src/presentation/taskfilterproxymodel.h index 08e64534..878a2e83 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 }; + Q_ENUM(SortType) 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/tests/units/domain/livequerytest.cpp b/tests/units/domain/livequerytest.cpp index 42cedda3..8a49bf80 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] { + query.setFetchFunction([] (const Domain::LiveQueryInput::AddFunction &add) { + Utils::JobHandler::install(new FakeJob, [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 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] { + query.setFetchFunction([] (const Domain::LiveQueryInput::AddFunction &add) { + Utils::JobHandler::install(new FakeJob, [add] { add(QStringLiteral("0")); add(QStringLiteral("1")); add(QString()); add(QStringLiteral("a")); add(QStringLiteral("2")); }); }); - query.setConvertFunction([this] (const QString &s) { + query.setConvertFunction([] (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 ce1d96a0..9012eb21 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] { + query.setFetchFunction([] (const Domain::LiveQueryInput::AddFunction &add) { + Utils::JobHandler::install(new FakeJob, [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 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] { + query.setFetchFunction([] (const Domain::LiveQueryInput::AddFunction &add) { + Utils::JobHandler::install(new FakeJob, [add] { add(QStringLiteral("0")); add(QStringLiteral("1")); add(QString()); add(QStringLiteral("a")); add(QStringLiteral("2")); }); }); - query.setConvertFunction([this] (const QString &s) { + query.setConvertFunction([] (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] {}); + query.setFetchFunction([] (const Domain::LiveRelationshipQuery::AddFunction &) { + Utils::JobHandler::install(new FakeJob, [] {}); }); // 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) { + query.setFetchFunction([&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/presentation/taskfilterproxymodeltest.cpp b/tests/units/presentation/taskfilterproxymodeltest.cpp index 05b23738..fbf4e5ec 100644 --- a/tests/units/presentation/taskfilterproxymodeltest.cpp +++ b/tests/units/presentation/taskfilterproxymodeltest.cpp @@ -1,271 +1,271 @@ /* 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 "domain/task.h" #include "presentation/querytreemodelbase.h" #include "presentation/taskfilterproxymodel.h" #include "utils/datetime.h" Q_DECLARE_METATYPE(QList) class TaskFilterProxyModelTest : public QObject { Q_OBJECT private: QStandardItem *createTaskItem(const QString &title, const QString &text, const QDate &start = QDate(), const QDate &due = QDate()) const { auto task = Domain::Task::Ptr::create(); task->setTitle(title); task->setText(text); task->setStartDate(start); task->setDueDate(due); auto item = new QStandardItem; item->setData(task->title(), Qt::DisplayRole); item->setData(QVariant::fromValue(task), Presentation::QueryTreeModelBase::ObjectRole); return item; } private slots: void initTestCase() { qputenv("ZANSHIN_OVERRIDE_DATE", "2015-03-11"); } void shouldHaveDefaultState() { Presentation::TaskFilterProxyModel proxy; QVERIFY(!proxy.sourceModel()); QCOMPARE(proxy.sortColumn(), 0); QCOMPARE(proxy.sortOrder(), Qt::AscendingOrder); QCOMPARE(proxy.sortType(), Presentation::TaskFilterProxyModel::TitleSort); QCOMPARE(proxy.sortCaseSensitivity(), Qt::CaseInsensitive); QVERIFY(!proxy.showFutureTasks()); } void shouldFilterByTextAndTitle() { // GIVEN QStandardItemModel input; input.appendRow(createTaskItem(QStringLiteral("1. foo"), QStringLiteral("find me"))); input.appendRow(createTaskItem(QStringLiteral("2. Find Me"), QStringLiteral("bar"))); input.appendRow(createTaskItem(QStringLiteral("3. baz"), QStringLiteral("baz"))); Presentation::TaskFilterProxyModel output; output.setSourceModel(&input); // WHEN output.setFilterFixedString(QStringLiteral("find me")); // THEN QCOMPARE(output.rowCount(), 2); QCOMPARE(output.index(0, 0).data().toString(), QStringLiteral("1. foo")); QCOMPARE(output.index(1, 0).data().toString(), QStringLiteral("2. Find Me")); } void shouldFilterByStartDate() { // GIVEN QStandardItemModel input; const auto today = Utils::DateTime::currentDate(); - input.appendRow(createTaskItem(QStringLiteral("1. past"), QStringLiteral(""), today.addDays(-1))); - input.appendRow(createTaskItem(QStringLiteral("2. present"), QStringLiteral(""), today)); - input.appendRow(createTaskItem(QStringLiteral("3. future"), QStringLiteral(""), today.addDays(1))); - input.appendRow(createTaskItem(QStringLiteral("4. whatever"), QStringLiteral(""))); + input.appendRow(createTaskItem(QStringLiteral("1. past"), QString(), today.addDays(-1))); + input.appendRow(createTaskItem(QStringLiteral("2. present"), QString(), today)); + input.appendRow(createTaskItem(QStringLiteral("3. future"), QString(), today.addDays(1))); + input.appendRow(createTaskItem(QStringLiteral("4. whatever"), QString())); Presentation::TaskFilterProxyModel output; output.setSourceModel(&input); // WHEN output.setShowFutureTasks(true); // THEN QCOMPARE(output.rowCount(), 4); QCOMPARE(output.index(0, 0).data().toString(), QStringLiteral("1. past")); QCOMPARE(output.index(1, 0).data().toString(), QStringLiteral("2. present")); QCOMPARE(output.index(2, 0).data().toString(), QStringLiteral("3. future")); QCOMPARE(output.index(3, 0).data().toString(), QStringLiteral("4. whatever")); // WHEN output.setShowFutureTasks(false); // THEN QCOMPARE(output.rowCount(), 3); QCOMPARE(output.index(0, 0).data().toString(), QStringLiteral("1. past")); QCOMPARE(output.index(1, 0).data().toString(), QStringLiteral("2. present")); QCOMPARE(output.index(2, 0).data().toString(), QStringLiteral("4. whatever")); } void shouldKeepRowIfItHasAcceptableChildren() { // GIVEN QStandardItemModel input; input.appendRow(createTaskItem(QStringLiteral("1. foo"), QStringLiteral("find me"))); QStandardItem *item = createTaskItem(QStringLiteral("2. baz"), QStringLiteral("baz")); item->appendRow(createTaskItem(QStringLiteral("21. bar"), QStringLiteral("bar"))); item->appendRow(createTaskItem(QStringLiteral("22. find me"), QStringLiteral("foo"))); input.appendRow(item); input.appendRow(createTaskItem(QStringLiteral("3. baz"), QStringLiteral("baz"))); Presentation::TaskFilterProxyModel output; output.setSourceModel(&input); // WHEN output.setFilterFixedString(QStringLiteral("find me")); // THEN QCOMPARE(output.rowCount(), 2); QCOMPARE(output.index(0, 0).data().toString(), QStringLiteral("1. foo")); QCOMPARE(output.index(1, 0).data().toString(), QStringLiteral("2. baz")); const QModelIndex parent = output.index(1, 0); QCOMPARE(output.rowCount(parent), 1); QCOMPARE(output.index(0, 0, parent).data().toString(), QStringLiteral("22. find me")); } void shouldSortFollowingType_data() { QTest::addColumn("sortType"); QTest::addColumn("sortOrder"); QTest::addColumn>("inputItems"); QTest::addColumn("expectedOutputTitles"); QList inputItems; QStringList expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo")) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("B") << QStringLiteral("C"); QTest::newRow("title ascending") << int(Presentation::TaskFilterProxyModel::TitleSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo")) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("C") << QStringLiteral("B"); QTest::newRow("title descending") << int(Presentation::TaskFilterProxyModel::TitleSort) << int(Qt::DescendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(2014, 03, 10)) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo"), QDate(2014, 03, 01)) << createTaskItem(QStringLiteral("D"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("C") << QStringLiteral("B") << QStringLiteral("D"); QTest::newRow("start date ascending") << int(Presentation::TaskFilterProxyModel::DateSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(2014, 03, 10)) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo"), QDate(2014, 03, 01)) << createTaskItem(QStringLiteral("D"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("D") << QStringLiteral("B") << QStringLiteral("C"); QTest::newRow("start date descending") << int(Presentation::TaskFilterProxyModel::DateSort) << int(Qt::DescendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 10)) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 01)) << createTaskItem(QStringLiteral("D"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("C") << QStringLiteral("B") << QStringLiteral("D"); QTest::newRow("due date ascending") << int(Presentation::TaskFilterProxyModel::DateSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 10)) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 01)) << createTaskItem(QStringLiteral("D"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("D") << QStringLiteral("B") << QStringLiteral("C"); QTest::newRow("due date descending") << int(Presentation::TaskFilterProxyModel::DateSort) << int(Qt::DescendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("A"), QStringLiteral("foo"), QDate(2014, 03, 01), QDate(2014, 03, 10)) << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(2014, 03, 10), QDate(2014, 03, 01)); expectedOutputTitles << QStringLiteral("B") << QStringLiteral("A"); QTest::newRow("due date over start date") << int(Presentation::TaskFilterProxyModel::DateSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("A"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 10)) << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(2014, 03, 01), QDate()); expectedOutputTitles << QStringLiteral("B") << QStringLiteral("A"); QTest::newRow("due date over start date") << int(Presentation::TaskFilterProxyModel::DateSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; } void shouldSortFollowingType() { // GIVEN QFETCH(int, sortType); QFETCH(int, sortOrder); QFETCH(QList, inputItems); QFETCH(QStringList, expectedOutputTitles); QStandardItemModel input; foreach (QStandardItem *item, inputItems) { input.appendRow(item); } // WHEN Presentation::TaskFilterProxyModel output; output.setSourceModel(&input); output.setSortType(Presentation::TaskFilterProxyModel::SortType(sortType)); output.setSortOrder(Qt::SortOrder(sortOrder)); QStringList outputTitles; outputTitles.reserve(output.rowCount()); for (int row = 0; row < output.rowCount(); row++) { outputTitles << output.index(row, 0).data().toString(); } // THEN QCOMPARE(outputTitles, expectedOutputTitles); } }; ZANSHIN_TEST_MAIN(TaskFilterProxyModelTest) #include "taskfilterproxymodeltest.moc" diff --git a/tests/units/widgets/applicationcomponentstest.cpp b/tests/units/widgets/applicationcomponentstest.cpp index 27cdb65d..0c53a9cd 100644 --- a/tests/units/widgets/applicationcomponentstest.cpp +++ b/tests/units/widgets/applicationcomponentstest.cpp @@ -1,776 +1,779 @@ /* 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 = 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 = 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 = 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(nullptr), execCount(0), 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 = 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(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)); + auto availableGlobalActions = available->globalActions(); + for (auto it = availableGlobalActions.cbegin(); it != availableGlobalActions.cend(); ++it) + QCOMPARE(actions.value(it.key()), it.value()); // availableSources view auto availableSources = components.availableSourcesView(); - foreach (const auto &key, availableSources->globalActions().keys()) - QCOMPARE(actions.value(key), availableSources->globalActions().value(key)); + auto availableSourcesGlobalActions = availableSources->globalActions(); + for (auto it = availableSourcesGlobalActions.cbegin(); it != availableSourcesGlobalActions.cend(); ++it) + QCOMPARE(actions.value(it.key()), it.value()); // page view auto page = components.pageView(); - foreach (const auto &key, page->globalActions().keys()) - QCOMPARE(actions.value(key), page->globalActions().value(key)); + auto pageGlobalActions = page->globalActions(); + for (auto it = pageGlobalActions.cbegin(); it != pageGlobalActions.cend(); ++it) + QCOMPARE(actions.value(it.key()), it.value()); // 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"