diff --git a/tests/testlib/akonadifakestorage.cpp b/tests/testlib/akonadifakestorage.cpp index 1fcf80da..cf53a394 100644 --- a/tests/testlib/akonadifakestorage.cpp +++ b/tests/testlib/akonadifakestorage.cpp @@ -1,406 +1,430 @@ /* This file is part of Zanshin Copyright 2015 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonadifakestorage.h" #include #include "akonadi/akonadistoragesettings.h" #include "akonadifakedata.h" #include "akonadifakejobs.h" #include "utils/jobhandler.h" using namespace Testlib; class AkonadiFakeTransaction : public FakeJob { Q_OBJECT public: explicit AkonadiFakeTransaction() : FakeJob(), m_nextIdx(0) { } private slots: void onTimeout() override { auto jobs = childJobs(); if (m_nextIdx == 0) { const auto it = std::find_if(jobs.constBegin(), jobs.constEnd(), [] (FakeJob *job) { return job->expectedError() != 0; }); if (it != jobs.constEnd()) { setError((*it)->expectedError()); setErrorText((*it)->expectedErrorText()); emitResult(); return; } } if (m_nextIdx >= jobs.size()) { emitResult(); return; } auto job = jobs[m_nextIdx]; connect(job, &KJob::result, this, &AkonadiFakeTransaction::onTimeout); job->start(); m_nextIdx++; } private: QList childJobs() const { QList jobs = findChildren(); jobs.erase(std::remove_if(jobs.begin(), jobs.end(), [this] (FakeJob *job) { return job->parent() != this; }), jobs.end()); return jobs; } int m_nextIdx; }; Utils::JobHandler::StartMode startModeForParent(QObject *parent) { bool isTransaction = qobject_cast(parent); return isTransaction ? Utils::JobHandler::ManualStart : Utils::JobHandler::AutoStart; } void noop() {} AkonadiFakeStorage::AkonadiFakeStorage(AkonadiFakeData *data) : m_data(data) { } Akonadi::Collection AkonadiFakeStorage::defaultCollection() { return Akonadi::StorageSettings::instance().defaultCollection(); } KJob *AkonadiFakeStorage::createItem(Akonadi::Item item, Akonadi::Collection collection) { Q_ASSERT(!item.isValid()); auto job = new FakeJob; if (!m_data->item(item.id()).isValid()) { + job->setExpectedError(m_data->storageBehavior().createNextItemErrorCode(), + m_data->storageBehavior().createNextItemErrorText()); Utils::JobHandler::install(job, [=] () mutable { - item.setId(m_data->maxItemId() + 1); - item.setParentCollection(collection); - // Force payload detach - item.setPayloadFromData(item.payloadData()); - m_data->createItem(item); + if (!job->error()) { + item.setId(m_data->maxItemId() + 1); + item.setParentCollection(collection); + // Force payload detach + item.setPayloadFromData(item.payloadData()); + m_data->createItem(item); + } }); } else { job->setExpectedError(1, QStringLiteral("Item already exists")); Utils::JobHandler::install(job, noop); } return job; } KJob *AkonadiFakeStorage::updateItem(Akonadi::Item item, QObject *parent) { auto job = new FakeJob(parent); auto startMode = startModeForParent(parent); if (m_data->item(item.id()).isValid()) { + job->setExpectedError(m_data->storageBehavior().updateNextItemErrorCode(), + m_data->storageBehavior().updateNextItemErrorText()); Utils::JobHandler::install(job, [=] () mutable { - // Force payload detach - item.setPayloadFromData(item.payloadData()); - m_data->modifyItem(item); + if (!job->error()) { + // Force payload detach + item.setPayloadFromData(item.payloadData()); + m_data->modifyItem(item); + } }, startMode); } else { job->setExpectedError(1, QStringLiteral("Item doesn't exist")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::removeItem(Akonadi::Item item) { auto job = new FakeJob; if (m_data->item(item.id()).isValid()) { Utils::JobHandler::install(job, [=] { - m_data->removeItem(item); + if (!job->error()) { + m_data->removeItem(item); + } }); } else { job->setExpectedError(1, QStringLiteral("Item doesn't exist")); Utils::JobHandler::install(job, noop); } return job; } KJob *AkonadiFakeStorage::removeItems(Akonadi::Item::List items, QObject *parent) { auto job = new FakeJob; auto startMode = startModeForParent(parent); bool allItemsExist = std::all_of(items.constBegin(), items.constEnd(), [=] (const Akonadi::Item &item) { return m_data->item(item.id()).isValid(); }); if (allItemsExist) { + job->setExpectedError(m_data->storageBehavior().deleteNextItemErrorCode(), + m_data->storageBehavior().deleteNextItemErrorText()); Utils::JobHandler::install(job, [=] { - foreach (const Akonadi::Item &item, items) { - m_data->removeItem(item); + if (!job->error()) { + foreach (const Akonadi::Item &item, items) { + m_data->removeItem(item); + } } }, startMode); } else { job->setExpectedError(1, QStringLiteral("At least one item doesn't exist")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::moveItem(Akonadi::Item item, Akonadi::Collection collection, QObject *parent) { auto job = new FakeJob(parent); auto startMode = startModeForParent(parent); if (m_data->item(item.id()).isValid() && m_data->collection(collection.id()).isValid()) { Utils::JobHandler::install(job, [=] () mutable { - item.setParentCollection(collection); - // Force payload detach - item.setPayloadFromData(item.payloadData()); - m_data->modifyItem(item); + if (!job->error()) { + item.setParentCollection(collection); + // Force payload detach + item.setPayloadFromData(item.payloadData()); + m_data->modifyItem(item); + } }, startMode); } else { job->setExpectedError(1, QStringLiteral("The item or the collection doesn't exist")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::moveItems(Akonadi::Item::List items, Akonadi::Collection collection, QObject *parent) { using namespace std::placeholders; auto job = new FakeJob(parent); auto startMode = startModeForParent(parent); bool allItemsExist = std::all_of(items.constBegin(), items.constEnd(), [=] (const Akonadi::Item &item) { return m_data->item(item.id()).isValid(); }); if (allItemsExist && m_data->collection(collection.id()).isValid()) { Utils::JobHandler::install(job, [=] () mutable { - std::transform(items.constBegin(), items.constEnd(), - items.begin(), - [=] (const Akonadi::Item &item) { - auto result = item; - result.setParentCollection(collection); - // Force payload detach - result.setPayloadFromData(result.payloadData()); - return result; - }); - - foreach (const Akonadi::Item &item, items) { - m_data->modifyItem(item); + if (!job->error()) { + std::transform(items.constBegin(), items.constEnd(), + items.begin(), + [=] (const Akonadi::Item &item) { + auto result = item; + result.setParentCollection(collection); + // Force payload detach + result.setPayloadFromData(result.payloadData()); + return result; + }); + + foreach (const Akonadi::Item &item, items) { + m_data->modifyItem(item); + } } }, startMode); } else { job->setExpectedError(1, QStringLiteral("One of the items or the collection doesn't exist")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::createCollection(Akonadi::Collection collection, QObject *parent) { Q_ASSERT(!collection.isValid()); auto job = new FakeJob(parent); auto startMode = startModeForParent(parent); if (!m_data->collection(collection.id()).isValid()) { Utils::JobHandler::install(job, [=] () mutable { - collection.setId(m_data->maxCollectionId() + 1); - m_data->createCollection(collection); + if (!job->error()) { + collection.setId(m_data->maxCollectionId() + 1); + m_data->createCollection(collection); + } }, startMode); } else { job->setExpectedError(1, QStringLiteral("The collection already exists")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::updateCollection(Akonadi::Collection collection, QObject *parent) { auto job = new FakeJob(parent); auto startMode = startModeForParent(parent); if (m_data->collection(collection.id()).isValid()) { Utils::JobHandler::install(job, [=] { - m_data->modifyCollection(collection); + if (!job->error()) { + m_data->modifyCollection(collection); + } }, startMode); } else { job->setExpectedError(1, QStringLiteral("The collection doesn't exist")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::removeCollection(Akonadi::Collection collection, QObject *parent) { auto job = new FakeJob(parent); auto startMode = startModeForParent(parent); if (m_data->collection(collection.id()).isValid()) { Utils::JobHandler::install(job, [=] { - m_data->removeCollection(collection); + if (!job->error()) { + m_data->removeCollection(collection); + } }, startMode); } else { job->setExpectedError(1, QStringLiteral("The collection doesn't exist")); Utils::JobHandler::install(job, noop, startMode); } return job; } KJob *AkonadiFakeStorage::createTransaction() { auto job = new AkonadiFakeTransaction; Utils::JobHandler::install(job, noop); return job; } Akonadi::CollectionFetchJobInterface *AkonadiFakeStorage::fetchCollections(Akonadi::Collection collection, Akonadi::StorageInterface::FetchDepth depth) { auto job = new AkonadiFakeCollectionFetchJob; auto children = Akonadi::Collection::List(); switch (depth) { case Base: children << m_data->collection(findId(collection)); break; case FirstLevel: children << m_data->childCollections(findId(collection)); break; case Recursive: children = collectChildren(collection); break; } auto collections = children; if (depth != Base) { // Replace the dummy parents in the ancestor chain with proper ones // full of juicy data using namespace std::placeholders; auto completeCollection = std::bind(&AkonadiFakeData::reconstructAncestors, m_data, _1, collection); std::transform(collections.begin(), collections.end(), collections.begin(), completeCollection); } const auto behavior = m_data->storageBehavior().fetchCollectionsBehavior(collection.id()); if (behavior == AkonadiFakeStorageBehavior::NormalFetch) job->setCollections(collections); job->setExpectedError(m_data->storageBehavior().fetchCollectionsErrorCode(collection.id())); Utils::JobHandler::install(job, noop); return job; } Akonadi::ItemFetchJobInterface *AkonadiFakeStorage::fetchItems(Akonadi::Collection collection) { auto items = m_data->childItems(findId(collection)); std::transform(items.begin(), items.end(), items.begin(), [this] (const Akonadi::Item &item) { auto result = m_data->reconstructItemDependencies(item); // Force payload detach result.setPayloadFromData(result.payloadData()); return result; }); auto job = new AkonadiFakeItemFetchJob; const auto behavior = m_data->storageBehavior().fetchItemsBehavior(collection.id()); if (behavior == AkonadiFakeStorageBehavior::NormalFetch) job->setItems(items); job->setExpectedError(m_data->storageBehavior().fetchItemsErrorCode(collection.id())); Utils::JobHandler::install(job, noop); return job; } Akonadi::ItemFetchJobInterface *AkonadiFakeStorage::fetchItem(Akonadi::Item item) { auto fullItem = m_data->item(findId(item)); fullItem = m_data->reconstructItemDependencies(fullItem); // Force payload detach fullItem.setPayloadFromData(fullItem.payloadData()); auto job = new AkonadiFakeItemFetchJob; const auto behavior = m_data->storageBehavior().fetchItemBehavior(item.id()); if (behavior == AkonadiFakeStorageBehavior::NormalFetch) job->setItems(Akonadi::Item::List() << fullItem); job->setExpectedError(m_data->storageBehavior().fetchItemErrorCode(item.id())); Utils::JobHandler::install(job, noop); return job; } Akonadi::Collection::Id AkonadiFakeStorage::findId(const Akonadi::Collection &collection) { if (collection.isValid() || collection.remoteId().isEmpty()) return collection.id(); const auto remoteId = collection.remoteId(); auto collections = m_data->collections(); auto result = std::find_if(collections.constBegin(), collections.constEnd(), [remoteId] (const Akonadi::Collection &collection) { return collection.remoteId() == remoteId; }); return (result != collections.constEnd()) ? result->id() : collection.id(); } Akonadi::Item::Id AkonadiFakeStorage::findId(const Akonadi::Item &item) { if (item.isValid() || item.remoteId().isEmpty()) return item.id(); const auto remoteId = item.remoteId(); auto items = m_data->items(); auto result = std::find_if(items.constBegin(), items.constEnd(), [remoteId] (const Akonadi::Item &item) { return item.remoteId() == remoteId; }); return (result != items.constEnd()) ? result->id() : item.id(); } Akonadi::Collection::List AkonadiFakeStorage::collectChildren(const Akonadi::Collection &root) { auto collections = Akonadi::Collection::List(); foreach (const auto &child, m_data->childCollections(findId(root))) { if (child.enabled()) collections << m_data->collection(findId(child)); collections += collectChildren(child); } return collections; } #include "akonadifakestorage.moc" diff --git a/tests/testlib/akonadifakestoragebehavior.cpp b/tests/testlib/akonadifakestoragebehavior.cpp index 5a61144e..25067d4c 100644 --- a/tests/testlib/akonadifakestoragebehavior.cpp +++ b/tests/testlib/akonadifakestoragebehavior.cpp @@ -1,114 +1,162 @@ /* 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 "akonadifakestoragebehavior.h" using namespace Testlib; AkonadiFakeStorageBehavior::AkonadiFakeStorageBehavior() { } AkonadiFakeStorageBehavior::~AkonadiFakeStorageBehavior() { } void AkonadiFakeStorageBehavior::setFetchCollectionsErrorCode(Akonadi::Collection::Id id, int errorCode) { m_fetchCollectionsErrorCode[id] = errorCode; } int AkonadiFakeStorageBehavior::fetchCollectionsErrorCode(Akonadi::Collection::Id id) const { return m_fetchCollectionsErrorCode.value(id, KJob::NoError); } void AkonadiFakeStorageBehavior::setFetchCollectionsBehavior(Akonadi::Collection::Id id, FetchBehavior behavior) { m_fetchCollectionsBehavior[id] = behavior; } AkonadiFakeStorageBehavior::FetchBehavior AkonadiFakeStorageBehavior::fetchCollectionsBehavior(Akonadi::Collection::Id id) const { return m_fetchCollectionsBehavior.value(id, NormalFetch); } void AkonadiFakeStorageBehavior::setSearchCollectionsErrorCode(const QString &name, int errorCode) { m_searchCollectionsErrorCode[name] = errorCode; } int AkonadiFakeStorageBehavior::searchCollectionsErrorCode(const QString &name) const { return m_searchCollectionsErrorCode.value(name, KJob::NoError); } void AkonadiFakeStorageBehavior::setSearchCollectionsBehavior(const QString &name, AkonadiFakeStorageBehavior::FetchBehavior behavior) { m_searchCollectionsBehavior[name] = behavior; } AkonadiFakeStorageBehavior::FetchBehavior AkonadiFakeStorageBehavior::searchCollectionsBehavior(const QString &name) const { return m_searchCollectionsBehavior.value(name, NormalFetch); } void AkonadiFakeStorageBehavior::setFetchItemsErrorCode(Akonadi::Collection::Id id, int errorCode) { m_fetchItemsErrorCode[id] = errorCode; } int AkonadiFakeStorageBehavior::fetchItemsErrorCode(Akonadi::Collection::Id id) const { return m_fetchItemsErrorCode.value(id, KJob::NoError); } void AkonadiFakeStorageBehavior::setFetchItemsBehavior(Akonadi::Collection::Id id, FetchBehavior behavior) { m_fetchItemsBehavior[id] = behavior; } AkonadiFakeStorageBehavior::FetchBehavior AkonadiFakeStorageBehavior::fetchItemsBehavior(Akonadi::Collection::Id id) const { return m_fetchItemsBehavior.value(id, NormalFetch); } void AkonadiFakeStorageBehavior::setFetchItemErrorCode(Akonadi::Item::Id id, int errorCode) { m_fetchItemErrorCode[id] = errorCode; } int AkonadiFakeStorageBehavior::fetchItemErrorCode(Akonadi::Item::Id id) const { return m_fetchItemErrorCode.value(id, KJob::NoError); } void AkonadiFakeStorageBehavior::setFetchItemBehavior(Akonadi::Item::Id id, FetchBehavior behavior) { m_fetchItemBehavior[id] = behavior; } AkonadiFakeStorageBehavior::FetchBehavior AkonadiFakeStorageBehavior::fetchItemBehavior(Akonadi::Item::Id id) const { return m_fetchItemBehavior.value(id, NormalFetch); } + +void AkonadiFakeStorageBehavior::setCreateNextItemError(int errorCode, const QString &errorText) +{ + m_createNextItemErrorCode = errorCode; + m_createNextItemErrorText = errorText; +} + +int AkonadiFakeStorageBehavior::createNextItemErrorCode() +{ + return std::exchange(m_createNextItemErrorCode, KJob::NoError); +} + +QString AkonadiFakeStorageBehavior::createNextItemErrorText() +{ + return std::exchange(m_createNextItemErrorText, QString()); +} + +void AkonadiFakeStorageBehavior::setDeleteNextItemError(int errorCode, const QString &errorText) +{ + m_deleteNextItemErrorCode = errorCode; + m_deleteNextItemErrorText = errorText; +} + +int AkonadiFakeStorageBehavior::deleteNextItemErrorCode() +{ + return std::exchange(m_deleteNextItemErrorCode, KJob::NoError); +} + +QString AkonadiFakeStorageBehavior::deleteNextItemErrorText() +{ + return std::exchange(m_deleteNextItemErrorText, QString()); +} + +void AkonadiFakeStorageBehavior::setUpdateNextItemError(int errorCode, const QString &errorText) +{ + m_updateNextItemErrorCode = errorCode; + m_updateNextItemErrorText = errorText; +} + +int AkonadiFakeStorageBehavior::updateNextItemErrorCode() +{ + return std::exchange(m_updateNextItemErrorCode, KJob::NoError); +} + +QString AkonadiFakeStorageBehavior::updateNextItemErrorText() +{ + return std::exchange(m_updateNextItemErrorText, QString()); +} diff --git a/tests/testlib/akonadifakestoragebehavior.h b/tests/testlib/akonadifakestoragebehavior.h index a482a409..35fc2a72 100644 --- a/tests/testlib/akonadifakestoragebehavior.h +++ b/tests/testlib/akonadifakestoragebehavior.h @@ -1,81 +1,102 @@ /* 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_AKONADIFAKESTORAGEBEHAVIOR_H #define TESTLIB_AKONADIFAKESTORAGEBEHAVIOR_H #include #include #include namespace Testlib { class AkonadiFakeStorageBehavior { public: enum FetchBehavior { NormalFetch = 0x0, EmptyFetch }; AkonadiFakeStorageBehavior(); ~AkonadiFakeStorageBehavior(); void setFetchCollectionsErrorCode(Akonadi::Collection::Id id, int errorCode); int fetchCollectionsErrorCode(Akonadi::Collection::Id id) const; void setFetchCollectionsBehavior(Akonadi::Collection::Id id, FetchBehavior behavior); FetchBehavior fetchCollectionsBehavior(Akonadi::Collection::Id id) const; void setSearchCollectionsErrorCode(const QString &name, int errorCode); int searchCollectionsErrorCode(const QString &name) const; void setSearchCollectionsBehavior(const QString &name, FetchBehavior behavior); FetchBehavior searchCollectionsBehavior(const QString &name) const; void setFetchItemsErrorCode(Akonadi::Collection::Id id, int errorCode); int fetchItemsErrorCode(Akonadi::Collection::Id id) const; void setFetchItemsBehavior(Akonadi::Collection::Id id, FetchBehavior behavior); FetchBehavior fetchItemsBehavior(Akonadi::Collection::Id id) const; void setFetchItemErrorCode(Akonadi::Item::Id id, int errorCode); int fetchItemErrorCode(Akonadi::Item::Id id) const; void setFetchItemBehavior(Akonadi::Item::Id id, FetchBehavior behavior); FetchBehavior fetchItemBehavior(Akonadi::Item::Id id) const; + void setCreateNextItemError(int errorCode, const QString &errorText); + int createNextItemErrorCode(); + QString createNextItemErrorText(); + + void setDeleteNextItemError(int errorCode, const QString &errorText); + int deleteNextItemErrorCode(); + QString deleteNextItemErrorText(); + + void setUpdateNextItemError(int errorCode, const QString &errorText); + int updateNextItemErrorCode(); + QString updateNextItemErrorText(); + private: QHash m_fetchCollectionsErrorCode; QHash m_fetchCollectionsBehavior; QHash m_searchCollectionsErrorCode; QHash m_searchCollectionsBehavior; QHash m_fetchItemsErrorCode; QHash m_fetchItemsBehavior; QHash m_fetchItemErrorCode; QHash m_fetchItemBehavior; + + int m_createNextItemErrorCode = 0; + QString m_createNextItemErrorText; + + int m_deleteNextItemErrorCode = 0; + QString m_deleteNextItemErrorText; + + int m_updateNextItemErrorCode = 0; + QString m_updateNextItemErrorText; }; } #endif // TESTLIB_AKONADIFAKESTORAGEBEHAVIOR_H diff --git a/tests/units/presentation/inboxpagemodeltest.cpp b/tests/units/presentation/inboxpagemodeltest.cpp index f70d558c..741448e8 100644 --- a/tests/units/presentation/inboxpagemodeltest.cpp +++ b/tests/units/presentation/inboxpagemodeltest.cpp @@ -1,573 +1,542 @@ /* 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 "domain/taskqueries.h" #include "domain/taskrepository.h" #include "presentation/inboxpagemodel.h" #include "presentation/errorhandler.h" #include "presentation/querytreemodelbase.h" #include "akonadi/akonadiserializerinterface.h" #include "utils/dependencymanager.h" #include "integration/dependencies.h" #include "testlib/fakejob.h" #include "testlib/akonadifakedata.h" #include "testlib/gencollection.h" #include "testlib/gentodo.h" #include "testlib/testhelpers.h" using namespace Testlib; -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 InboxPageModelTest : public QObject { Q_OBJECT private slots: + void cleanup() + { + // The first call to QueryTreeModelBase::data triggers fetchTaskExtraData which creates jobs. + // Wait for these to finish so they don't mess up subsequent tests + TestHelpers::waitForEmptyJobQueue(); + } + void shouldListInboxInCentralListModel() { // GIVEN AkonadiFakeData data; auto deps = data.createDependencies(); Integration::initializeDefaultDomainDependencies(*deps.get()); // One top level collection data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // One root task data.createItem(GenTodo().withId(1).withParent(42).withUid("1").withTitle(QStringLiteral("rootTask"))); // One task under the root task data.createItem(GenTodo().withId(2).withParent(42).withUid("2").withParentUid("1").done(true).withTitle(QStringLiteral("childTask"))); auto serializer = deps->create(); Presentation::InboxPageModel inbox(deps->create(), deps->create()); // WHEN QAbstractItemModel *model = inbox.centralListModel(); TestHelpers::waitForEmptyJobQueue(); // THEN const QModelIndex rootTaskIndex = model->index(0, 0); const QModelIndex childTaskIndex = model->index(0, 0, rootTaskIndex); QCOMPARE(model->rowCount(), 1); QCOMPARE(model->rowCount(rootTaskIndex), 1); QCOMPARE(model->rowCount(childTaskIndex), 0); auto rootTask = model->data(rootTaskIndex, Presentation::QueryTreeModelBase::ObjectRole).value(); auto childTask = model->data(childTaskIndex, Presentation::QueryTreeModelBase::ObjectRole).value(); const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled; QCOMPARE(model->flags(rootTaskIndex), defaultFlags | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled); QCOMPARE(model->flags(childTaskIndex), defaultFlags | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled); QCOMPARE(model->data(rootTaskIndex).toString(), rootTask->title()); QCOMPARE(model->data(childTaskIndex).toString(), childTask->title()); QCOMPARE(model->data(rootTaskIndex, Qt::EditRole).toString(), rootTask->title()); QCOMPARE(model->data(childTaskIndex, Qt::EditRole).toString(), childTask->title()); QVERIFY(model->data(rootTaskIndex, Qt::CheckStateRole).isValid()); QVERIFY(model->data(childTaskIndex, Qt::CheckStateRole).isValid()); QCOMPARE(model->data(rootTaskIndex, Qt::CheckStateRole).toBool(), rootTask->isDone()); QCOMPARE(model->data(childTaskIndex, Qt::CheckStateRole).toBool(), childTask->isDone()); // WHEN QVERIFY(model->setData(rootTaskIndex, "newRootTask")); QVERIFY(model->setData(childTaskIndex, "newChildTask")); QVERIFY(model->setData(rootTaskIndex, Qt::Checked, Qt::CheckStateRole)); QVERIFY(model->setData(childTaskIndex, Qt::Unchecked, Qt::CheckStateRole)); // THEN QCOMPARE(rootTask->title(), QStringLiteral("newRootTask")); QCOMPARE(childTask->title(), QStringLiteral("newChildTask")); QCOMPARE(rootTask->isDone(), true); QCOMPARE(childTask->isDone(), false); // WHEN auto mimeData = std::unique_ptr(model->mimeData(QModelIndexList() << childTaskIndex)); // THEN QVERIFY(mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))); QCOMPARE(mimeData->property("objects").value(), Domain::Task::List() << childTask); // WHEN // - root (1) // - child (2) // - grandchild (48), will be dropped onto root data.createItem(GenTodo().withId(48).withParent(42).withUid("48").withParentUid("2").withTitle(QStringLiteral("childTask2"))); QCOMPARE(model->rowCount(childTaskIndex), 1); auto grandChildTask = model->data(model->index(0, 0, childTaskIndex), Presentation::QueryTreeModelBase::ObjectRole).value(); QVERIFY(grandChildTask); mimeData.reset(new QMimeData); mimeData->setData(QStringLiteral("application/x-zanshin-object"), "object"); mimeData->setProperty("objects", QVariant::fromValue(Domain::Task::List() << grandChildTask)); QVERIFY(model->dropMimeData(mimeData.get(), Qt::MoveAction, -1, -1, rootTaskIndex)); TestHelpers::waitForEmptyJobQueue(); // THEN // root (1) // - child (2) // - second child (48) QCOMPARE(serializer->relatedUidFromItem(data.item(48)), QStringLiteral("1")); QCOMPARE(model->rowCount(), 1); QCOMPARE(model->rowCount(rootTaskIndex), 2); QCOMPARE(model->rowCount(childTaskIndex), 0); // WHEN // two more toplevel tasks data.createItem(GenTodo().withId(49).withParent(42).withUid("49").withTitle(QStringLiteral("childTask3"))); auto task3 = model->data(model->index(1, 0), Presentation::QueryTreeModelBase::ObjectRole).value(); QVERIFY(task3); data.createItem(GenTodo().withId(50).withParent(42).withUid("50").withTitle(QStringLiteral("childTask4"))); auto task4 = model->data(model->index(2, 0), Presentation::QueryTreeModelBase::ObjectRole).value(); QVERIFY(task4); mimeData.reset(new QMimeData); mimeData->setData(QStringLiteral("application/x-zanshin-object"), "object"); mimeData->setProperty("objects", QVariant::fromValue(Domain::Task::List() << task3 << task4)); QVERIFY(model->dropMimeData(mimeData.get(), Qt::MoveAction, -1, -1, rootTaskIndex)); TestHelpers::waitForEmptyJobQueue(); // THEN // root (1) // - child (2) // - second child (48) // - task3 (49) // - task4 (50) QCOMPARE(serializer->relatedUidFromItem(data.item(49)), QStringLiteral("1")); QCOMPARE(serializer->relatedUidFromItem(data.item(50)), QStringLiteral("1")); QCOMPARE(model->rowCount(), 1); QCOMPARE(model->rowCount(rootTaskIndex), 4); } void shouldAddTasksInInbox() { // GIVEN + AkonadiFakeData data; + auto deps = data.createDependencies(); + Integration::initializeDefaultDomainDependencies(*deps.get()); + data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); - // ... in fact we won't list any model - Utils::MockObject taskQueriesMock; - - // We'll gladly create a task though - Utils::MockObject taskRepositoryMock; - taskRepositoryMock(&Domain::TaskRepository::create).when(any()).thenReturn(new FakeJob(this)); - - Presentation::InboxPageModel inbox(taskQueriesMock.getInstance(), - taskRepositoryMock.getInstance()); + Presentation::InboxPageModel pageModel(deps->create(), + deps->create()); + QAbstractItemModel *model = pageModel.centralListModel(); // WHEN auto title = QStringLiteral("New task"); - auto task = inbox.addItem(title); + auto createdTask = pageModel.addItem(title); + TestHelpers::waitForEmptyJobQueue(); // THEN - QVERIFY(taskRepositoryMock(&Domain::TaskRepository::create).when(any()).exactly(1)); - QVERIFY(task); - QCOMPARE(task->title(), title); + QVERIFY(createdTask); + QCOMPARE(createdTask->title(), title); + QCOMPARE(model->rowCount(), 1); + auto taskInModel = model->data(model->index(0, 0), Presentation::QueryTreeModelBase::ObjectRole).value(); + QVERIFY(taskInModel); + QCOMPARE(taskInModel->title(), createdTask->title()); + QCOMPARE(taskInModel->startDate(), createdTask->startDate()); + + QCOMPARE(data.items().count(), 1); + TestHelpers::waitForEmptyJobQueue(); } void shouldAddChildTask() { // GIVEN + AkonadiFakeData data; + auto deps = data.createDependencies(); + Integration::initializeDefaultDomainDependencies(*deps.get()); + data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // Two tasks - auto task1 = Domain::Task::Ptr::create(); - auto task2 = Domain::Task::Ptr::create(); - auto taskProvider = Domain::QueryResultProvider::Ptr::create(); - auto taskResult = Domain::QueryResult::create(taskProvider); - taskProvider->append(task1); - taskProvider->append(task2); - - Utils::MockObject taskQueriesMock; - taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(taskResult); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(task1).thenReturn(Domain::QueryResult::Ptr()); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(task2).thenReturn(Domain::QueryResult::Ptr()); - taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); - - Utils::MockObject taskRepositoryMock; - taskRepositoryMock(&Domain::TaskRepository::createChild).when(any(), - any()) - .thenReturn(new FakeJob(this)); - - Presentation::InboxPageModel inbox(taskQueriesMock.getInstance(), - taskRepositoryMock.getInstance()); + data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1"))); + data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2"))); + QCOMPARE(data.items().count(), 2); + + Presentation::InboxPageModel pageModel(deps->create(), + deps->create()); + QAbstractItemModel *model = pageModel.centralListModel(); + TestHelpers::waitForEmptyJobQueue(); + QCOMPARE(model->rowCount(), 2); // WHEN const auto title = QStringLiteral("New task"); - const auto parentIndex = inbox.centralListModel()->index(0, 0); - const auto createdTask = inbox.addItem(title, parentIndex); + const auto parentIndex = model->index(0, 0); + const auto createdTask = pageModel.addItem(title, parentIndex); + TestHelpers::waitForEmptyJobQueue(); // THEN - QVERIFY(taskRepositoryMock(&Domain::TaskRepository::createChild).when(any(), - any()) - .exactly(1)); QVERIFY(createdTask); QCOMPARE(createdTask->title(), title); + + QCOMPARE(model->rowCount(parentIndex), 1); + auto taskInModel = model->data(model->index(0, 0, parentIndex), Presentation::QueryTreeModelBase::ObjectRole).value(); + QVERIFY(taskInModel); + QCOMPARE(taskInModel->title(), createdTask->title()); + QCOMPARE(taskInModel->startDate(), createdTask->startDate()); + + QCOMPARE(data.items().count(), 3); + TestHelpers::waitForEmptyJobQueue(); } void shouldGetAnErrorMessageWhenAddTaskFailed() { // GIVEN + AkonadiFakeData data; + auto deps = data.createDependencies(); + Integration::initializeDefaultDomainDependencies(*deps.get()); + data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); - // ... in fact we won't list any model - Utils::MockObject taskQueriesMock; - - // We'll gladly create a task though - Utils::MockObject taskRepositoryMock; - auto job = new FakeJob(this); - job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); - taskRepositoryMock(&Domain::TaskRepository::create).when(any()).thenReturn(job); - - Presentation::InboxPageModel inbox(taskQueriesMock.getInstance(), - taskRepositoryMock.getInstance()); + Presentation::InboxPageModel pageModel(deps->create(), + deps->create()); FakeErrorHandler errorHandler; - inbox.setErrorHandler(&errorHandler); + pageModel.setErrorHandler(&errorHandler); // WHEN - inbox.addItem(QStringLiteral("New task")); + data.storageBehavior().setCreateNextItemError(1, QStringLiteral("Foo")); + pageModel.addItem(QStringLiteral("New task")); + TestHelpers::waitForEmptyJobQueue(); // THEN - QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot add task New task in Inbox: Foo")); } void shouldDeleteItems() { // GIVEN + AkonadiFakeData data; + auto deps = data.createDependencies(); + Integration::initializeDefaultDomainDependencies(*deps.get()); + data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // Two tasks - auto task1 = Domain::Task::Ptr::create(); - auto task2 = Domain::Task::Ptr::create(); - auto taskProvider = Domain::QueryResultProvider::Ptr::create(); - auto taskResult = Domain::QueryResult::create(taskProvider); - taskProvider->append(task1); - taskProvider->append(task2); + data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1"))); + data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2"))); + QCOMPARE(data.items().count(), 2); - Utils::MockObject taskQueriesMock; - taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(taskResult); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(task1).thenReturn(Domain::QueryResult::Ptr()); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(task2).thenReturn(Domain::QueryResult::Ptr()); - taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); - - Utils::MockObject taskRepositoryMock; - taskRepositoryMock(&Domain::TaskRepository::remove).when(task2).thenReturn(new FakeJob(this)); - - Presentation::InboxPageModel inbox(taskQueriesMock.getInstance(), - taskRepositoryMock.getInstance()); + Presentation::InboxPageModel pageModel(deps->create(), + deps->create()); + QAbstractItemModel *model = pageModel.centralListModel(); + TestHelpers::waitForEmptyJobQueue(); + QCOMPARE(model->rowCount(), 2); // WHEN - const QModelIndex index = inbox.centralListModel()->index(1, 0); - inbox.removeItem(index); + const QModelIndex index = model->index(1, 0); + QVERIFY(index.isValid()); + pageModel.removeItem(index); + TestHelpers::waitForEmptyJobQueue(); // THEN - QVERIFY(taskRepositoryMock(&Domain::TaskRepository::remove).when(task2).exactly(1)); + QCOMPARE(model->rowCount(), 1); + QCOMPARE(data.items().count(), 1); } void shouldGetAnErrorMessageWhenDeleteItemsFailed() { // GIVEN + AkonadiFakeData data; + auto deps = data.createDependencies(); + Integration::initializeDefaultDomainDependencies(*deps.get()); + data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // Two tasks - auto task1 = Domain::Task::Ptr::create(); - auto task2 = Domain::Task::Ptr::create(); - task2->setTitle(QStringLiteral("task2")); - auto inboxProvider = Domain::QueryResultProvider::Ptr::create(); - auto inboxResult = Domain::QueryResult::create(inboxProvider); - inboxProvider->append(task1); - inboxProvider->append(task2); - - Utils::MockObject taskQueriesMock; - taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(inboxResult); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(task1).thenReturn(Domain::QueryResult::Ptr()); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(task2).thenReturn(Domain::QueryResult::Ptr()); - taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); - - Utils::MockObject taskRepositoryMock; - auto job = new FakeJob(this); - job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); - taskRepositoryMock(&Domain::TaskRepository::remove).when(task2).thenReturn(job); - - Presentation::InboxPageModel inbox(taskQueriesMock.getInstance(), - taskRepositoryMock.getInstance()); + data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1"))); + data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2"))); + QCOMPARE(data.items().count(), 2); + + Presentation::InboxPageModel pageModel(deps->create(), + deps->create()); + QAbstractItemModel *model = pageModel.centralListModel(); + TestHelpers::waitForEmptyJobQueue(); + QCOMPARE(model->rowCount(), 2); + FakeErrorHandler errorHandler; - inbox.setErrorHandler(&errorHandler); + pageModel.setErrorHandler(&errorHandler); // WHEN - const QModelIndex index = inbox.centralListModel()->index(1, 0); - inbox.removeItem(index); + data.storageBehavior().setDeleteNextItemError(1, QStringLiteral("Error deleting")); + const QModelIndex index = model->index(1, 0); + pageModel.removeItem(index); + TestHelpers::waitForEmptyJobQueue(); // THEN - QTest::qWait(150); - QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot remove task task2 from Inbox: Foo")); + QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot remove task task2 from Inbox: Error deleting")); } void shouldPromoteItem() { // GIVEN + AkonadiFakeData data; + auto deps = data.createDependencies(); + Integration::initializeDefaultDomainDependencies(*deps.get()); + data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); + // Two tasks - auto task1 = Domain::Task::Ptr::create(); - auto task2 = Domain::Task::Ptr::create(); - auto taskProvider = Domain::QueryResultProvider::Ptr::create(); - auto taskResult = Domain::QueryResult::create(taskProvider); - taskProvider->append(task1); - taskProvider->append(task2); + data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1"))); + data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2"))); + QCOMPARE(data.items().count(), 2); - Utils::MockObject taskQueriesMock; - taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(taskResult); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(task1).thenReturn(Domain::QueryResult::Ptr()); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(task2).thenReturn(Domain::QueryResult::Ptr()); - taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); + auto serializer = deps->create(); - Utils::MockObject taskRepositoryMock; - taskRepositoryMock(&Domain::TaskRepository::promoteToProject).when(task2).thenReturn(new FakeJob(this)); + QVERIFY(!serializer->isProjectItem(data.items().at(1))); - Presentation::InboxPageModel inbox(taskQueriesMock.getInstance(), - taskRepositoryMock.getInstance()); + Presentation::InboxPageModel pageModel(deps->create(), + deps->create()); + QAbstractItemModel *model = pageModel.centralListModel(); + TestHelpers::waitForEmptyJobQueue(); + QCOMPARE(model->rowCount(), 2); // WHEN - const QModelIndex index = inbox.centralListModel()->index(1, 0); - inbox.promoteItem(index); + const QModelIndex index = model->index(1, 0); + pageModel.promoteItem(index); + TestHelpers::waitForEmptyJobQueue(); // THEN - QVERIFY(taskRepositoryMock(&Domain::TaskRepository::promoteToProject).when(task2).exactly(1)); + QCOMPARE(model->rowCount(), 1); + QCOMPARE(data.items().count(), 2); + QVERIFY(serializer->isProjectItem(data.items().at(1))); } void shouldGetAnErrorMessageWhenPromoteItemFailed() { // GIVEN + AkonadiFakeData data; + auto deps = data.createDependencies(); + Integration::initializeDefaultDomainDependencies(*deps.get()); + data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // Two tasks - auto task1 = Domain::Task::Ptr::create(); - auto task2 = Domain::Task::Ptr::create(); - task2->setTitle(QStringLiteral("task2")); - auto inboxProvider = Domain::QueryResultProvider::Ptr::create(); - auto inboxResult = Domain::QueryResult::create(inboxProvider); - inboxProvider->append(task1); - inboxProvider->append(task2); - - Utils::MockObject taskQueriesMock; - taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(inboxResult); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(task1).thenReturn(Domain::QueryResult::Ptr()); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(task2).thenReturn(Domain::QueryResult::Ptr()); - taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); - - Utils::MockObject taskRepositoryMock; - auto job = new FakeJob(this); - job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); - taskRepositoryMock(&Domain::TaskRepository::promoteToProject).when(task2).thenReturn(job); - - Presentation::InboxPageModel inbox(taskQueriesMock.getInstance(), - taskRepositoryMock.getInstance()); + data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1"))); + data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2"))); + QCOMPARE(data.items().count(), 2); + + Presentation::InboxPageModel pageModel(deps->create(), + deps->create()); + QAbstractItemModel *model = pageModel.centralListModel(); + TestHelpers::waitForEmptyJobQueue(); + QCOMPARE(model->rowCount(), 2); + FakeErrorHandler errorHandler; - inbox.setErrorHandler(&errorHandler); + pageModel.setErrorHandler(&errorHandler); // WHEN - const QModelIndex index = inbox.centralListModel()->index(1, 0); - inbox.promoteItem(index); + data.storageBehavior().setUpdateNextItemError(44, QStringLiteral("Foo")); + const QModelIndex index = model->index(1, 0); + pageModel.promoteItem(index); + TestHelpers::waitForEmptyJobQueue(); // THEN - QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot promote task task2 to be a project: Foo")); } void shouldGetAnErrorMessageWhenUpdateTaskFailed() { // GIVEN + AkonadiFakeData data; + auto deps = data.createDependencies(); + Integration::initializeDefaultDomainDependencies(*deps.get()); + data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // One task - auto rootTask = Domain::Task::Ptr::create(); - rootTask->setTitle(QStringLiteral("rootTask")); - auto inboxProvider = Domain::QueryResultProvider::Ptr::create(); - auto inboxResult = Domain::QueryResult::create(inboxProvider); - inboxProvider->append(rootTask); - auto taskProvider = Domain::QueryResultProvider::Ptr::create(); - auto taskResult = Domain::QueryResult::create(taskProvider); - - Utils::MockObject taskQueriesMock; - taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(inboxResult); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(rootTask).thenReturn(taskResult); - taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); - Utils::MockObject taskRepositoryMock; - - Presentation::InboxPageModel inbox(taskQueriesMock.getInstance(), - taskRepositoryMock.getInstance()); + data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("rootTask"))); + + auto serializer = deps->create(); + Presentation::InboxPageModel pageModel(deps->create(), + deps->create()); + QAbstractItemModel *model = pageModel.centralListModel(); + TestHelpers::waitForEmptyJobQueue(); - QAbstractItemModel *model = inbox.centralListModel(); - const QModelIndex rootTaskIndex = model->index(0, 0); FakeErrorHandler errorHandler; - inbox.setErrorHandler(&errorHandler); + pageModel.setErrorHandler(&errorHandler); - // WHEN - auto job = new FakeJob(this); - job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); - taskRepositoryMock(&Domain::TaskRepository::update).when(rootTask).thenReturn(job); + const QModelIndex index = model->index(0, 0); + auto rootTask = model->data(index, Presentation::QueryTreeModelBase::ObjectRole).value(); - QVERIFY(model->setData(rootTaskIndex, "newRootTask")); + // WHEN + data.storageBehavior().setUpdateNextItemError(1, QStringLiteral("Update error")); + QVERIFY(model->setData(index, "newRootTask")); + TestHelpers::waitForEmptyJobQueue(); // THEN - QTest::qWait(150); - QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot modify task rootTask in Inbox: Foo")); + QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot modify task rootTask in Inbox: Update error")); + QCOMPARE(rootTask->title(), QStringLiteral("newRootTask")); // Note that the task *did* get updated + QCOMPARE(index.data().toString(), QStringLiteral("newRootTask")); // and therefore the model keeps showing the new value + QCOMPARE(serializer->createTaskFromItem(data.item(42))->title(), QStringLiteral("rootTask")); // but the underlying data wasn't updated } void shouldGetAnErrorMessageWhenAssociateTaskFailed() { // GIVEN + AkonadiFakeData data; + auto deps = data.createDependencies(); + Integration::initializeDefaultDomainDependencies(*deps.get()); + data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); - // One task - auto rootTask = Domain::Task::Ptr::create(); - rootTask->setTitle(QStringLiteral("rootTask")); - auto inboxProvider = Domain::QueryResultProvider::Ptr::create(); - auto inboxResult = Domain::QueryResult::create(inboxProvider); - inboxProvider->append(rootTask); - auto taskProvider = Domain::QueryResultProvider::Ptr::create(); - auto taskResult = Domain::QueryResult::create(taskProvider); - - Utils::MockObject taskQueriesMock; - taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(inboxResult); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(rootTask).thenReturn(taskResult); - taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); - Utils::MockObject taskRepositoryMock; - - Presentation::InboxPageModel inbox(taskQueriesMock.getInstance(), - taskRepositoryMock.getInstance()); + // Three tasks + data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1"))); + data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2"))); + data.createItem(GenTodo().withId(44).withParent(42).withUid("44").withTitle(QStringLiteral("task3"))); + + Presentation::InboxPageModel pageModel(deps->create(), + deps->create()); + QAbstractItemModel *model = pageModel.centralListModel(); + TestHelpers::waitForEmptyJobQueue(); + QCOMPARE(model->rowCount(), 3); - QAbstractItemModel *model = inbox.centralListModel(); - const QModelIndex rootTaskIndex = model->index(0, 0); FakeErrorHandler errorHandler; - inbox.setErrorHandler(&errorHandler); + pageModel.setErrorHandler(&errorHandler); + const QModelIndex rootTaskIndex = model->index(0, 0); + + auto task2 = model->data(model->index(1, 0), Presentation::QueryTreeModelBase::ObjectRole).value(); + auto task3 = model->data(model->index(2, 0), Presentation::QueryTreeModelBase::ObjectRole).value(); // WHEN - auto childTask3 = Domain::Task::Ptr::create(); - childTask3->setTitle(QStringLiteral("childTask3")); - auto childTask4 = Domain::Task::Ptr::create(); - auto job = new FakeJob(this); - job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); - taskRepositoryMock(&Domain::TaskRepository::associate).when(rootTask, childTask3).thenReturn(job); - taskRepositoryMock(&Domain::TaskRepository::associate).when(rootTask, childTask4).thenReturn(new FakeJob(this)); - auto data = std::make_unique(); - data->setData(QStringLiteral("application/x-zanshin-object"), "object"); - data->setProperty("objects", QVariant::fromValue(Domain::Task::List() << childTask3 << childTask4)); - model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, rootTaskIndex); + data.storageBehavior().setUpdateNextItemError(1, QStringLiteral("Update error")); + + auto mimeData = std::make_unique(); + mimeData->setData(QStringLiteral("application/x-zanshin-object"), "object"); + mimeData->setProperty("objects", QVariant::fromValue(Domain::Task::List() << task2 << task3)); + QVERIFY(model->dropMimeData(mimeData.get(), Qt::MoveAction, -1, -1, rootTaskIndex)); + TestHelpers::waitForEmptyJobQueue(); // THEN - QTest::qWait(150); - QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot move task childTask3 as sub-task of rootTask: Foo")); - QVERIFY(taskRepositoryMock(&Domain::TaskRepository::associate).when(rootTask, childTask4).exactly(1)); + QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot move task task2 as sub-task of task1: Update error")); + QCOMPARE(model->rowCount(), 2); // One failed, one succeeded } void shouldDeparentWhenNoErrorHappens() { // GIVEN + AkonadiFakeData data; + auto deps = data.createDependencies(); + Integration::initializeDefaultDomainDependencies(*deps.get()); + data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // One task, top level - auto topLevelTask = Domain::Task::Ptr::create(); - topLevelTask->setTitle(QStringLiteral("topLevelTask")); - auto inboxProvider = Domain::QueryResultProvider::Ptr::create(); - auto inboxResult = Domain::QueryResult::create(inboxProvider); - inboxProvider->append(topLevelTask); + data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("topLevelTask"))); // Two tasks under the top level task - auto childTask = Domain::Task::Ptr::create(); - childTask->setTitle(QStringLiteral("childTask")); - childTask->setDone(true); - auto childTask2 = Domain::Task::Ptr::create(); - childTask2->setTitle(QStringLiteral("childTask2")); - childTask2->setDone(false); - auto taskProvider = Domain::QueryResultProvider::Ptr::create(); - auto taskResult = Domain::QueryResult::create(taskProvider); - taskProvider->append(childTask); - taskProvider->append(childTask2); - - Utils::MockObject taskQueriesMock; - taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(inboxResult); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(topLevelTask).thenReturn(taskResult); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(childTask).thenReturn(Domain::QueryResult::Ptr()); - taskQueriesMock(&Domain::TaskQueries::findChildren).when(childTask2).thenReturn(Domain::QueryResult::Ptr()); - taskQueriesMock(&Domain::TaskQueries::findDataSource).when(childTask).thenReturn(Domain::QueryResult::Ptr()); - - Utils::MockObject taskRepositoryMock; - - Presentation::InboxPageModel inbox(taskQueriesMock.getInstance(), - taskRepositoryMock.getInstance()); + data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withParentUid("42").withTitle(QStringLiteral("childTask")).done()); + data.createItem(GenTodo().withId(44).withParent(42).withUid("44").withParentUid("42").withTitle(QStringLiteral("childTask2"))); + + Presentation::InboxPageModel pageModel(deps->create(), + deps->create()); + QAbstractItemModel *model = pageModel.centralListModel(); + TestHelpers::waitForEmptyJobQueue(); - QAbstractItemModel *model = inbox.centralListModel(); const QModelIndex emptyPartModel = QModelIndex(); // model root, drop on the empty part is equivalent FakeErrorHandler errorHandler; - inbox.setErrorHandler(&errorHandler); + pageModel.setErrorHandler(&errorHandler); - // WHEN - auto job = new FakeJob(this); - job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); + const auto topLevelIndex = model->index(0, 0); + QVERIFY(topLevelIndex.isValid()); + const auto childTask = model->data(model->index(0, 0, topLevelIndex), Presentation::QueryTreeModelBase::ObjectRole).value(); + const auto childTask2 = model->data(model->index(1, 0, topLevelIndex), Presentation::QueryTreeModelBase::ObjectRole).value(); - taskRepositoryMock(&Domain::TaskRepository::dissociate).when(childTask).thenReturn(job); - taskRepositoryMock(&Domain::TaskRepository::dissociate).when(childTask2).thenReturn(new FakeJob(this)); + // WHEN + data.storageBehavior().setUpdateNextItemError(1, QStringLiteral("Deparent error")); - auto data = std::make_unique(); - data->setData(QStringLiteral("application/x-zanshin-object"), "object"); - data->setProperty("objects", QVariant::fromValue(Domain::Task::List() << childTask << childTask2)); // both will be DnD on the empty part - model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, emptyPartModel); + auto mimeData = std::make_unique(); + mimeData->setData(QStringLiteral("application/x-zanshin-object"), "object"); + mimeData->setProperty("objects", QVariant::fromValue(Domain::Task::List() << childTask << childTask2)); // both will be DnD on the empty part + QVERIFY(model->dropMimeData(mimeData.get(), Qt::MoveAction, -1, -1, emptyPartModel)); + TestHelpers::waitForEmptyJobQueue(); // THEN - QTest::qWait(150); - QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot deparent task childTask from its parent: Foo")); - QVERIFY(taskRepositoryMock(&Domain::TaskRepository::dissociate).when(childTask).exactly(1)); - QVERIFY(taskRepositoryMock(&Domain::TaskRepository::dissociate).when(childTask2).exactly(1)); + QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot deparent task childTask from its parent: Deparent error")); + QCOMPARE(model->rowCount(), 2); // One failed, one succeeded } }; ZANSHIN_TEST_MAIN(InboxPageModelTest) #include "inboxpagemodeltest.moc"