diff --git a/src/akonadi/akonadistoragesettings.cpp b/src/akonadi/akonadistoragesettings.cpp index 54881705..4c3f8436 100644 --- a/src/akonadi/akonadistoragesettings.cpp +++ b/src/akonadi/akonadistoragesettings.cpp @@ -1,103 +1,104 @@ /* This file is part of Zanshin Todo. Copyright 2012 Christian Mollekopf 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 "akonadistoragesettings.h" #include #include using namespace Akonadi; StorageSettings::StorageSettings() : QObject() { } StorageSettings &StorageSettings::instance() { static StorageSettings i; return i; } Collection::List StorageSettings::activeCollections() { KConfigGroup config(KSharedConfig::openConfig(), "General"); QList ids = config.readEntry("activeCollections", QList()); Collection::List list; + list.reserve(ids.size()); foreach (const auto &id, ids) { list << Collection(id); } return list; } Collection StorageSettings::defaultNoteCollection() { KConfigGroup config(KSharedConfig::openConfig(), "General"); Collection::Id id = config.readEntry("defaultNoteCollection", -1); return Collection(id); } Collection StorageSettings::defaultTaskCollection() { KConfigGroup config(KSharedConfig::openConfig(), "General"); Collection::Id id = config.readEntry("defaultCollection", -1); return Collection(id); } void StorageSettings::setDefaultNoteCollection(const Collection &collection) { if (defaultNoteCollection() == collection) return; KConfigGroup config(KSharedConfig::openConfig(), "General"); config.writeEntry("defaultNoteCollection", QString::number(collection.id())); config.sync(); emit defaultNoteCollectionChanged(collection); } void StorageSettings::setDefaultTaskCollection(const Collection &collection) { if (defaultTaskCollection() == collection) return; KConfigGroup config(KSharedConfig::openConfig(), "General"); config.writeEntry("defaultCollection", QString::number(collection.id())); config.sync(); emit defaultTaskCollectionChanged(collection); } void StorageSettings::setActiveCollections(const Collection::List &collections) { if (activeCollections() == collections) return; QList ids; for (const auto &col : collections) { ids << col.id(); } KConfigGroup config(KSharedConfig::openConfig(), "General"); config.writeEntry("activeCollections", ids); config.sync(); emit activeCollectionsChanged(collections); } diff --git a/src/presentation/contextpagemodel.cpp b/src/presentation/contextpagemodel.cpp index bf9fd141..13a9ca3e 100644 --- a/src/presentation/contextpagemodel.cpp +++ b/src/presentation/contextpagemodel.cpp @@ -1,204 +1,205 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens Copyright 2014 Rémi Benoit This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "contextpagemodel.h" #include #include "domain/task.h" #include "domain/contextqueries.h" #include "domain/contextrepository.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" #include "presentation/querytreemodel.h" using namespace Presentation; ContextPageModel::ContextPageModel(const Domain::Context::Ptr &context, const Domain::ContextQueries::Ptr &contextQueries, const Domain::ContextRepository::Ptr &contextRepository, const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent) : PageModel(parent), m_context(context), m_contextQueries(contextQueries), m_contextRepository(contextRepository), m_taskQueries(taskQueries), m_taskRepository(taskRepository) { } Domain::Context::Ptr ContextPageModel::context() const { return m_context; } Domain::Artifact::Ptr ContextPageModel::addItem(const QString &title, const QModelIndex &parentIndex) { const auto parentData = parentIndex.data(QueryTreeModel::ObjectRole); const auto parentArtifact = parentData.value(); const auto parentTask = parentArtifact.objectCast(); auto task = Domain::Task::Ptr::create(); task->setTitle(title); const auto job = parentTask ? m_taskRepository->createChild(task, parentTask) : m_taskRepository->createInContext(task, m_context); installHandler(job, tr("Cannot add task %1 in context %2").arg(title, m_context->name())); return task; } void ContextPageModel::removeItem(const QModelIndex &index) { QVariant data = index.data(QueryTreeModel::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); const auto job = index.parent().isValid() ? m_taskRepository->dissociate(task) : m_contextRepository->dissociate(m_context, task); installHandler(job, tr("Cannot remove task %1 from context %2").arg(task->title(), m_context->name())); } void ContextPageModel::promoteItem(const QModelIndex &index) { QVariant data = index.data(QueryTreeModel::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); Q_ASSERT(task); const auto job = m_taskRepository->promoteToProject(task); installHandler(job, tr("Cannot promote task %1 to be a project").arg(task->title())); } QAbstractItemModel *ContextPageModel::createCentralListModel() { auto query = [this] (const Domain::Task::Ptr &task) -> Domain::QueryResultInterface::Ptr { if (!task) return m_contextQueries->findTopLevelTasks(m_context); else return m_taskQueries->findChildren(task); }; auto flags = [] (const Domain::Task::Ptr &task) { Q_UNUSED(task); return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled; }; auto data = [] (const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { return task->title(); } else if (role == Qt::CheckStateRole){ return task->isDone() ? Qt::Checked : Qt::Unchecked; } else { return QVariant(); } }; auto setData = [this] (const Domain::Task::Ptr &task, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) return false; const auto currentTitle = task->title(); if (role == Qt::EditRole) task->setTitle(value.toString()); else task->setDone(value.toInt() == Qt::Checked); const auto job = m_taskRepository->update(task); installHandler(job, tr("Cannot modify task %1 in context %2").arg(currentTitle, m_context->name())); return true; }; auto drop = [this] (const QMimeData *mimeData, Qt::DropAction, const Domain::Task::Ptr &parentTask) { if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))) return false; auto droppedArtifacts = mimeData->property("objects").value(); if (droppedArtifacts.isEmpty()) return false; if (std::any_of(droppedArtifacts.begin(), droppedArtifacts.end(), [](const Domain::Artifact::Ptr &droppedArtifact) { return !droppedArtifact.objectCast(); })) { return false; } using namespace std::placeholders; auto associate = std::function(); auto dissociate = std::function(); auto parentTitle = QString(); if (parentTask) { associate = std::bind(&Domain::TaskRepository::associate, m_taskRepository, parentTask, _1); dissociate = [] (Domain::Task::Ptr) -> KJob* { return Q_NULLPTR; }; parentTitle = parentTask->title(); } else { associate = std::bind(&Domain::ContextRepository::associate, m_contextRepository, m_context, _1); dissociate = std::bind(&Domain::TaskRepository::dissociate, m_taskRepository, _1); parentTitle = m_context->name(); } foreach(const Domain::Artifact::Ptr &droppedArtifact, droppedArtifacts) { auto childTask = droppedArtifact.objectCast(); auto job = associate(childTask); installHandler(job, tr("Cannot move task %1 as sub-task of %2").arg(childTask->title(), parentTitle)); job = dissociate(childTask); if (job) installHandler(job, tr("Cannot dissociate task %1 from its parent").arg(childTask->title())); } return true; }; auto drag = [] (const Domain::Task::List &tasks) -> QMimeData* { if (tasks.isEmpty()) return Q_NULLPTR; auto draggedArtifacts = Domain::Artifact::List(); + draggedArtifacts.reserve(tasks.size()); foreach (const Domain::Task::Ptr &task, tasks) { draggedArtifacts.append(task.objectCast()); } auto data = new QMimeData(); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(draggedArtifacts)); return data; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, this); } diff --git a/src/presentation/projectpagemodel.cpp b/src/presentation/projectpagemodel.cpp index aa764e5e..692ecefc 100644 --- a/src/presentation/projectpagemodel.cpp +++ b/src/presentation/projectpagemodel.cpp @@ -1,189 +1,190 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectpagemodel.h" #include #include "presentation/querytreemodel.h" using namespace Presentation; ProjectPageModel::ProjectPageModel(const Domain::Project::Ptr &project, const Domain::ProjectQueries::Ptr &projectQueries, const Domain::ProjectRepository::Ptr &projectRepository, const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent) : PageModel(parent), m_projectQueries(projectQueries), m_projectRepository(projectRepository), m_project(project), m_taskQueries(taskQueries), m_taskRepository(taskRepository) { } Domain::Project::Ptr ProjectPageModel::project() const { return m_project; } Domain::Artifact::Ptr ProjectPageModel::addItem(const QString &title, const QModelIndex &parentIndex) { const auto parentData = parentIndex.data(QueryTreeModel::ObjectRole); const auto parentArtifact = parentData.value(); const auto parentTask = parentArtifact.objectCast(); auto task = Domain::Task::Ptr::create(); task->setTitle(title); const auto job = parentTask ? m_taskRepository->createChild(task, parentTask) : m_taskRepository->createInProject(task, m_project); installHandler(job, tr("Cannot add task %1 in project %2").arg(title, m_project->name())); return task; } void ProjectPageModel::removeItem(const QModelIndex &index) { QVariant data = index.data(QueryTreeModel::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); Q_ASSERT(task); const auto job = m_taskRepository->remove(task); installHandler(job, tr("Cannot remove task %1 from project %2").arg(task->title(), m_project->name())); } void ProjectPageModel::promoteItem(const QModelIndex &index) { QVariant data = index.data(QueryTreeModel::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); Q_ASSERT(task); const auto job = m_taskRepository->promoteToProject(task); installHandler(job, tr("Cannot promote task %1 to be a project").arg(task->title())); } QAbstractItemModel *ProjectPageModel::createCentralListModel() { auto query = [this](const Domain::Task::Ptr &task) -> Domain::QueryResultInterface::Ptr { if (!task) return m_projectQueries->findTopLevel(m_project); else return m_taskQueries->findChildren(task); }; auto flags = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled; }; auto data = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { return task->title(); } else { return task->isDone() ? Qt::Checked : Qt::Unchecked; } }; auto setData = [this](const Domain::Task::Ptr &task, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) { return false; } const auto currentTitle = task->title(); if (role == Qt::EditRole) task->setTitle(value.toString()); else task->setDone(value.toInt() == Qt::Checked); const auto job = m_taskRepository->update(task); installHandler(job, tr("Cannot modify task %1 in project %2").arg(currentTitle, m_project->name())); return true; }; auto drop = [this](const QMimeData *mimeData, Qt::DropAction, const Domain::Task::Ptr &parentTask) { if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))) return false; auto droppedArtifacts = mimeData->property("objects").value(); if (droppedArtifacts.isEmpty()) return false; if (std::any_of(droppedArtifacts.begin(), droppedArtifacts.end(), [](const Domain::Artifact::Ptr &droppedArtifact) { return !droppedArtifact.objectCast(); })) { return false; } using namespace std::placeholders; auto associate = std::function(); auto parentTitle = QString(); if (parentTask) { associate = std::bind(&Domain::TaskRepository::associate, m_taskRepository, parentTask, _1); parentTitle = parentTask->title(); } else { associate = std::bind(&Domain::ProjectRepository::associate, m_projectRepository, m_project, _1); parentTitle = m_project->name(); } foreach(const Domain::Artifact::Ptr &droppedArtifact, droppedArtifacts) { auto childTask = droppedArtifact.objectCast(); const auto job = associate(childTask); installHandler(job, tr("Cannot move task %1 as a sub-task of %2").arg(childTask->title(), parentTitle)); } return true; }; auto drag = [](const Domain::Task::List &tasks) -> QMimeData* { if (tasks.isEmpty()) return Q_NULLPTR; auto draggedArtifacts = Domain::Artifact::List(); + draggedArtifacts.reserve(tasks.size()); foreach (const Domain::Task::Ptr &task, tasks) { draggedArtifacts.append(task.objectCast()); } auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(draggedArtifacts)); return data; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, this); } diff --git a/src/presentation/taskinboxpagemodel.cpp b/src/presentation/taskinboxpagemodel.cpp index 20276c43..59cf377f 100644 --- a/src/presentation/taskinboxpagemodel.cpp +++ b/src/presentation/taskinboxpagemodel.cpp @@ -1,173 +1,174 @@ /* 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 "taskinboxpagemodel.h" #include #include "presentation/querytreemodel.h" using namespace Presentation; TaskInboxPageModel::TaskInboxPageModel(const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent) : PageModel(parent), m_taskQueries(taskQueries), m_taskRepository(taskRepository) { } Domain::Artifact::Ptr TaskInboxPageModel::addItem(const QString &title, const QModelIndex &parentIndex) { const auto parentData = parentIndex.data(QueryTreeModel::ObjectRole); const auto parentArtifact = parentData.value(); const auto parentTask = parentArtifact.objectCast(); auto task = Domain::Task::Ptr::create(); task->setTitle(title); const auto job = parentTask ? m_taskRepository->createChild(task, parentTask) : m_taskRepository->create(task); installHandler(job, tr("Cannot add task %1 in Inbox").arg(title)); return task; } void TaskInboxPageModel::removeItem(const QModelIndex &index) { QVariant data = index.data(QueryTreeModel::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); Q_ASSERT(task); const auto job = m_taskRepository->remove(task); installHandler(job, tr("Cannot remove task %1 from Inbox").arg(task->title())); } void TaskInboxPageModel::promoteItem(const QModelIndex &index) { QVariant data = index.data(QueryTreeModel::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); Q_ASSERT(task); const auto job = m_taskRepository->promoteToProject(task); installHandler(job, tr("Cannot promote task %1 to be a project").arg(task->title())); } QAbstractItemModel *TaskInboxPageModel::createCentralListModel() { auto query = [this](const Domain::Task::Ptr &task) -> Domain::QueryResultInterface::Ptr { if (!task) return m_taskQueries->findInboxTopLevel(); else return m_taskQueries->findChildren(task); }; auto flags = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled; }; auto data = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { return task->title(); } else { return task->isDone() ? Qt::Checked : Qt::Unchecked; } }; auto setData = [this](const Domain::Task::Ptr &task, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) { return false; } const auto currentTitle = task->title(); if (role == Qt::EditRole) task->setTitle(value.toString()); else task->setDone(value.toInt() == Qt::Checked); const auto job = m_taskRepository->update(task); installHandler(job, tr("Cannot modify task %1 in Inbox").arg(currentTitle)); return true; }; auto drop = [this](const QMimeData *mimeData, Qt::DropAction, const Domain::Task::Ptr &task) { auto parentTask = task.objectCast(); if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))) return false; auto droppedArtifacts = mimeData->property("objects").value(); if (droppedArtifacts.isEmpty()) return false; if (std::any_of(droppedArtifacts.begin(), droppedArtifacts.end(), [](const Domain::Artifact::Ptr &droppedArtifact) { return !droppedArtifact.objectCast(); })) { return false; } foreach(const auto &droppedArtifact, droppedArtifacts) { auto childTask = droppedArtifact.objectCast(); if (parentTask) { const auto job = m_taskRepository->associate(parentTask, childTask); installHandler(job, tr("Cannot move task %1 as sub-task of %2").arg(childTask->title(), parentTask->title())); } else { const auto job = m_taskRepository->dissociate(childTask); installHandler(job, tr("Cannot deparent task %1 from its parent").arg(childTask->title())); } } return true; }; auto drag = [](const Domain::Task::List &tasks) -> QMimeData* { if (tasks.isEmpty()) return Q_NULLPTR; auto draggedArtifacts = Domain::Artifact::List(); + draggedArtifacts.reserve(tasks.size()); foreach (const Domain::Task::Ptr &task, tasks) { draggedArtifacts.append(task.objectCast()); } auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(draggedArtifacts)); return data; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, this); } diff --git a/tests/testlib/akonadistoragetestbase.cpp b/tests/testlib/akonadistoragetestbase.cpp index 48e50bc8..34012105 100644 --- a/tests/testlib/akonadistoragetestbase.cpp +++ b/tests/testlib/akonadistoragetestbase.cpp @@ -1,1462 +1,1466 @@ /* This file is part of Zanshin Copyright 2014-2015 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonadistoragetestbase.h" #include #include #include #include #include #include #include #include "utils/mem_fn.h" #include "AkonadiCore/qtest_akonadi.h" #include #include "akonadi/akonadiapplicationselectedattribute.h" #include "akonadi/akonadicollectionfetchjobinterface.h" #include "akonadi/akonadicollectionsearchjobinterface.h" #include "akonadi/akonadiitemfetchjobinterface.h" #include "akonadi/akonadimonitorimpl.h" #include "akonadi/akonadistorage.h" #include "akonadi/akonadistoragesettings.h" #include "akonadi/akonaditagfetchjobinterface.h" #include "akonadi/akonaditimestampattribute.h" using namespace Testlib; AkonadiStorageTestBase::AkonadiStorageTestBase(QObject *parent) : QObject(parent) { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); } void AkonadiStorageTestBase::dumpTree() { TestLib::AkonadiDebug::dumpTree(createStorage()); } void AkonadiStorageTestBase::shouldListCollections_data() { QTest::addColumn("collection"); QTest::addColumn("expectedNames"); QTest::addColumn("depth"); QTest::addColumn("contentTypes"); QTest::addColumn("referenceCalendar1"); QTest::addColumn("enableCalendar1"); QTest::newRow("all") << Akonadi::Collection::root() << QStringList({ "Calendar1", "Calendar2", "Calendar3", "Change me!", "Destroy me!", "Notes" }) << Akonadi::Storage::Recursive << int(Akonadi::StorageInterface::Notes|Akonadi::StorageInterface::Tasks) << false << true; QTest::newRow("include referenced") << Akonadi::Collection::root() << QStringList({ "Calendar1", "Calendar2", "Calendar3", "Change me!", "Destroy me!", "Notes" }) << Akonadi::Storage::Recursive << int(Akonadi::StorageInterface::Notes|Akonadi::StorageInterface::Tasks) << true << false; QTest::newRow("include referenced + enabled") << Akonadi::Collection::root() << QStringList({ "Calendar1", "Calendar2", "Calendar3", "Change me!", "Destroy me!", "Notes" }) << Akonadi::Storage::Recursive << int(Akonadi::StorageInterface::Notes|Akonadi::StorageInterface::Tasks) << true << true; QTest::newRow("exclude !referenced + !enabled") << Akonadi::Collection::root() << QStringList({ "Calendar2", "Calendar3", "Change me!", "Destroy me!", "Notes" }) << Akonadi::Storage::Recursive << int(Akonadi::StorageInterface::Notes|Akonadi::StorageInterface::Tasks) << false << false; QTest::newRow("notes") << Akonadi::Collection::root() << QStringList({ "Notes" }) << Akonadi::Storage::Recursive << int(Akonadi::StorageInterface::Notes) << false << true; QTest::newRow("tasks") << Akonadi::Collection::root() << QStringList({ "Calendar1", "Calendar2", "Calendar3", "Change me!", "Destroy me!" }) << Akonadi::Storage::Recursive << int(Akonadi::StorageInterface::Tasks) << false << true; QTest::newRow("base type") << calendar2() << QStringList({"Calendar2"}) << Akonadi::Storage::Base << int(Akonadi::StorageInterface::Tasks) << false << true; QTest::newRow("firstLevel type") << calendar1() << QStringList({"Calendar2"}) << Akonadi::Storage::FirstLevel << int(Akonadi::StorageInterface::Tasks) << false << true; QTest::newRow("recursive type") << calendar1() << QStringList({"Calendar2", "Calendar3"}) << Akonadi::Storage::Recursive << int(Akonadi::StorageInterface::Tasks) << false << true; } void AkonadiStorageTestBase::shouldListCollections() { // GIVEN QFETCH(Akonadi::Collection, collection); QFETCH(QStringList, expectedNames); QFETCH(Akonadi::StorageInterface::FetchDepth, depth); QFETCH(int, contentTypes); QFETCH(bool, referenceCalendar1); QFETCH(bool, enableCalendar1); auto storage = createStorage(); // Default is not referenced and enabled // no need to feedle with the collection in that case if (referenceCalendar1 || !enableCalendar1) { Akonadi::Collection cal1 = calendar1(); cal1.setReferenced(referenceCalendar1); cal1.setEnabled(enableCalendar1); auto update = storage->updateCollection(cal1); AKVERIFYEXEC(update); } // WHEN auto job = storage->fetchCollections(collection, depth, Akonadi::StorageInterface::FetchContentTypes(contentTypes)); AKVERIFYEXEC(job->kjob()); // THEN auto collections = job->collections(); QStringList collectionNames; + collectionNames.reserve(collections.size()); foreach (const auto &collection, collections) { collectionNames << collection.name(); } collectionNames.sort(); // Restore proper DB state if (referenceCalendar1 || !enableCalendar1) { Akonadi::Collection cal1 = calendar1(); cal1.setReferenced(false); cal1.setEnabled(true); auto update = storage->updateCollection(cal1); AKVERIFYEXEC(update); } QCOMPARE(collectionNames, expectedNames); } void AkonadiStorageTestBase::shouldRetrieveAllCollectionAncestors() { // GIVEN auto storage = createStorage(); // WHEN auto job = storage->fetchCollections(Akonadi::Collection::root(), Akonadi::Storage::Recursive, Akonadi::Storage::Tasks|Akonadi::Storage::Notes); AKVERIFYEXEC(job->kjob()); // THEN auto collections = job->collections(); foreach (const auto &collection, collections) { auto parent = collection.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); QVERIFY(!parent.displayName().isEmpty()); parent = parent.parentCollection(); } } } void AkonadiStorageTestBase::shouldListFullItemsInACollection() { // GIVEN auto storage = createStorage(); const QStringList expectedRemoteIds = { "{1d33862f-f274-4c67-ab6c-362d56521ff4}", "{1d33862f-f274-4c67-ab6c-362d56521ff5}", "{1d33862f-f274-4c67-ab6c-362d56521ff6}", "{7824df00-2fd6-47a4-8319-52659dc82005}", "{7824df00-2fd6-47a4-8319-52659dc82006}" }; // WHEN auto job = storage->fetchItems(calendar2()); AKVERIFYEXEC(job->kjob()); // THEN auto items = job->items(); QStringList itemRemoteIds; + itemRemoteIds.reserve(items.size()); foreach (const auto &item, items) { itemRemoteIds << item.remoteId(); QVERIFY(item.loadedPayloadParts().contains(Akonadi::Item::FullPayload)); QVERIFY(!item.attributes().isEmpty()); QVERIFY(item.modificationTime().isValid()); QVERIFY(!item.flags().isEmpty()); Akonadi::Tag::List tags = item.tags(); QVERIFY(!item.tags().isEmpty()); foreach (const auto &tag, tags) { QVERIFY(tag.isValid()); QVERIFY(!tag.name().isEmpty()); QVERIFY(!tag.type().isEmpty()); } auto parent = item.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } itemRemoteIds.sort(); QCOMPARE(itemRemoteIds, expectedRemoteIds); } void AkonadiStorageTestBase::shouldListTags() { // GIVEN auto storage = createStorage(); const QStringList expectedGids = { "change-me", "delete-me", "errands-context", "online-context", "philosophy-tag", "physics-tag" }; // WHEN auto job = storage->fetchTags(); AKVERIFYEXEC(job->kjob()); // THEN auto tags = job->tags(); QStringList tagGids; + tagGids.reserve(tags.size()); foreach (const auto &tag, tags) { tagGids << tag.gid(); QVERIFY(!tag.name().isEmpty()); QVERIFY(!tag.type().isEmpty()); } tagGids.sort(); QCOMPARE(tagGids, expectedGids); } void AkonadiStorageTestBase::shouldListItemsAssociatedWithTag() { // GIVEN auto storage = createStorage(); Akonadi::Tag tag = fetchTagByGID(QStringLiteral("errands-context")); const QStringList expectedRemoteIds = { "{1d33862f-f274-4c67-ab6c-362d56521ff4}", "{7824df00-2fd6-47a4-8319-52659dc82005}" }; // WHEN auto job = storage->fetchTagItems(tag); AKVERIFYEXEC(job->kjob()); // THEN auto items = job->items(); QStringList itemRemoteIds; + itemRemoteIds.reserve(items.size()); foreach (const auto &item, items) { itemRemoteIds << item.remoteId(); QVERIFY(item.loadedPayloadParts().contains(Akonadi::Item::FullPayload)); QVERIFY(!item.attributes().isEmpty()); QVERIFY(item.modificationTime().isValid()); QVERIFY(!item.flags().isEmpty()); auto parent = item.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } itemRemoteIds.sort(); QCOMPARE(itemRemoteIds, expectedRemoteIds); } void AkonadiStorageTestBase::shouldNotifyCollectionAdded() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::collectionAdded); MonitorSpy monitorSpy(monitor.data()); // A collection Akonadi::Collection collection; collection.setParentCollection(calendar2()); collection.setName(QStringLiteral("Foo!")); collection.setContentMimeTypes(QStringList() << QStringLiteral("application/x-vnd.akonadi.calendar.todo")); // WHEN auto storage = createStorage(); auto job = storage->createCollection(collection); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedCollection = spy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.name(), collection.name()); auto parent = notifiedCollection.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldNotifyCollectionRemoved() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::collectionRemoved); MonitorSpy monitorSpy(monitor.data()); // An existing item (if we trust the test data) Akonadi::Collection collection = fetchCollectionByRID(QStringLiteral("{1f78b360-a01b-4785-9187-75450190342c}")); QVERIFY(collection.isValid()); // WHEN auto storage = createStorage(); auto job = storage->removeCollection(collection); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedCollection= spy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); } void AkonadiStorageTestBase::shouldNotifyCollectionChanged() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::collectionChanged); MonitorSpy monitorSpy(monitor.data()); // A colection with an existing id (if we trust the test data) Akonadi::Collection collection = fetchCollectionByRID(QStringLiteral("{28ef9f03-4ebc-4e33-970f-f379775894f9}")); QVERIFY(collection.isValid()); collection.setName(QStringLiteral("Bar!")); // WHEN auto storage = createStorage(); auto job = storage->updateCollection(collection); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedCollection = spy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); QCOMPARE(notifiedCollection.name(), collection.name()); auto parent = notifiedCollection.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldNotifyItemAdded() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemAdded); MonitorSpy monitorSpy(monitor.data()); // A todo... KCalCore::Todo::Ptr todo(new KCalCore::Todo); todo->setSummary(QStringLiteral("summary")); todo->setDescription(QStringLiteral("content")); todo->setCompleted(false); todo->setDtStart(KDateTime(QDate(2013, 11, 24))); todo->setDtDue(KDateTime(QDate(2014, 03, 01))); // ... as payload of an item... Akonadi::Item item; item.setMimeType(QStringLiteral("application/x-vnd.akonadi.calendar.todo")); item.setPayload(todo); item.addAttribute(new Akonadi::EntityDisplayAttribute); // WHEN auto storage = createStorage(); auto job = storage->createItem(item, calendar2()); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(*notifiedItem.payload(), *todo); QVERIFY(notifiedItem.hasAttribute()); auto parent = notifiedItem.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldNotifyItemRemoved() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemRemoved); MonitorSpy monitorSpy(monitor.data()); const Akonadi::Collection notesCol = fetchCollectionByRID(QStringLiteral("{f5e3f1be-b998-4c56-aa3d-e3a6e7e5493a}")); Akonadi::Item item = fetchItemByRID(QStringLiteral("{d0159c99-0d23-41fa-bb5f-436570140f8b}"), notesCol); QVERIFY(item.isValid()); // WHEN auto storage = createStorage(); auto job = storage->removeItem(item); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item.id()); auto parent = notifiedItem.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldNotifyItemChanged() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemChanged); MonitorSpy monitorSpy(monitor.data()); // A todo... KCalCore::Todo::Ptr todo(new KCalCore::Todo); todo->setSummary(QStringLiteral("summary")); todo->setDescription(QStringLiteral("content")); todo->setCompleted(false); todo->setDtStart(KDateTime(QDate(2013, 11, 24))); todo->setDtDue(KDateTime(QDate(2014, 03, 01))); // ... as payload of an existing item (if we trust the test data)... Akonadi::Item item = fetchItemByRID(QStringLiteral("{1d33862f-f274-4c67-ab6c-362d56521ff6}"), calendar2()); QVERIFY(item.isValid()); item.setMimeType(QStringLiteral("application/x-vnd.akonadi.calendar.todo")); item.setPayload(todo); item.addAttribute(new Akonadi::EntityDisplayAttribute); // WHEN auto storage = createStorage(); auto job = storage->updateItem(item); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item.id()); QCOMPARE(*notifiedItem.payload(), *todo); QVERIFY(notifiedItem.hasAttribute()); auto parent = notifiedItem.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldNotifyItemTagAdded() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemChanged); MonitorSpy monitorSpy(monitor.data()); // An existing item (if we trust the test data)... Akonadi::Item item = fetchItemByRID(QStringLiteral("{1d33862f-f274-4c67-ab6c-362d56521ff5}"), calendar2()); QVERIFY(item.isValid()); item.setMimeType(QStringLiteral("application/x-vnd.akonadi.calendar.todo")); // An existing tag (if we trust the test data) Akonadi::Tag tag(5); // WHEN item.setTag(tag); auto storage = createStorage(); auto job = storage->updateItem(item); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item.id()); QVERIFY(notifiedItem.hasPayload()); Akonadi::Tag::List notifiedTags = notifiedItem.tags(); QVERIFY(notifiedTags.contains(tag)); foreach (const auto &tag, notifiedTags) { QVERIFY(tag.isValid()); QVERIFY(!tag.name().isEmpty()); QVERIFY(!tag.type().isEmpty()); } auto parent = notifiedItem.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldNotifyItemTagRemoved() // aka dissociate { // GIVEN auto storage = createStorage(); Akonadi::Tag tag = fetchTagByGID(QStringLiteral("philosophy-tag")); const QString expectedRemoteIds = {QStringLiteral("{7824df00-2fd6-47a4-8319-52659dc82006}")}; auto job = storage->fetchTagItems(tag); AKVERIFYEXEC(job->kjob()); auto item = job->items().at(0); QCOMPARE(item.remoteId(), expectedRemoteIds); QVERIFY(item.loadedPayloadParts().contains(Akonadi::Item::FullPayload)); QVERIFY(!item.attributes().isEmpty()); QVERIFY(item.modificationTime().isValid()); QVERIFY(!item.flags().isEmpty()); QVERIFY(!item.tags().isEmpty()); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemChanged); MonitorSpy monitorSpy(monitor.data()); // WHEN item.clearTag(tag); auto jobUpdate = storage->updateItem(item); AKVERIFYEXEC(jobUpdate); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item.id()); QVERIFY(!notifiedItem.tags().contains(tag)); } void AkonadiStorageTestBase::shouldNotifyTagAdded() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::tagAdded); MonitorSpy monitorSpy(monitor.data()); // A tag Akonadi::Tag tag; tag.setGid("gid"); tag.setName(QStringLiteral("name")); tag.setType("type"); // WHEN auto storage = createStorage(); auto job = storage->createTag(tag); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedTag = spy.takeFirst().at(0).value(); QCOMPARE(notifiedTag.gid(), tag.gid()); QCOMPARE(notifiedTag.name(), tag.name()); QCOMPARE(notifiedTag.type(), tag.type()); } void AkonadiStorageTestBase::shouldNotifyTagRemoved() { // GIVEN // An existing tag (if we trust the test data) connected to an existing item tagged to it auto storage = createStorage(); Akonadi::Tag tag = fetchTagByGID(QStringLiteral("delete-me")); // NOTE : this item was linked to the delete-me tag during test time const QString expectedRemoteIds = {QStringLiteral("{1d33862f-f274-4c67-ab6c-362d56521ff5}")}; auto job = storage->fetchTagItems(tag); AKVERIFYEXEC(job->kjob()); QCOMPARE(job->items().size(), 1); auto itemTagged = job->items().at(0); QCOMPARE(itemTagged.remoteId(), expectedRemoteIds); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::tagRemoved); QSignalSpy spyItemChanged(monitor.data(), &Akonadi::MonitorInterface::itemChanged); MonitorSpy monitorSpy(monitor.data()); // WHEN auto jobDelete = storage->removeTag(tag); AKVERIFYEXEC(jobDelete); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); QTRY_VERIFY(!spyItemChanged.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedTag = spy.takeFirst().at(0).value(); QCOMPARE(notifiedTag.id(), tag.id()); QCOMPARE(spyItemChanged.size(), 1); auto notifiedItem = spyItemChanged.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), itemTagged.id()); } void AkonadiStorageTestBase::shouldNotifyTagChanged() { // GIVEN // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::tagChanged); MonitorSpy monitorSpy(monitor.data()); // An existing tag (if we trust the test data) Akonadi::Tag tag(6); tag.setName(QStringLiteral("Oh it changed!")); // WHEN auto storage = createStorage(); auto job = storage->updateTag(tag); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedTag = spy.takeFirst().at(0).value(); QCOMPARE(notifiedTag.id(), tag.id()); QCOMPARE(notifiedTag.name(), tag.name()); } void AkonadiStorageTestBase::shouldReadDefaultNoteCollectionFromSettings() { // GIVEN // A storage implementation auto storage = createStorage(); // WHEN Akonadi::StorageSettings::instance().setDefaultNoteCollection(Akonadi::Collection(24)); // THEN QCOMPARE(storage->defaultNoteCollection(), Akonadi::Collection(24)); } void AkonadiStorageTestBase::shouldReadDefaultTaskCollectionFromSettings() { // GIVEN // A storage implementation auto storage = createStorage(); // WHEN Akonadi::StorageSettings::instance().setDefaultTaskCollection(Akonadi::Collection(24)); // THEN QCOMPARE(storage->defaultTaskCollection(), Akonadi::Collection(24)); } void AkonadiStorageTestBase::shouldUpdateItem() { // GIVEN // A storage implementation auto storage = createStorage(); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemChanged); MonitorSpy monitorSpy(monitor.data()); // A todo... KCalCore::Todo::Ptr todo(new KCalCore::Todo); todo->setSummary(QStringLiteral("new summary")); todo->setDescription(QStringLiteral("new content")); // ... as payload of an existing item (if we trust the test data)... Akonadi::Item item = fetchItemByRID(QStringLiteral("{1d33862f-f274-4c67-ab6c-362d56521ff4}"), calendar2()); item.setMimeType(QStringLiteral("application/x-vnd.akonadi.calendar.todo")); item.setPayload(todo); // WHEN auto job = storage->updateItem(item); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item.id()); // KCalCore 4.83 fixes this bug #if KCALCORE_VERSION < 0x045300 QCOMPARE(notifiedItem.payload()->uid(), todo->uid()); QCOMPARE(notifiedItem.payload()->summary(), todo->summary()); QCOMPARE(notifiedItem.payload()->description(), todo->description()); QEXPECT_FAIL("", "Bug introduced by 76c686bc1de3a5d16956a627744ce352bc28d12a in KCalCore", Continue); QCOMPARE(*notifiedItem.payload(), *todo); QEXPECT_FAIL("", "Bug introduced by 76c686bc1de3a5d16956a627744ce352bc28d12a in KCalCore", Continue); QCOMPARE(notifiedItem.payload()->status(), todo->status()); #else QCOMPARE(*notifiedItem.payload(), *todo); #endif } void AkonadiStorageTestBase::shouldUseTransaction() { // GIVEN auto storage = createStorage(); Akonadi::Item item1 = fetchItemByRID(QStringLiteral("{0aa4dc30-a2c2-4e08-8241-033b3344debc}"), calendar1()); QVERIFY(item1.isValid()); Akonadi::Item item2 = fetchItemByRID(QStringLiteral("{5dc1aba7-eead-4254-ba7a-58e397de1179}"), calendar1()); QVERIFY(item2.isValid()); // create wrong item Akonadi::Item item3(10000); item3.setRemoteId(QStringLiteral("wrongId")); // A spied monitor auto monitor = createMonitor(); QSignalSpy spyUpdated(monitor.data(), &Akonadi::MonitorInterface::itemChanged); MonitorSpy monitorSpy(monitor.data()); // WHEN auto todo = item1.payload(); todo->setSummary(QStringLiteral("Buy tomatoes")); todo = item2.payload(); todo->setSummary(QStringLiteral("Buy chocolate")); auto transaction = storage->createTransaction(); storage->updateItem(item1, transaction); storage->updateItem(item3, transaction); // this job should fail storage->updateItem(item2, transaction); QVERIFY(!transaction->exec()); monitorSpy.waitForStableState(); // THEN QCOMPARE(spyUpdated.size(), 0); auto job = storage->fetchItem(item1); AKVERIFYEXEC(job->kjob()); QCOMPARE(job->items().size(), 1); item1 = job->items().at(0); job = storage->fetchItem(item2); AKVERIFYEXEC(job->kjob()); QCOMPARE(job->items().size(), 1); item2 = job->items().at(0); QCOMPARE(item1.payload()->summary(), QStringLiteral("Buy kiwis")); QCOMPARE(item2.payload()->summary(), QStringLiteral("Buy cheese")); } void AkonadiStorageTestBase::shouldCreateItem() { // GIVEN // A storage implementation auto storage = createStorage(); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemAdded); MonitorSpy monitorSpy(monitor.data()); // A todo... KCalCore::Todo::Ptr todo(new KCalCore::Todo); todo->setSummary(QStringLiteral("summary")); todo->setDescription(QStringLiteral("content")); todo->setCompleted(false); todo->setDtStart(KDateTime(QDate(2013, 11, 24))); todo->setDtDue(KDateTime(QDate(2014, 03, 01))); // ... as payload of a new item Akonadi::Item item; item.setMimeType(QStringLiteral("application/x-vnd.akonadi.calendar.todo")); item.setPayload(todo); // WHEN auto job = storage->createItem(item, calendar2()); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.parentCollection(), calendar2()); QCOMPARE(*notifiedItem.payload(), *todo); } void AkonadiStorageTestBase::shouldRetrieveItem() { // GIVEN auto storage = createStorage(); Akonadi::Item findItem = fetchItemByRID(QStringLiteral("{7824df00-2fd6-47a4-8319-52659dc82005}"), calendar2()); QVERIFY(findItem.isValid()); // WHEN auto job = storage->fetchItem(findItem); AKVERIFYEXEC(job->kjob()); // THEN auto items = job->items(); QCOMPARE(items.size(), 1); const auto &item = items[0]; QCOMPARE(item.id(), findItem.id()); QVERIFY(item.loadedPayloadParts().contains(Akonadi::Item::FullPayload)); QVERIFY(!item.attributes().isEmpty()); QVERIFY(item.modificationTime().isValid()); QVERIFY(!item.flags().isEmpty()); auto parent = item.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); parent = parent.parentCollection(); } } void AkonadiStorageTestBase::shouldMoveItem() { // GIVEN auto storage = createStorage(); Akonadi::Item item = fetchItemByRID(QStringLiteral("{7824df00-2fd6-47a4-8319-52659dc82005}"), calendar2()); QVERIFY(item.isValid()); // A spied monitor auto monitor = createMonitor(); QSignalSpy spyMoved(monitor.data(), &Akonadi::MonitorInterface::itemMoved); MonitorSpy monitorSpy(monitor.data()); auto job = storage->moveItem(item, calendar1()); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spyMoved.isEmpty()); QCOMPARE(spyMoved.size(), 1); auto movedItem = spyMoved.takeFirst().at(0).value(); QCOMPARE(movedItem.id(), item.id()); } void AkonadiStorageTestBase::shouldMoveItems() { // GIVEN auto storage = createStorage(); Akonadi::Item item = fetchItemByRID(QStringLiteral("{1d33862f-f274-4c67-ab6c-362d56521ff4}"), calendar2()); QVERIFY(item.isValid()); Akonadi::Item::List list; list << item; // A spied monitor auto monitor = createMonitor(); QSignalSpy spyMoved(monitor.data(), &Akonadi::MonitorInterface::itemMoved); MonitorSpy monitorSpy(monitor.data()); auto job = storage->moveItems(list, calendar1()); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spyMoved.isEmpty()); QCOMPARE(spyMoved.size(), 1); auto movedItem = spyMoved.takeFirst().at(0).value(); QCOMPARE(movedItem.id(), item.id()); } void AkonadiStorageTestBase::shouldDeleteItem() { //GIVEN auto storage = createStorage(); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemRemoved); MonitorSpy monitorSpy(monitor.data()); // An existing item (if we trust the test data) Akonadi::Item item = fetchItemByRID(QStringLiteral("{0aa4dc30-a2c2-4e08-8241-033b3344debc}"), calendar1()); QVERIFY(item.isValid()); //When auto job = storage->removeItem(item); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item.id()); } void AkonadiStorageTestBase::shouldDeleteItems() { //GIVEN auto storage = createStorage(); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::itemRemoved); MonitorSpy monitorSpy(monitor.data()); // An existing item (if we trust the test data) Akonadi::Item item = fetchItemByRID(QStringLiteral("{6c7bf5b9-4136-4203-9f45-54e32ea0eacb}"), calendar1()); QVERIFY(item.isValid()); Akonadi::Item item2 = fetchItemByRID(QStringLiteral("{83cf0b15-8d61-436b-97ae-4bd88fb2fef9}"), calendar1()); QVERIFY(item2.isValid()); Akonadi::Item::List list; list << item << item2; //When auto job = storage->removeItems(list); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 2); auto notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item.id()); notifiedItem = spy.takeFirst().at(0).value(); QCOMPARE(notifiedItem.id(), item2.id()); } void AkonadiStorageTestBase::shouldCreateTag() { // GIVEN // A storage implementation auto storage = createStorage(); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::tagAdded); MonitorSpy monitorSpy(monitor.data()); // A tag Akonadi::Tag tag; QString name = QStringLiteral("Tag42"); const QByteArray type = QByteArray("Zanshin-Context"); const QByteArray gid = QByteArray(name.toLatin1()); tag.setName(name); tag.setType(QByteArray("Zanshin-Context")); tag.setGid(gid); // WHEN auto job = storage->createTag(tag); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedTag = spy.takeFirst().at(0).value(); QCOMPARE(notifiedTag.name(), name); QCOMPARE(notifiedTag.type(), type); QCOMPARE(notifiedTag.gid(), gid); } void AkonadiStorageTestBase::shouldRemoveTag() { // GIVEN auto storage = createStorage(); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::tagRemoved); MonitorSpy monitorSpy(monitor.data()); // An existing tag Akonadi::Tag tag = fetchTagByGID(QStringLiteral("errands-context")); // WHEN auto job = storage->removeTag(tag); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedTag = spy.takeFirst().at(0).value(); QCOMPARE(notifiedTag.id(), tag.id()); } void AkonadiStorageTestBase::shouldUpdateTag() { // GIVEN auto storage = createStorage(); // A spied monitor auto monitor = createMonitor(); QSignalSpy spy(monitor.data(), &Akonadi::MonitorInterface::tagChanged); MonitorSpy monitorSpy(monitor.data()); // An existing tag Akonadi::Tag tag = fetchTagByGID(QStringLiteral("change-me")); // WHEN auto job = storage->updateTag(tag); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!spy.isEmpty()); // THEN QCOMPARE(spy.size(), 1); auto notifiedTag = spy.takeFirst().at(0).value(); QCOMPARE(notifiedTag.id(), tag.id()); } void AkonadiStorageTestBase::shouldUpdateCollection() { // GIVEN // A storage implementation auto storage = createStorage(); // An existing collection Akonadi::Collection collection = calendar2(); // A spied monitor auto monitor = createMonitor(); QSignalSpy changeSpy(monitor.data(), &Akonadi::MonitorInterface::collectionChanged); QSignalSpy selectionSpy(monitor.data(), &Akonadi::MonitorInterface::collectionSelectionChanged); MonitorSpy monitorSpy(monitor.data()); // WHEN auto attr = new Akonadi::EntityDisplayAttribute; attr->setDisplayName(QStringLiteral("Foo")); collection.addAttribute(attr); auto job = storage->updateCollection(collection); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!changeSpy.isEmpty()); // THEN QCOMPARE(changeSpy.size(), 1); QCOMPARE(selectionSpy.size(), 0); auto notifiedCollection = changeSpy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); QVERIFY(notifiedCollection.hasAttribute()); QCOMPARE(notifiedCollection.attribute()->displayName(), attr->displayName()); } void AkonadiStorageTestBase::shouldNotifyCollectionTimestampChanges() { // GIVEN // A storage implementation auto storage = createStorage(); // An existing collection Akonadi::Collection collection = calendar2(); // A spied monitor auto monitor = createMonitor(); QSignalSpy changeSpy(monitor.data(), &Akonadi::MonitorInterface::collectionChanged); MonitorSpy monitorSpy(monitor.data()); // WHEN collection.attribute(Akonadi::Collection::AddIfMissing)->refreshTimestamp(); auto job = storage->updateCollection(collection); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!changeSpy.isEmpty()); // THEN QCOMPARE(changeSpy.size(), 1); auto notifiedCollection = changeSpy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); QVERIFY(notifiedCollection.hasAttribute()); } void AkonadiStorageTestBase::shouldNotifyCollectionSelectionChanges() { // GIVEN // A storage implementation auto storage = createStorage(); // An existing collection Akonadi::Collection collection = calendar2(); // A spied monitor auto monitor = createMonitor(); QSignalSpy changeSpy(monitor.data(), &Akonadi::MonitorInterface::collectionChanged); QSignalSpy selectionSpy(monitor.data(), &Akonadi::MonitorInterface::collectionSelectionChanged); MonitorSpy monitorSpy(monitor.data()); // WHEN auto attr = new Akonadi::ApplicationSelectedAttribute; attr->setSelected(false); collection.addAttribute(attr); auto job = storage->updateCollection(collection); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!changeSpy.isEmpty()); // THEN QCOMPARE(changeSpy.size(), 1); QCOMPARE(selectionSpy.size(), 1); auto notifiedCollection = changeSpy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); QVERIFY(notifiedCollection.hasAttribute()); QVERIFY(!notifiedCollection.attribute()->isSelected()); notifiedCollection = selectionSpy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); QVERIFY(notifiedCollection.hasAttribute()); QVERIFY(!notifiedCollection.attribute()->isSelected()); } void AkonadiStorageTestBase::shouldNotNotifyCollectionSelectionChangesForIrrelevantCollections() { // GIVEN // A storage implementation auto storage = createStorage(); // An existing collection Akonadi::Collection collection = emails(); // A spied monitor auto monitor = createMonitor(); QSignalSpy changeSpy(monitor.data(), &Akonadi::MonitorInterface::collectionChanged); QSignalSpy selectionSpy(monitor.data(), &Akonadi::MonitorInterface::collectionSelectionChanged); MonitorSpy monitorSpy(monitor.data()); // WHEN auto attr = new Akonadi::ApplicationSelectedAttribute; attr->setSelected(false); collection.addAttribute(attr); auto job = storage->updateCollection(collection); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!changeSpy.isEmpty()); // THEN QCOMPARE(changeSpy.size(), 1); QVERIFY(selectionSpy.isEmpty()); } void AkonadiStorageTestBase::shouldNotifyCollectionSubscriptionChanges_data() { QTest::addColumn("isEnabled"); QTest::addColumn("isReferenced"); QTest::newRow("enabled and !referenced") << true << false; // Fails randomly due to an akonadi bug... //QTest::newRow("!enabled and referenced") << false << true; QTest::newRow("!enabled and !referenced") << false << false; QTest::newRow("!enabled and referenced (again)") << false << true; QTest::newRow("enabled and !referenced (again)") << true << false; } void AkonadiStorageTestBase::shouldNotifyCollectionSubscriptionChanges() { // GIVEN QFETCH(bool, isEnabled); QFETCH(bool, isReferenced); // A storage implementation auto storage = createStorage(); // An existing collection Akonadi::Collection collection(calendar2().id()); // A spied monitor auto monitor = createMonitor(); QSignalSpy changeSpy(monitor.data(), &Akonadi::MonitorInterface::collectionChanged); MonitorSpy monitorSpy(monitor.data()); // WHEN static int run = 1; collection.attribute(Akonadi::Collection::AddIfMissing) ->setIconName(QStringLiteral("folder-%1").arg(run++)); collection.setEnabled(isEnabled); collection.setReferenced(isReferenced); auto job = storage->updateCollection(collection); AKVERIFYEXEC(job); monitorSpy.waitForStableState(); QTRY_VERIFY(!changeSpy.isEmpty()); // THEN QCOMPARE(changeSpy.size(), 1); auto notifiedCollection = changeSpy.takeFirst().at(0).value(); QCOMPARE(notifiedCollection.id(), collection.id()); QCOMPARE(notifiedCollection.enabled(), isEnabled); QCOMPARE(notifiedCollection.referenced(), isReferenced); } void AkonadiStorageTestBase::shouldFindCollectionsByName_data() { QTest::addColumn("name"); QTest::addColumn("contentType"); QTest::addColumn("expectedResults"); QTest::addColumn("referenceCalendar1"); QTest::addColumn("enableCalendar1"); QStringList expectedResults; expectedResults << QStringLiteral("Calendar1"); QTest::newRow("get a collection") << "Calendar1" << int(Akonadi::StorageInterface::Tasks) << expectedResults << false << true; expectedResults.clear(); QTest::newRow("try with wrong type") << "Calendar1" << int(Akonadi::StorageInterface::Notes) << expectedResults << false << true; expectedResults << QStringLiteral("Notes"); QTest::newRow("get a note collection") << "Not" << int(Akonadi::StorageInterface::Notes) << expectedResults << false << true; expectedResults.clear(); QTest::newRow("try with unknown name") << "toto" << int(Akonadi::StorageInterface::Tasks) << expectedResults << false << true; expectedResults << QStringLiteral("Calendar3") << QStringLiteral("Calendar2") << QStringLiteral("Calendar1"); QTest::newRow("try with a part of a name") << "Calendar" << int(Akonadi::StorageInterface::Tasks) << expectedResults << false << true; expectedResults.clear(); expectedResults << QStringLiteral("Calendar2"); QTest::newRow("make sure it is case insensitive") << "calendar2" << int(Akonadi::StorageInterface::Tasks) << expectedResults << false << true; expectedResults.clear(); expectedResults << QStringLiteral("Calendar1"); QTest::newRow("include referenced") << "Calendar1" << int(Akonadi::StorageInterface::Tasks) << expectedResults << true << false; QTest::newRow("include referenced + enabled") << "Calendar1" << int(Akonadi::StorageInterface::Tasks) << expectedResults << true << true; QTest::newRow("include !referenced + !enabled") << "Calendar1" << int(Akonadi::StorageInterface::Tasks) << expectedResults << false << false; } void AkonadiStorageTestBase::shouldFindCollectionsByName() { // GIVEN auto storage = createStorage(); QFETCH(QString, name); QFETCH(int, contentType); QFETCH(QStringList, expectedResults); QFETCH(bool, referenceCalendar1); QFETCH(bool, enableCalendar1); // A spied monitor auto monitor = createMonitor(); MonitorSpy monitorSpy(monitor.data()); // Default is not referenced and enabled // no need to feedle with the collection in that case if (referenceCalendar1 || !enableCalendar1) { Akonadi::Collection cal1 = calendar1(); cal1.setReferenced(referenceCalendar1); cal1.setEnabled(enableCalendar1); auto update = storage->updateCollection(cal1); AKVERIFYEXEC(update); monitorSpy.waitForStableState(); } // WHEN auto job = storage->searchCollections(name, Akonadi::StorageInterface::FetchContentType(contentType)); AKVERIFYEXEC(job->kjob()); // THEN auto collections = job->collections(); // Restore proper DB state if (referenceCalendar1 || !enableCalendar1) { Akonadi::Collection cal1 = calendar1(); cal1.setReferenced(false); cal1.setEnabled(true); auto update = storage->updateCollection(cal1); AKVERIFYEXEC(update); monitorSpy.waitForStableState(); } auto collectionNames = QStringList(); std::transform(collections.constBegin(), collections.constEnd(), std::back_inserter(collectionNames), Utils::mem_fn(&Akonadi::Collection::name)); QCOMPARE(collectionNames.toSet(), expectedResults.toSet()); } void AkonadiStorageTestBase::shouldFindCollectionsByNameIncludingTheirAncestors_data() { QTest::addColumn("searchTerm"); QTest::addColumn("contentType"); QTest::newRow("task search") << "Calendar3" << int(Akonadi::StorageInterface::Tasks); QTest::newRow("note search") << "Notes" << int(Akonadi::StorageInterface::Notes); } void AkonadiStorageTestBase::shouldFindCollectionsByNameIncludingTheirAncestors() { // GIVEN auto storage = createStorage(); // WHEN QFETCH(QString, searchTerm); QFETCH(int, contentType); auto job = storage->searchCollections(searchTerm, Akonadi::StorageInterface::FetchContentType(contentType)); AKVERIFYEXEC(job->kjob()); // THEN auto collections = job->collections(); foreach (const auto &collection, collections) { auto parent = collection.parentCollection(); while (parent != Akonadi::Collection::root()) { QVERIFY(parent.isValid()); QVERIFY(!parent.displayName().isEmpty()); parent = parent.parentCollection(); } } } Akonadi::Item AkonadiStorageTestBase::fetchItemByRID(const QString &remoteId, const Akonadi::Collection &collection) { Akonadi::Item item; item.setRemoteId(remoteId); auto job = createStorage()->fetchItem(item); job->setCollection(collection); if (!job->kjob()->exec()) { qWarning() << job->kjob()->errorString(); return Akonadi::Item(); } if (job->items().count() != 1) { qWarning() << "Received unexpected amount of items: " << job->items().count(); return Akonadi::Item(); } return job->items().at(0); } Akonadi::Collection AkonadiStorageTestBase::fetchCollectionByRID(const QString &remoteId) { Akonadi::Collection collection; collection.setRemoteId(remoteId); auto job = createStorage()->fetchCollections(collection, Akonadi::StorageInterface::Base, Akonadi::StorageInterface::AllContent); job->setResource(QStringLiteral("akonadi_knut_resource_0")); job->setFiltered(false); if (!job->kjob()->exec()) { qWarning() << job->kjob()->errorString() << remoteId; return Akonadi::Collection(); } if (job->collections().count() != 1) { qWarning() << "Received unexpected amount of collections: " << job->collections().count(); return Akonadi::Collection(); } return job->collections().at(0); } Akonadi::Tag AkonadiStorageTestBase::fetchTagByGID(const QString &gid) { auto job = createStorage()->fetchTags(); if (!job->kjob()->exec()) { qWarning() << job->kjob()->errorString(); return Akonadi::Tag(); } auto tags = job->tags(); foreach (const auto &tag, tags) { if (tag.gid() == gid) return tag; } return Akonadi::Tag(); } Akonadi::Collection AkonadiStorageTestBase::calendar1() { return fetchCollectionByRID(QStringLiteral("{cdc229c7-a9b5-4d37-989d-a28e372be2a9}")); } Akonadi::Collection AkonadiStorageTestBase::calendar2() { return fetchCollectionByRID(QStringLiteral("{e682b8b5-b67c-4538-8689-6166f64177f0}")); } Akonadi::Collection AkonadiStorageTestBase::emails() { return fetchCollectionByRID(QStringLiteral("{14096930-7bfe-46ca-8fba-7c04d3b62ec8}")); } diff --git a/tests/units/akonadi/akonadistoragesettingstest.cpp b/tests/units/akonadi/akonadistoragesettingstest.cpp index 5253327a..707f7049 100644 --- a/tests/units/akonadi/akonadistoragesettingstest.cpp +++ b/tests/units/akonadi/akonadistoragesettingstest.cpp @@ -1,167 +1,169 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "akonadi/akonadistoragesettings.h" using namespace Akonadi; Q_DECLARE_METATYPE(QSet) class AkonadiStorageSettingsTest : public QObject { Q_OBJECT public: explicit AkonadiStorageSettingsTest(QObject *parent = Q_NULLPTR) : QObject(parent) { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType>(); } private: KConfigGroup configGroup() { return KConfigGroup(KSharedConfig::openConfig(), "General"); } QList idList(int max) { QList list; + list.reserve(max); for (int i = 1; i < max; i++) { list << i; } return list; } Akonadi::Collection::List colList(int max) { Akonadi::Collection::List list; + list.reserve(max); foreach (auto id, idList(max)) { list << Collection(id); } return list; } private slots: void shouldReadFromConfigurationFile() { // GIVEN KConfigGroup g = configGroup(); for (int i = 1; i <= 18; i += 3) { // WHEN g.writeEntry("defaultCollection", i); g.writeEntry("defaultNoteCollection", i + 1); g.writeEntry("activeCollections", idList(i + 2)); // THEN QCOMPARE(StorageSettings::instance().defaultTaskCollection(), Collection(i)); QCOMPARE(StorageSettings::instance().defaultNoteCollection(), Collection(i + 1)); QCOMPARE(StorageSettings::instance().activeCollections(), colList(i + 2)); } } void shouldWriteToConfigurationFile() { // GIVEN KConfigGroup g = configGroup(); for (int i = 1; i <= 18; i += 3) { // WHEN StorageSettings::instance().setDefaultTaskCollection(Collection(i)); StorageSettings::instance().setDefaultNoteCollection(Collection(i + 1)); StorageSettings::instance().setActiveCollections(colList(i + 2)); // THEN QCOMPARE(g.readEntry("defaultCollection", -1), i); QCOMPARE(g.readEntry("defaultNoteCollection", -1), i + 1); QCOMPARE(g.readEntry("activeCollections", QList()), idList(i + 2)); } } void shouldNotifyTaskCollectionChanges() { StorageSettings &settings = StorageSettings::instance(); QSignalSpy spy(&settings, &Akonadi::StorageSettings::defaultTaskCollectionChanged); settings.setDefaultTaskCollection(Collection(2)); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().value(), Collection(2)); } void shouldNotNotifyIdenticalTaskCollectionChanges() { StorageSettings &settings = StorageSettings::instance(); settings.setDefaultTaskCollection(Collection(4)); QSignalSpy spy(&settings, &Akonadi::StorageSettings::defaultTaskCollectionChanged); settings.setDefaultTaskCollection(Collection(4)); QCOMPARE(spy.count(), 0); } void shouldNotifyNoteCollectionChanges() { StorageSettings &settings = StorageSettings::instance(); QSignalSpy spy(&settings, &Akonadi::StorageSettings::defaultNoteCollectionChanged); settings.setDefaultNoteCollection(Collection(2)); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().value(), Collection(2)); } void shouldNotNotifyIdenticalNoteCollectionChanges() { StorageSettings &settings = StorageSettings::instance(); settings.setDefaultNoteCollection(Collection(4)); QSignalSpy spy(&settings, &Akonadi::StorageSettings::defaultNoteCollectionChanged); settings.setDefaultNoteCollection(Collection(4)); QCOMPARE(spy.count(), 0); } void shouldNotifyActiveCollectionsChanges() { StorageSettings &settings = StorageSettings::instance(); QSignalSpy spy(&settings, &Akonadi::StorageSettings::activeCollectionsChanged); settings.setActiveCollections(colList(2)); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().value(), colList(2)); } void shouldNotNotifyIdenticalActiveCollectionsChanges() { StorageSettings &settings = StorageSettings::instance(); settings.setActiveCollections(colList(4)); QSignalSpy spy(&settings, &Akonadi::StorageSettings::activeCollectionsChanged); settings.setActiveCollections(colList(4)); QCOMPARE(spy.count(), 0); } }; ZANSHIN_TEST_MAIN(AkonadiStorageSettingsTest) #include "akonadistoragesettingstest.moc" diff --git a/tests/units/presentation/artifactfilterproxymodeltest.cpp b/tests/units/presentation/artifactfilterproxymodeltest.cpp index f0ab94b4..1dd5b0ae 100644 --- a/tests/units/presentation/artifactfilterproxymodeltest.cpp +++ b/tests/units/presentation/artifactfilterproxymodeltest.cpp @@ -1,255 +1,256 @@ /* 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/note.h" #include "domain/task.h" #include "presentation/artifactfilterproxymodel.h" #include "presentation/querytreemodelbase.h" Q_DECLARE_METATYPE(QList) class ArtifactFilterProxyModelTest : 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(QDateTime(start)); task->setDueDate(QDateTime(due)); auto item = new QStandardItem; item->setData(task->title(), Qt::DisplayRole); item->setData(QVariant::fromValue(Domain::Artifact::Ptr(task)), Presentation::QueryTreeModelBase::ObjectRole); return item; } QStandardItem *createNoteItem(const QString &title, const QString &text) const { auto note = Domain::Note::Ptr::create(); note->setTitle(title); note->setText(text); auto item = new QStandardItem; item->setData(note->title(), Qt::DisplayRole); item->setData(QVariant::fromValue(Domain::Artifact::Ptr(note)), Presentation::QueryTreeModelBase::ObjectRole); return item; } private slots: void shouldHaveDefaultState() { Presentation::ArtifactFilterProxyModel proxy; QVERIFY(!proxy.sourceModel()); QCOMPARE(proxy.sortColumn(), 0); QCOMPARE(proxy.sortOrder(), Qt::AscendingOrder); QCOMPARE(proxy.sortType(), Presentation::ArtifactFilterProxyModel::TitleSort); QCOMPARE(proxy.sortCaseSensitivity(), Qt::CaseInsensitive); } 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"))); input.appendRow(createNoteItem(QStringLiteral("4. foo"), QStringLiteral("find me"))); input.appendRow(createNoteItem(QStringLiteral("5. find me"), QStringLiteral("bar"))); input.appendRow(createNoteItem(QStringLiteral("6. baz"), QStringLiteral("baz"))); Presentation::ArtifactFilterProxyModel output; output.setSourceModel(&input); // WHEN output.setFilterFixedString(QStringLiteral("find me")); // THEN QCOMPARE(output.rowCount(), 4); QCOMPARE(output.index(0, 0).data().toString(), QStringLiteral("1. foo")); QCOMPARE(output.index(1, 0).data().toString(), QStringLiteral("2. Find Me")); QCOMPARE(output.index(2, 0).data().toString(), QStringLiteral("4. foo")); QCOMPARE(output.index(3, 0).data().toString(), QStringLiteral("5. find me")); } 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(createNoteItem(QStringLiteral("22. foo"), QStringLiteral("Find Me"))); item->appendRow(createTaskItem(QStringLiteral("23. find me"), QStringLiteral("foo"))); input.appendRow(item); input.appendRow(createTaskItem(QStringLiteral("3. baz"), QStringLiteral("baz"))); Presentation::ArtifactFilterProxyModel 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), 2); QCOMPARE(output.index(0, 0, parent).data().toString(), QStringLiteral("22. foo")); QCOMPARE(output.index(1, 0, parent).data().toString(), QStringLiteral("23. 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")) << createNoteItem(QStringLiteral("A"), QStringLiteral("foo")) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C"); QTest::newRow("title ascending") << int(Presentation::ArtifactFilterProxyModel::TitleSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo")) << createNoteItem(QStringLiteral("A"), QStringLiteral("foo")) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("C") << QStringLiteral("B") << QStringLiteral("A"); QTest::newRow("title descending") << int(Presentation::ArtifactFilterProxyModel::TitleSort) << int(Qt::DescendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(2014, 03, 10)) << createNoteItem(QStringLiteral("A"), QStringLiteral("foo")) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo"), QDate(2014, 03, 01)) << createTaskItem(QStringLiteral("D"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("C") << QStringLiteral("B") << QStringLiteral("D") << QStringLiteral("A"); QTest::newRow("start date ascending") << int(Presentation::ArtifactFilterProxyModel::DateSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(2014, 03, 10)) << createNoteItem(QStringLiteral("A"), QStringLiteral("foo")) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo"), QDate(2014, 03, 01)) << createTaskItem(QStringLiteral("D"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("A") << QStringLiteral("D") << QStringLiteral("B") << QStringLiteral("C"); QTest::newRow("start date descending") << int(Presentation::ArtifactFilterProxyModel::DateSort) << int(Qt::DescendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 10)) << createNoteItem(QStringLiteral("A"), QStringLiteral("foo")) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 01)) << createTaskItem(QStringLiteral("D"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("C") << QStringLiteral("B") << QStringLiteral("D") << QStringLiteral("A"); QTest::newRow("due date ascending") << int(Presentation::ArtifactFilterProxyModel::DateSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 10)) << createNoteItem(QStringLiteral("A"), QStringLiteral("foo")) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 01)) << createTaskItem(QStringLiteral("D"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("A") << QStringLiteral("D") << QStringLiteral("B") << QStringLiteral("C"); QTest::newRow("due date descending") << int(Presentation::ArtifactFilterProxyModel::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::ArtifactFilterProxyModel::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::ArtifactFilterProxyModel::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::ArtifactFilterProxyModel output; output.setSourceModel(&input); output.setSortType(Presentation::ArtifactFilterProxyModel::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(ArtifactFilterProxyModelTest) #include "artifactfilterproxymodeltest.moc" diff --git a/tests/units/presentation/querytreemodeltest.cpp b/tests/units/presentation/querytreemodeltest.cpp index 711c8fae..85d2dd36 100644 --- a/tests/units/presentation/querytreemodeltest.cpp +++ b/tests/units/presentation/querytreemodeltest.cpp @@ -1,980 +1,982 @@ /* This file is part of Zanshin Copyright 2014 Mario Bensi Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "utils/mockobject.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" #include "presentation/querytreemodel.h" #include "testlib/modeltest.h" using namespace mockitopp; Q_DECLARE_METATYPE(QModelIndex) Q_DECLARE_METATYPE(QList) class QueryTreeModelTest : public QObject { Q_OBJECT public: explicit QueryTreeModelTest(QObject *parent = Q_NULLPTR) : QObject(parent) { qRegisterMetaType(); } private: Domain::Task::List createTasks() const { Domain::Task::List result; const QStringList titles = {"first", "second", "third"}; const QList doneStates = {true, false, false}; Q_ASSERT(titles.size() == doneStates.size()); + result.reserve(titles.size()); for (int i = 0; i < titles.size(); i++) { auto task = Domain::Task::Ptr::create(); task->setTitle(titles.at(i)); task->setDone(doneStates.at(i)); result << task; } return result; } Domain::Task::List createChildrenTasks() const { Domain::Task::List result; const QStringList titles = {"childFirst", "childSecond", "childThird"}; const QList doneStates = {true, false, false}; Q_ASSERT(titles.size() == doneStates.size()); + result.reserve(titles.size()); for (int i = 0; i < titles.size(); i++) { auto task = Domain::Task::Ptr::create(); task->setTitle(titles.at(i)); task->setDone(doneStates.at(i)); result << task; } return result; } private slots: void shouldHaveRoleNames() { // GIVEN auto queryGenerator = [](const QColor &) { return Domain::QueryResult::Ptr(); }; auto flagsFunction = [](const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [](const QColor &, int) { return QVariant(); }; auto setDataFunction = [](const QColor &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction); // WHEN auto roles = model.roleNames(); // THEN QCOMPARE(roles.value(Qt::DisplayRole), QByteArray("display")); QCOMPARE(roles.value(Presentation::QueryTreeModel::ObjectRole), QByteArray("object")); QCOMPARE(roles.value(Presentation::QueryTreeModel::IconNameRole), QByteArray("icon")); QCOMPARE(roles.value(Presentation::QueryTreeModel::IsDefaultRole), QByteArray("default")); } void shouldListTasks() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); // WHEN auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model); // THEN QCOMPARE(model.rowCount(), 3); QCOMPARE(model.rowCount(model.index(0, 0)), 3); QCOMPARE(model.rowCount(model.index(1, 0)), 0); QCOMPARE(model.rowCount(model.index(2, 0)), 0); QCOMPARE(model.rowCount(model.index(0, 0, model.index(0, 0))), 0); QCOMPARE(model.rowCount(model.index(1, 0, model.index(0, 0))), 0); QCOMPARE(model.rowCount(model.index(2, 0, model.index(0, 0))), 0); QCOMPARE(model.rowCount(model.index(3, 0, model.index(0, 0))), 3); for (int i = 0; i < tasks.size(); i++) { auto task = tasks.at(i); auto index = model.index(i, 0); QCOMPARE(model.data(index), model.data(index, Qt::DisplayRole)); QCOMPARE(model.data(index).toString(), task->title()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Checked, task->isDone()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked, !task->isDone()); } for (int i = 0; i < childrenTasks.size(); i++) { auto task = childrenTasks.at(i); auto index = model.index(i, 0, model.index(0, 0)); QCOMPARE(model.data(index), model.data(index, Qt::DisplayRole)); QCOMPARE(model.data(index).toString(), task->title()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Checked, task->isDone()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked, !task->isDone()); } } void shouldDealWithNullQueriesProperly() { // GIVEN auto queryGenerator = [](const QString &) { return Domain::QueryResult::Ptr(); }; auto flagsFunction = [](const QString &) { return Qt::NoItemFlags; }; auto dataFunction = [](const QString &, int) { return QVariant(); }; auto setDataFunction = [](const QString &, const QVariant &, int) { return false; }; // WHEN Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model); // THEN QCOMPARE(model.rowCount(), 0); } void shouldReactToTaskAdd() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(tasks.at(1)); provider->append(tasks.at(2)); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model); QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); // WHEN queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); provider->insert(0, tasks.at(0)); // THEN QCOMPARE(aboutToBeInsertedSpy.size(), 1); QCOMPARE(aboutToBeInsertedSpy.first().at(0).value(), QModelIndex()); QCOMPARE(aboutToBeInsertedSpy.first().at(1).toInt(), 0); QCOMPARE(aboutToBeInsertedSpy.first().at(2).toInt(), 0); QCOMPARE(insertedSpy.size(), 1); QCOMPARE(insertedSpy.first().at(0).value(), QModelIndex()); QCOMPARE(insertedSpy.first().at(1).toInt(), 0); QCOMPARE(insertedSpy.first().at(2).toInt(), 0); } void shouldReactToChilrenTaskAdd() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); childrenProvider->append(childrenTasks.at(0)); childrenProvider->append(childrenTasks.at(1)); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model); QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); // WHEN queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); childrenProvider->insert(1, tasks.at(2)); // THEN QCOMPARE(aboutToBeInsertedSpy.size(), 1); QCOMPARE(aboutToBeInsertedSpy.first().at(0).value(), model.index(0, 0)); QCOMPARE(aboutToBeInsertedSpy.first().at(1).toInt(), 1); QCOMPARE(aboutToBeInsertedSpy.first().at(2).toInt(), 1); QCOMPARE(insertedSpy.size(), 1); QCOMPARE(insertedSpy.first().at(0).value(), model.index(0, 0)); QCOMPARE(insertedSpy.first().at(1).toInt(), 1); QCOMPARE(insertedSpy.first().at(2).toInt(), 1); } void shouldReactToTaskRemove() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model); QSignalSpy aboutToBeRemovedSpy(&model, &QAbstractItemModel::rowsAboutToBeRemoved); QSignalSpy removedSpy(&model, &QAbstractItemModel::rowsRemoved); QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); QModelIndex removeIndex = model.index(0, 0); // WHEN // Remove children childrenProvider->removeAt(0); childrenProvider->removeAt(0); childrenProvider->removeAt(0); // Move children to Top Level provider->append(childrenTasks.at(0)); provider->append(childrenTasks.at(1)); provider->append(childrenTasks.at(2)); // Remove firt element from topLevel provider->removeAt(0); // THEN QCOMPARE(aboutToBeRemovedSpy.size(), 4); QCOMPARE(removedSpy.size(), 4); for (int i = 0; i < aboutToBeRemovedSpy.size(); i++) { if (i != 3) QCOMPARE(aboutToBeRemovedSpy.at(i).at(0).value(), removeIndex); else QCOMPARE(aboutToBeRemovedSpy.at(i).at(0).value(), QModelIndex()); QCOMPARE(aboutToBeRemovedSpy.at(i).at(1).toInt(), 0); QCOMPARE(aboutToBeRemovedSpy.at(i).at(2).toInt(), 0); if (i != 3) QCOMPARE(removedSpy.at(i).at(0).value(), removeIndex); else QCOMPARE(removedSpy.at(i).at(0).value(), QModelIndex()); QCOMPARE(removedSpy.at(i).at(1).toInt(), 0); QCOMPARE(removedSpy.at(i).at(2).toInt(), 0); } QCOMPARE(aboutToBeInsertedSpy.size(), 3); QCOMPARE(insertedSpy.size(), 3); for (int i = 0; i < aboutToBeInsertedSpy.size(); i++) { QCOMPARE(aboutToBeInsertedSpy.at(i).at(0).value(), QModelIndex()); QCOMPARE(aboutToBeInsertedSpy.at(i).at(1).toInt(), i + 3); QCOMPARE(aboutToBeInsertedSpy.at(i).at(2).toInt(), i + 3); QCOMPARE(insertedSpy.at(i).at(0).value(), QModelIndex()); QCOMPARE(insertedSpy.at(i).at(1).toInt(), i + 3); QCOMPARE(insertedSpy.at(i).at(1).toInt(), i + 3); } } void shouldReactToTaskChange() { // GIVEN // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); // WHEN auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model); QSignalSpy dataChangedSpy(&model, &QAbstractItemModel::dataChanged); // WHEN tasks.at(2)->setDone(true); childrenTasks.at(2)->setDone(true); provider->replace(2, tasks.at(2)); childrenProvider->replace(2, tasks.at(2)); // THEN QCOMPARE(dataChangedSpy.size(), 2); QCOMPARE(dataChangedSpy.first().at(0).value(), model.index(2, 0)); QCOMPARE(dataChangedSpy.first().at(1).value(), model.index(2, 0)); QCOMPARE(dataChangedSpy.last().at(0).value(), model.index(2, 0, model.index(0, 0))); QCOMPARE(dataChangedSpy.last().at(1).value(), model.index(2, 0, model.index(0, 0))); } void shouldAllowEditsAndChecks() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); // WHEN auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model); // WHEN // Nothing particular // THEN for (int row = 0; row < tasks.size(); row++) { QVERIFY(model.flags(model.index(row, 0)) & Qt::ItemIsEditable); QVERIFY(model.flags(model.index(row, 0)) & Qt::ItemIsUserCheckable); } for (int row = 0; row < childrenTasks.size(); row++) { QVERIFY(model.flags(model.index(row, 0, model.index(0, 0))) & Qt::ItemIsEditable); QVERIFY(model.flags(model.index(row, 0, model.index(0, 0))) & Qt::ItemIsUserCheckable); } } void shouldSaveChanges() { // GIVEN auto tasks = createTasks(); const int taskPos = 1; const auto task = tasks[taskPos]; auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); Utils::MockObject repositoryMock; repositoryMock(&Domain::TaskRepository::update).when(task).thenReturn(Q_NULLPTR); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [&](const Domain::Task::Ptr &task, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) { return false; } if (role == Qt::EditRole) { task->setTitle(value.toString()); } else { task->setDone(value.toInt() == Qt::Checked); } repositoryMock.getInstance()->update(task); return true; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model); QSignalSpy titleChangedSpy(task.data(), &Domain::Task::titleChanged); QSignalSpy doneChangedSpy(task.data(), &Domain::Task::doneChanged); // WHEN const auto index = model.index(taskPos, 0); model.setData(index, "alternate second"); model.setData(index, Qt::Checked, Qt::CheckStateRole); // THEN QVERIFY(repositoryMock(&Domain::TaskRepository::update).when(task).exactly(2)); QCOMPARE(titleChangedSpy.size(), 1); QCOMPARE(titleChangedSpy.first().first().toString(), QStringLiteral("alternate second")); QCOMPARE(doneChangedSpy.size(), 1); QCOMPARE(doneChangedSpy.first().first().toBool(), true); } void shouldProvideUnderlyingObject() { // GIVEN auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(Qt::red); provider->append(Qt::green); provider->append(Qt::blue); auto queryGenerator = [&](const QColor &color) { if (!color.isValid()) return Domain::QueryResult::create(provider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [](const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [](const QColor &, int) { return QVariant(); }; auto setDataFunction = [](const QColor &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction); new ModelTest(&model); // WHEN const QModelIndex index = model.index(1, 0); const QVariant data = index.data(Presentation::QueryTreeModel::ObjectRole); // THEN QVERIFY(data.isValid()); QCOMPARE(data.value(), provider->data().at(1)); } void shouldProvideUnderlyingTaskAsArtifact() { // GIVEN auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, createTasks()) provider->append(task); auto queryGenerator = [&](const Domain::Task::Ptr &artifact) { if (!artifact) return Domain::QueryResult::create(provider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::NoItemFlags; }; auto dataFunction = [](const Domain::Task::Ptr &, int) { return QVariant(); }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction); new ModelTest(&model); // WHEN const QModelIndex index = model.index(1, 0); const QVariant data = index.data(Presentation::QueryTreeModel::ObjectRole); // THEN QVERIFY(data.isValid()); // Note it says Artifact and *not* Task here, it should up-cast automatically QVERIFY(!data.value().isNull()); QCOMPARE(data.value(), provider->data().at(1).staticCast()); } void shouldMoveOnlyDuringDragAndDrop() { // GIVEN auto queryGenerator = [&] (const QColor &) { return Domain::QueryResult::Ptr(); }; auto flagsFunction = [] (const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [] (const QColor &, int) { return QVariant(); }; auto setDataFunction = [] (const QColor &, const QVariant &, int) { return false; }; auto dropFunction = [] (const QMimeData *, Qt::DropAction, const QColor &) { return false; }; auto dragFunction = [] (const QList &) { return Q_NULLPTR; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction, dragFunction); // THEN QCOMPARE(model.supportedDragActions(), Qt::MoveAction); QCOMPARE(model.supportedDropActions(), Qt::MoveAction); } void shouldCreateMimeData() { // GIVEN auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(Qt::red); provider->append(Qt::green); provider->append(Qt::blue); auto queryGenerator = [&] (const QColor &color) { if (!color.isValid()) return Domain::QueryResult::create(provider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [] (const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [] (const QColor &, int) { return QVariant(); }; auto setDataFunction = [] (const QColor &, const QVariant &, int) { return false; }; auto dropFunction = [] (const QMimeData *, Qt::DropAction, const QColor &) { return false; }; auto dragFunction = [] (const QList &colors) { auto mimeData = new QMimeData; mimeData->setColorData(QVariant::fromValue(colors)); return mimeData; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction, dragFunction); new ModelTest(&model); // WHEN auto data = model.mimeData(QList() << model.index(1, 0) << model.index(2, 0)); // THEN QVERIFY(data); QVERIFY(model.mimeTypes().contains(QStringLiteral("application/x-zanshin-object"))); QList colors; colors << Qt::green << Qt::blue; QCOMPARE(data->colorData().value>(), colors); } void shouldDropMimeData_data() { QTest::addColumn("row"); QTest::addColumn("column"); QTest::addColumn("parentRow"); QTest::addColumn("callExpected"); QTest::newRow("drop on object") << -1 << -1 << 2 << true; QTest::newRow("drop between object") << 1 << 0 << -1 << true; QTest::newRow("drop in empty area") << -1 << -1 << -1 << true; } void shouldDropMimeData() { // GIVEN QFETCH(int, row); QFETCH(int, column); QFETCH(int, parentRow); QFETCH(bool, callExpected); bool dropCalled = false; const QMimeData *droppedData = Q_NULLPTR; QColor colorSeen; auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(Qt::red); provider->append(Qt::green); provider->append(Qt::blue); auto queryGenerator = [&] (const QColor &color) { if (!color.isValid()) return Domain::QueryResult::create(provider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [] (const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [] (const QColor &, int) { return QVariant(); }; auto setDataFunction = [] (const QColor &, const QVariant &, int) { return false; }; auto dropFunction = [&] (const QMimeData *data, Qt::DropAction, const QColor &color) { dropCalled = true; droppedData = data; colorSeen = color; return false; }; auto dragFunction = [] (const QList &) -> QMimeData* { return Q_NULLPTR; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction, dragFunction); new ModelTest(&model); // WHEN auto data = new QMimeData; const QModelIndex parent = parentRow >= 0 ? model.index(parentRow, 0) : QModelIndex(); model.dropMimeData(data, Qt::MoveAction, row, column, parent); // THEN QCOMPARE(dropCalled, callExpected); if (callExpected) { QCOMPARE(droppedData, data); QCOMPARE(colorSeen, parent.data(Presentation::QueryTreeModelBase::ObjectRole).value()); } } void shouldPreventCyclesByDragAndDrop() { // GIVEN bool dropCalled = false; auto topProvider = Domain::QueryResultProvider::Ptr::create(); topProvider->append(QStringLiteral("1")); topProvider->append(QStringLiteral("2")); topProvider->append(QStringLiteral("3")); auto firstLevelProvider = Domain::QueryResultProvider::Ptr::create(); firstLevelProvider->append(QStringLiteral("2.1")); firstLevelProvider->append(QStringLiteral("2.2")); firstLevelProvider->append(QStringLiteral("2.3")); auto secondLevelProvider = Domain::QueryResultProvider::Ptr::create(); secondLevelProvider->append(QStringLiteral("2.1.1")); secondLevelProvider->append(QStringLiteral("2.1.2")); secondLevelProvider->append(QStringLiteral("2.1.3")); auto queryGenerator = [&] (const QString &string) { if (string.isEmpty()) return Domain::QueryResult::create(topProvider); else if (string == QLatin1String("2")) return Domain::QueryResult::create(firstLevelProvider); else if (string == QLatin1String("2.1")) return Domain::QueryResult::create(secondLevelProvider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [] (const QString &) { return Qt::NoItemFlags; }; auto dataFunction = [] (const QString &, int) { return QVariant(); }; auto setDataFunction = [] (const QString &, const QVariant &, int) { return false; }; auto dropFunction = [&] (const QMimeData *, Qt::DropAction, const QString &) { dropCalled = true; return false; }; auto dragFunction = [] (const QStringList &strings) -> QMimeData* { auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(strings)); return data; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction, dragFunction); new ModelTest(&model); const auto indexes = QModelIndexList() << model.index(0, 0) << model.index(1, 0) << model.index(1, 0, model.index(1, 0)); // WHEN auto data = model.mimeData(indexes); const auto parent = model.index(1, 0, model.index(0, 0, model.index(1, 0))); model.dropMimeData(data, Qt::MoveAction, -1, -1, parent); // THEN QVERIFY(!dropCalled); } }; ZANSHIN_TEST_MAIN(QueryTreeModelTest) #include "querytreemodeltest.moc" diff --git a/tests/units/presentation/tasklistmodeltest.cpp b/tests/units/presentation/tasklistmodeltest.cpp index aa678a02..469c66a8 100644 --- a/tests/units/presentation/tasklistmodeltest.cpp +++ b/tests/units/presentation/tasklistmodeltest.cpp @@ -1,236 +1,237 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "utils/mockobject.h" #include "domain/taskrepository.h" #include "presentation/tasklistmodel.h" #include "testlib/modeltest.h" using namespace mockitopp; Q_DECLARE_METATYPE(QModelIndex) class TaskListModelTest : public QObject { Q_OBJECT public: explicit TaskListModelTest(QObject *parent = Q_NULLPTR) : QObject(parent) { qRegisterMetaType(); } private: Domain::Task::List createTasks() const { Domain::Task::List result; const QStringList titles = {"first", "second", "third"}; const QList doneStates = {true, false, false}; Q_ASSERT(titles.size() == doneStates.size()); + result.reserve(titles.size()); for (int i = 0; i < titles.size(); i++) { auto task = Domain::Task::Ptr::create(); task->setTitle(titles.at(i)); task->setDone(doneStates.at(i)); result << task; } return result; } private slots: void shouldListTasks() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto list = Domain::QueryResult::create(provider); // WHEN Presentation::TaskListModel model(list, Domain::TaskRepository::Ptr()); new ModelTest(&model); // THEN QCOMPARE(model.rowCount(), tasks.size()); QCOMPARE(model.rowCount(model.index(0)), 0); for (int i = 0; i < tasks.size(); i++) { auto task = tasks.at(i); auto index = model.index(i); QCOMPARE(model.data(index), model.data(index, Qt::DisplayRole)); QCOMPARE(model.data(index).toString(), task->title()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Checked, task->isDone()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked, !task->isDone()); } } void shouldReactToTaskAdd() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(tasks.at(0)); provider->append(tasks.at(1)); auto list = Domain::QueryResult::create(provider); Presentation::TaskListModel model(list, Domain::TaskRepository::Ptr()); new ModelTest(&model); QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); // WHEN provider->insert(1, tasks.at(2)); // THEN QCOMPARE(aboutToBeInsertedSpy.size(), 1); QCOMPARE(aboutToBeInsertedSpy.first().at(0).value(), QModelIndex()); QCOMPARE(aboutToBeInsertedSpy.first().at(1).toInt(), 1); QCOMPARE(aboutToBeInsertedSpy.first().at(2).toInt(), 1); QCOMPARE(insertedSpy.size(), 1); QCOMPARE(insertedSpy.first().at(0).value(), QModelIndex()); QCOMPARE(insertedSpy.first().at(1).toInt(), 1); QCOMPARE(insertedSpy.first().at(2).toInt(), 1); } void shouldReactToTaskRemove() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto list = Domain::QueryResult::create(provider); Presentation::TaskListModel model(list, Domain::TaskRepository::Ptr()); new ModelTest(&model); QSignalSpy aboutToBeRemovedSpy(&model, &QAbstractItemModel::rowsAboutToBeRemoved); QSignalSpy removedSpy(&model, &QAbstractItemModel::rowsRemoved); // WHEN provider->removeAt(0); // THEN QCOMPARE(aboutToBeRemovedSpy.size(), 1); QCOMPARE(aboutToBeRemovedSpy.first().at(0).value(), QModelIndex()); QCOMPARE(aboutToBeRemovedSpy.first().at(1).toInt(), 0); QCOMPARE(aboutToBeRemovedSpy.first().at(2).toInt(), 0); QCOMPARE(removedSpy.size(), 1); QCOMPARE(removedSpy.first().at(0).value(), QModelIndex()); QCOMPARE(removedSpy.first().at(1).toInt(), 0); QCOMPARE(removedSpy.first().at(2).toInt(), 0); } void shouldReactToTaskChange() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto list = Domain::QueryResult::create(provider); Presentation::TaskListModel model(list, Domain::TaskRepository::Ptr()); new ModelTest(&model); QSignalSpy dataChangedSpy(&model, &QAbstractItemModel::dataChanged); // WHEN tasks.at(2)->setDone(true); provider->replace(2, tasks.at(2)); // THEN QCOMPARE(dataChangedSpy.size(), 1); QCOMPARE(dataChangedSpy.first().at(0).value(), model.index(2)); QCOMPARE(dataChangedSpy.first().at(1).value(), model.index(2)); } void shouldAllowEditsAndChecks() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto list = Domain::QueryResult::create(provider); Presentation::TaskListModel model(list, Domain::TaskRepository::Ptr()); new ModelTest(&model); // WHEN // Nothing particular // THEN for (int row = 0; row < tasks.size(); row++) { QVERIFY(model.flags(model.index(row)) & Qt::ItemIsEditable); QVERIFY(model.flags(model.index(row)) & Qt::ItemIsUserCheckable); } } void shouldSaveChanges() { // GIVEN auto tasks = createTasks(); const int taskPos = 1; const auto task = tasks[taskPos]; auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto list = Domain::QueryResult::create(provider); Utils::MockObject repositoryMock; repositoryMock(&Domain::TaskRepository::update).when(task).thenReturn(Q_NULLPTR); Presentation::TaskListModel model(list, repositoryMock.getInstance()); new ModelTest(&model); QSignalSpy titleChangedSpy(task.data(), &Domain::Task::titleChanged); QSignalSpy doneChangedSpy(task.data(), &Domain::Task::doneChanged); // WHEN const auto index = model.index(taskPos); model.setData(index, "alternate second"); model.setData(index, Qt::Checked, Qt::CheckStateRole); // THEN QVERIFY(repositoryMock(&Domain::TaskRepository::update).when(task).exactly(2)); QCOMPARE(titleChangedSpy.size(), 1); QCOMPARE(titleChangedSpy.first().first().toString(), QStringLiteral("alternate second")); QCOMPARE(doneChangedSpy.size(), 1); QCOMPARE(doneChangedSpy.first().first().toBool(), true); } }; ZANSHIN_TEST_MAIN(TaskListModelTest) #include "tasklistmodeltest.moc"