diff --git a/src/akonadi/akonadidatasourcequeries.cpp b/src/akonadi/akonadidatasourcequeries.cpp index 02c203f4..a7767ea6 100644 --- a/src/akonadi/akonadidatasourcequeries.cpp +++ b/src/akonadi/akonadidatasourcequeries.cpp @@ -1,103 +1,113 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonadidatasourcequeries.h" #include "akonadistoragesettings.h" using namespace Akonadi; DataSourceQueries::DataSourceQueries(StorageInterface::FetchContentTypes contentTypes, const StorageInterface::Ptr &storage, const SerializerInterface::Ptr &serializer, const MonitorInterface::Ptr &monitor) : m_contentTypes(contentTypes), m_serializer(serializer), m_helpers(new LiveQueryHelpers(serializer, storage)), m_integrator(new LiveQueryIntegrator(serializer, monitor)) { m_integrator->addRemoveHandler([this] (const Collection &collection) { m_findChildren.remove(collection.id()); }); } bool DataSourceQueries::isDefaultSource(Domain::DataSource::Ptr source) const { auto sourceCollection = m_serializer->createCollectionFromDataSource(source); if (m_contentTypes == StorageInterface::Tasks) return sourceCollection == StorageSettings::instance().defaultTaskCollection(); else if (m_contentTypes == StorageInterface::Notes) return sourceCollection == StorageSettings::instance().defaultNoteCollection(); else return false; } void DataSourceQueries::changeDefaultSource(Domain::DataSource::Ptr source) { auto sourceCollection = m_serializer->createCollectionFromDataSource(source); if (m_contentTypes == StorageInterface::Tasks) { StorageSettings::instance().setDefaultTaskCollection(sourceCollection); } else if (m_contentTypes == StorageInterface::Notes) { StorageSettings::instance().setDefaultNoteCollection(sourceCollection); } } DataSourceQueries::DataSourceResult::Ptr DataSourceQueries::findTopLevel() const { auto fetch = m_helpers->fetchCollections(Collection::root(), m_contentTypes); auto predicate = createFetchPredicate(Collection::root()); m_integrator->bind("DataSourceQueries::findTopLevel", m_findTopLevel, fetch, predicate); return m_findTopLevel->result(); } DataSourceQueries::DataSourceResult::Ptr DataSourceQueries::findChildren(Domain::DataSource::Ptr source) const { Collection root = m_serializer->createCollectionFromDataSource(source); auto &query = m_findChildren[root.id()]; auto fetch = m_helpers->fetchCollections(root, m_contentTypes); auto predicate = createFetchPredicate(root); m_integrator->bind("DataSourceQueries::findChildren", query, fetch, predicate); return query->result(); } +DataSourceQueries::DataSourceResult::Ptr DataSourceQueries::findAllSelected() const +{ + auto fetch = m_helpers->fetchAllCollections(m_contentTypes); + auto predicate = [this] (const Akonadi::Collection &collection) { + return collection.isValid() && m_serializer->isSelectedCollection(collection); + }; + m_integrator->bind("DataSourceQueries::findAllSelected", m_findAllSelected, fetch, predicate); + return m_findAllSelected->result(); +} + DataSourceQueries::ProjectResult::Ptr DataSourceQueries::findProjects(Domain::DataSource::Ptr source) const { Collection root = m_serializer->createCollectionFromDataSource(source); auto &query = m_findProjects[root.id()]; auto fetch = m_helpers->fetchItems(root); auto predicate = [this, root] (const Akonadi::Item &item) { return root == item.parentCollection() && m_serializer->isProjectItem(item); }; m_integrator->bind("DataSourceQueries::findProjects", query, fetch, predicate); return query->result(); } DataSourceQueries::CollectionInputQuery::PredicateFunction DataSourceQueries::createFetchPredicate(const Collection &root) const { return [this, root] (const Collection &collection) { return collection.isValid() && collection.parentCollection() == root; }; } diff --git a/src/akonadi/akonadidatasourcequeries.h b/src/akonadi/akonadidatasourcequeries.h index 399cca53..6dbe1a76 100644 --- a/src/akonadi/akonadidatasourcequeries.h +++ b/src/akonadi/akonadidatasourcequeries.h @@ -1,78 +1,80 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_DATASOURCEQUERIES_H #define AKONADI_DATASOURCEQUERIES_H #include "domain/datasourcequeries.h" #include "akonadi/akonadilivequeryhelpers.h" #include "akonadi/akonadilivequeryintegrator.h" namespace Akonadi { class DataSourceQueries : public Domain::DataSourceQueries { public: typedef QSharedPointer Ptr; typedef Domain::LiveQueryInput CollectionInputQuery; typedef Domain::LiveQueryOutput DataSourceQueryOutput; typedef Domain::QueryResultProvider DataSourceProvider; typedef Domain::QueryResult DataSourceResult; typedef Domain::LiveQueryInput ItemInputQuery; typedef Domain::LiveQueryOutput ProjectQueryOutput; typedef Domain::QueryResultProvider ProjectProvider; typedef Domain::QueryResult ProjectResult; DataSourceQueries(StorageInterface::FetchContentTypes contentTypes, const StorageInterface::Ptr &storage, const SerializerInterface::Ptr &serializer, const MonitorInterface::Ptr &monitor); bool isDefaultSource(Domain::DataSource::Ptr source) const Q_DECL_OVERRIDE; private: void changeDefaultSource(Domain::DataSource::Ptr source) Q_DECL_OVERRIDE; public: DataSourceResult::Ptr findTopLevel() const Q_DECL_OVERRIDE; DataSourceResult::Ptr findChildren(Domain::DataSource::Ptr source) const Q_DECL_OVERRIDE; + DataSourceResult::Ptr findAllSelected() const Q_DECL_OVERRIDE; ProjectResult::Ptr findProjects(Domain::DataSource::Ptr source) const Q_DECL_OVERRIDE; private: CollectionInputQuery::PredicateFunction createFetchPredicate(const Collection &root) const; StorageInterface::FetchContentTypes m_contentTypes; SerializerInterface::Ptr m_serializer; LiveQueryHelpers::Ptr m_helpers; LiveQueryIntegrator::Ptr m_integrator; mutable DataSourceQueryOutput::Ptr m_findTopLevel; mutable QHash m_findChildren; + mutable DataSourceQueryOutput::Ptr m_findAllSelected; mutable QHash m_findProjects; }; } #endif // AKONADI_DATASOURCEQUERIES_H diff --git a/src/domain/datasourcequeries.h b/src/domain/datasourcequeries.h index 1b0eefea..fdeccad2 100644 --- a/src/domain/datasourcequeries.h +++ b/src/domain/datasourcequeries.h @@ -1,76 +1,77 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOMAIN_DATASOURCEQUERIES_H #define DOMAIN_DATASOURCEQUERIES_H #include #include "datasource.h" #include "project.h" #include "queryresult.h" namespace Domain { class DataSourceQueries; class DataSourceQueriesNotifier : public QObject { Q_OBJECT signals: void defaultSourceChanged(); private: friend class DataSourceQueries; }; class DataSourceQueries { public: typedef QSharedPointer Ptr; DataSourceQueries(); virtual ~DataSourceQueries(); DataSourceQueriesNotifier *notifier() const; virtual bool isDefaultSource(DataSource::Ptr source) const = 0; void setDefaultSource(DataSource::Ptr source); // HACK: Ugly right? Find me another way to mock changeDefaultSource then... #ifdef ZANSHIN_I_SWEAR_I_AM_IN_A_PRESENTATION_TEST public: #else private: #endif virtual void changeDefaultSource(DataSource::Ptr source) = 0; public: virtual QueryResult::Ptr findTopLevel() const = 0; virtual QueryResult::Ptr findChildren(DataSource::Ptr source) const = 0; + virtual QueryResult::Ptr findAllSelected() const = 0; virtual QueryResult::Ptr findProjects(DataSource::Ptr source) const = 0; }; } #endif // DOMAIN_DATASOURCEQUERIES_H diff --git a/tests/units/akonadi/akonadidatasourcequeriestest.cpp b/tests/units/akonadi/akonadidatasourcequeriestest.cpp index a5be4103..52fe8e18 100644 --- a/tests/units/akonadi/akonadidatasourcequeriestest.cpp +++ b/tests/units/akonadi/akonadidatasourcequeriestest.cpp @@ -1,772 +1,979 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "akonadi/akonadidatasourcequeries.h" #include "akonadi/akonadiserializer.h" #include "akonadi/akonadistoragesettings.h" #include "utils/jobhandler.h" #include "utils/mem_fn.h" #include "testlib/akonadifakedata.h" #include "testlib/gencollection.h" #include "testlib/gentodo.h" #include "testlib/testhelpers.h" using namespace Testlib; typedef std::function::Ptr(Domain::DataSourceQueries*)> QueryFunction; Q_DECLARE_METATYPE(QueryFunction) typedef std::function SetDefaultCollectionFunction; Q_DECLARE_METATYPE(SetDefaultCollectionFunction) typedef std::function GetDefaultCollectionFunction; Q_DECLARE_METATYPE(GetDefaultCollectionFunction) Q_DECLARE_METATYPE(Akonadi::StorageInterface::FetchContentType) class AkonadiDataSourceQueriesTest : public QObject { Q_OBJECT public: explicit AkonadiDataSourceQueriesTest(QObject *parent = Q_NULLPTR) : QObject(parent) { qRegisterMetaType(); } private slots: void shouldCheckIfASourceIsDefaultFromSettings_data() { QTest::addColumn("contentType"); QTest::addColumn("setDefaultCollection"); QTest::addColumn("getDefaultCollection"); { SetDefaultCollectionFunction setFunction = Utils::mem_fn(&Akonadi::StorageSettings::setDefaultNoteCollection); GetDefaultCollectionFunction getFunction = Utils::mem_fn(&Akonadi::StorageSettings::defaultNoteCollection); QTest::newRow("notes") << Akonadi::StorageInterface::Notes << setFunction << getFunction; } { SetDefaultCollectionFunction setFunction = Utils::mem_fn(&Akonadi::StorageSettings::setDefaultTaskCollection); GetDefaultCollectionFunction getFunction = Utils::mem_fn(&Akonadi::StorageSettings::defaultTaskCollection); QTest::newRow("tasks") << Akonadi::StorageInterface::Tasks << setFunction << getFunction; } } void shouldCheckIfASourceIsDefaultFromSettings() { // GIVEN const auto minId = qint64(42); const auto maxId = qint64(44); const auto defaultId = qint64(43); QFETCH(Akonadi::StorageInterface::FetchContentType, contentType); QFETCH(SetDefaultCollectionFunction, setDefaultCollection); // A default collection for saving setDefaultCollection(&Akonadi::StorageSettings::instance(), Akonadi::Collection(defaultId)); // A few data sources AkonadiFakeData data; for (auto id = minId; id <= maxId; ++id) { auto col = GenCollection().withId(id).withName(QString::number(id)).withRootAsParent(); if (contentType == Akonadi::StorageInterface::Tasks) data.createCollection(col.withTaskContent()); else data.createCollection(col.withTaskContent()); } // WHEN auto serializer = Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(contentType, Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); // THEN for (auto id = minId; id <= maxId; ++id) { auto source = serializer->createDataSourceFromCollection(data.collection(id), Akonadi::SerializerInterface::BaseName); QCOMPARE(queries->isDefaultSource(source), id == defaultId); } } void shouldStoreDefaultSourceInTheSettingsAndNotify_data() { shouldCheckIfASourceIsDefaultFromSettings_data(); } void shouldStoreDefaultSourceInTheSettingsAndNotify() { // GIVEN const auto minId = qint64(42); const auto maxId = qint64(44); const auto defaultId = qint64(43); QFETCH(Akonadi::StorageInterface::FetchContentType, contentType); QFETCH(SetDefaultCollectionFunction, setDefaultCollection); // A default collection for saving setDefaultCollection(&Akonadi::StorageSettings::instance(), Akonadi::Collection(minId)); // A few data sources AkonadiFakeData data; for (auto id = minId; id <= maxId; ++id) { auto col = GenCollection().withId(id).withName(QString::number(id)).withRootAsParent(); if (contentType == Akonadi::StorageInterface::Tasks) data.createCollection(col.withTaskContent()); else data.createCollection(col.withTaskContent()); } // WHEN auto serializer = Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(contentType, Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); QSignalSpy spy(queries->notifier(), &Domain::DataSourceQueriesNotifier::defaultSourceChanged); auto defaultSource = serializer->createDataSourceFromCollection(data.collection(defaultId), Akonadi::SerializerInterface::BaseName); queries->setDefaultSource(defaultSource); // THEN QFETCH(GetDefaultCollectionFunction, getDefaultCollection); QCOMPARE(getDefaultCollection(&Akonadi::StorageSettings::instance()).id(), defaultId); QCOMPARE(spy.count(), 1); } void shouldLookInAllReportedForTopLevelSources_data() { QTest::addColumn("contentTypes"); QTest::addColumn("expectedNames"); auto expectedNames = QStringList(); expectedNames << QStringLiteral("42Task") << QStringLiteral("44Note"); QTest::newRow("tasks and notes") << int(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes) << expectedNames; expectedNames.clear(); expectedNames << QStringLiteral("42Task"); QTest::newRow("tasks") << int(Akonadi::StorageInterface::Tasks) << expectedNames; expectedNames.clear(); expectedNames << QStringLiteral("44Note"); QTest::newRow("notes") << int(Akonadi::StorageInterface::Notes) << expectedNames; } void shouldLookInAllReportedForTopLevelSources() { // GIVEN AkonadiFakeData data; // Two top level collections, one with tasks, one with notes data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent()); // One with a note child collection data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskChild")).withParent(42).withTaskContent()); // One with a task child collection data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45NoteChild")).withParent(44).withNoteContent()); // WHEN QFETCH(int, contentTypes); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::FetchContentTypes(contentTypes), Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findTopLevel(); result->data(); result = queries->findTopLevel(); // Should not cause any problem or wrong data // THEN QVERIFY(result->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); const auto sources = result->data(); auto actualNames = QStringList(); std::transform(sources.constBegin(), sources.constEnd(), std::back_inserter(actualNames), [] (const Domain::DataSource::Ptr &source) { return source->name(); }); actualNames.sort(); QFETCH(QStringList, expectedNames); expectedNames.sort(); QCOMPARE(actualNames, expectedNames); } void shouldReactToCollectionAddsForTopLevelSources() { // GIVEN AkonadiFakeData data; QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findTopLevel(); TestHelpers::waitForEmptyJobQueue(); QVERIFY(result->data().isEmpty()); // WHEN data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Note")).withRootAsParent().withNoteContent()); // THEN QCOMPARE(result->data().size(), 2); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42Task")); QCOMPARE(result->data().at(1)->name(), QStringLiteral("43Note")); } void shouldReactToCollectionRemovesForTopLevelSources() { // GIVEN AkonadiFakeData data; // Two top level collections and two child collections data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Note")).withRootAsParent().withNoteContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("43TaskChild")).withParent(42).withTaskContent()); data.createCollection(GenCollection().withId(45).withName(QStringLiteral("43NoteChild")).withParent(43).withNoteContent()); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findTopLevel(); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 2); // WHEN data.removeCollection(Akonadi::Collection(42)); data.removeCollection(Akonadi::Collection(43)); // THEN QCOMPARE(result->data().size(), 0); } void shouldReactToItemChangesForTopLevelTasks() { // GIVEN AkonadiFakeData data; // Two top level collections and one child collection data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Note")).withRootAsParent().withNoteContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44NoteChild")).withParent(43).withNoteContent()); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findTopLevel(); bool replaceHandlerCalled = false; result->addPostReplaceHandler([&replaceHandlerCalled](const Domain::DataSource::Ptr &, int) { replaceHandlerCalled = true; }); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 2); // WHEN data.modifyCollection(GenCollection(data.collection(42)).withName(QStringLiteral("42TaskBis"))); data.modifyCollection(GenCollection(data.collection(43)).withName(QStringLiteral("43NoteBis"))); TestHelpers::waitForEmptyJobQueue(); // THEN QCOMPARE(result->data().size(), 2); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42TaskBis")); QCOMPARE(result->data().at(1)->name(), QStringLiteral("43NoteBis")); QVERIFY(replaceHandlerCalled); } void shouldNotCrashDuringFindTopLevelWhenFetchJobFailedOrEmpty_data() { QTest::addColumn("colErrorCode"); QTest::addColumn("colFetchBehavior"); QTest::addColumn("deleteQuery"); QTest::newRow("No error with empty collection list") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << false; QTest::newRow("No error with empty collection list (+ query delete)") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << true; QTest::newRow("Error with empty collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << false; QTest::newRow("Error with empty collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << true; QTest::newRow("Error with collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) << false; QTest::newRow("Error with collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) << true; } void shouldNotCrashDuringFindTopLevelWhenFetchJobFailedOrEmpty() { // GIVEN AkonadiFakeData data; // Two top level collections data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withRootAsParent().withNoteContent()); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), Akonadi::MonitorInterface::Ptr(data.createMonitor()))); QFETCH(int, colErrorCode); QFETCH(int, colFetchBehavior); data.storageBehavior().setFetchCollectionsErrorCode(Akonadi::Collection::root().id(), colErrorCode); data.storageBehavior().setFetchCollectionsBehavior(Akonadi::Collection::root().id(), AkonadiFakeStorageBehavior::FetchBehavior(colFetchBehavior)); QFETCH(bool, deleteQuery); // WHEN Domain::QueryResult::Ptr result = queries->findTopLevel(); if (deleteQuery) delete queries.take(); // THEN QVERIFY(result->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 0); } void shouldLookInAllReportedForChildSources_data() { QTest::addColumn("contentTypes"); QTest::addColumn("expectedNames"); auto expectedNames = QStringList(); expectedNames << QStringLiteral("43TaskFirstChild") << QStringLiteral("45NoteSecondChild"); QTest::newRow("tasks and notes") << int(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes) << expectedNames; expectedNames.clear(); expectedNames << QStringLiteral("43TaskFirstChild"); QTest::newRow("tasks") << int(Akonadi::StorageInterface::Tasks) << expectedNames; expectedNames.clear(); expectedNames << QStringLiteral("45NoteSecondChild"); QTest::newRow("notes") << int(Akonadi::StorageInterface::Notes) << expectedNames; } void shouldLookInAllReportedForChildSources() { // GIVEN AkonadiFakeData data; // One top level collection with two children (one of them also having a child) data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskFirstChild")).withParent(42).withTaskContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44TaskFirstChildChild")).withParent(43).withTaskContent()); data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45NoteSecondChild")).withParent(42).withNoteContent()); // Serializer auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); // WHEN QFETCH(int, contentTypes); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::FetchContentTypes(contentTypes), Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findChildren(topLevelDataSource); result->data(); result = queries->findChildren(topLevelDataSource); // Should not cause any problem or wrong data // THEN QVERIFY(result->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); const auto sources = result->data(); auto actualNames = QStringList(); std::transform(sources.constBegin(), sources.constEnd(), std::back_inserter(actualNames), [] (const Domain::DataSource::Ptr &source) { return source->name(); }); actualNames.sort(); QFETCH(QStringList, expectedNames); expectedNames.sort(); QCOMPARE(actualNames, expectedNames); } void shouldReactToCollectionAddsForChildSources() { // GIVEN AkonadiFakeData data; // One top level collection with no child yet data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); // Serializer auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findChildren(topLevelDataSource); result->data(); result = queries->findChildren(topLevelDataSource); // Should not cause any problem or wrong data TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 0); // WHEN data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskChild")).withParent(42).withTaskContent()); // THEN QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().first()->name(), QStringLiteral("43TaskChild")); } void shouldReactToCollectionRemovesForChildSources() { // GIVEN AkonadiFakeData data; // One top level collection with two children data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskFirstChild")).withParent(42).withTaskContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("45NoteSecondChild")).withParent(42).withNoteContent()); // Serializer auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findChildren(topLevelDataSource); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 2); // WHEN data.removeCollection(Akonadi::Collection(44)); // THEN QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().first()->name(), QStringLiteral("43TaskFirstChild")); } void shouldReactToCollectionChangesForChildSources() { // GIVEN AkonadiFakeData data; // One top level collection with two children data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskFirstChild")).withParent(42).withTaskContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44NoteSecondChild")).withParent(42).withNoteContent()); // Serializer auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findChildren(topLevelDataSource); bool replaceHandlerCalled = false; result->addPostReplaceHandler([&replaceHandlerCalled](const Domain::DataSource::Ptr &, int) { replaceHandlerCalled = true; }); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 2); // WHEN data.modifyCollection(GenCollection(data.collection(43)).withName(QStringLiteral("43TaskFirstChildBis"))); // THEN TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 2); QCOMPARE(result->data().first()->name(), QStringLiteral("43TaskFirstChildBis")); QCOMPARE(result->data().at(1)->name(), QStringLiteral("44NoteSecondChild")); QVERIFY(replaceHandlerCalled); } void shouldNotCrashDuringFindChildrenWhenFetchJobFailedOrEmpty_data() { QTest::addColumn("colErrorCode"); QTest::addColumn("colFetchBehavior"); QTest::addColumn("deleteQuery"); QTest::newRow("No error with empty collection list") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << false; QTest::newRow("No error with empty collection list (+ query delete)") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << true; QTest::newRow("Error with empty collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << false; QTest::newRow("Error with empty collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) << true; QTest::newRow("Error with collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) << false; QTest::newRow("Error with collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) << true; } void shouldNotCrashDuringFindChildrenWhenFetchJobFailedOrEmpty() { // GIVEN AkonadiFakeData data; // One top level collection with two children data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskFirstChild")).withParent(42).withTaskContent()); data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44NoteSecondChild")).withParent(42).withNoteContent()); // Serializer auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); QFETCH(bool, deleteQuery); QFETCH(int, colErrorCode); QFETCH(int, colFetchBehavior); data.storageBehavior().setFetchCollectionsErrorCode(data.collection(42).id(), colErrorCode); data.storageBehavior().setFetchCollectionsBehavior(data.collection(42).id(), AkonadiFakeStorageBehavior::FetchBehavior(colFetchBehavior)); // WHEN QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::QueryResult::Ptr result = queries->findChildren(topLevelDataSource); if (deleteQuery) delete queries.take(); // THEN QVERIFY(result->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 0); } + void shouldLookInAllReportedForSelectedSources_data() + { + QTest::addColumn("contentTypes"); + QTest::addColumn("expectedNames"); + + auto expectedNames = QStringList(); + expectedNames << QStringLiteral("43Task") << QStringLiteral("45Note"); + QTest::newRow("tasks and notes") << int(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes) << expectedNames; + + expectedNames.clear(); + expectedNames << QStringLiteral("43Task"); + QTest::newRow("tasks") << int(Akonadi::StorageInterface::Tasks) << expectedNames; + + expectedNames.clear(); + expectedNames << QStringLiteral("45Note"); + QTest::newRow("notes") << int(Akonadi::StorageInterface::Notes) << expectedNames; + } + + void shouldLookInAllReportedForSelectedSources() + { + // GIVEN + AkonadiFakeData data; + + // Two top level collections, one with tasks, one with notes and two child collections + data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); + data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); + data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); + data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); + + // WHEN + QFETCH(int, contentTypes); + QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::FetchContentTypes(contentTypes), + Akonadi::StorageInterface::Ptr(data.createStorage()), + Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), + Akonadi::MonitorInterface::Ptr(data.createMonitor()))); + + Domain::QueryResult::Ptr result = queries->findAllSelected(); + result->data(); + result = queries->findAllSelected(); // Should not cause any problem or wrong data + + // THEN + QVERIFY(result->data().isEmpty()); + TestHelpers::waitForEmptyJobQueue(); + + const auto sources = result->data(); + auto actualNames = QStringList(); + std::transform(sources.constBegin(), sources.constEnd(), + std::back_inserter(actualNames), + [] (const Domain::DataSource::Ptr &source) { return source->name(); }); + actualNames.sort(); + + QFETCH(QStringList, expectedNames); + expectedNames.sort(); + QCOMPARE(actualNames, expectedNames); + } + + void shouldReactToCollectionAddsForSelectedSources() + { + // GIVEN + AkonadiFakeData data; + + QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, + Akonadi::StorageInterface::Ptr(data.createStorage()), + Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), + Akonadi::MonitorInterface::Ptr(data.createMonitor()))); + + Domain::QueryResult::Ptr result = queries->findAllSelected(); + TestHelpers::waitForEmptyJobQueue(); + QVERIFY(result->data().isEmpty()); + + // WHEN + data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); + data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); + data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); + data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); + + // THEN + QCOMPARE(result->data().size(), 2); + QCOMPARE(result->data().at(0)->name(), QStringLiteral("43Task")); + QCOMPARE(result->data().at(1)->name(), QStringLiteral("45Note")); + } + + void shouldReactToCollectionRemovesForSelectedSources() + { + // GIVEN + AkonadiFakeData data; + + // Two top level collections and two child collections + data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); + data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); + data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); + data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); + + QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, + Akonadi::StorageInterface::Ptr(data.createStorage()), + Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), + Akonadi::MonitorInterface::Ptr(data.createMonitor()))); + + Domain::QueryResult::Ptr result = queries->findAllSelected(); + TestHelpers::waitForEmptyJobQueue(); + QCOMPARE(result->data().size(), 2); + + // WHEN + data.removeCollection(Akonadi::Collection(43)); + data.removeCollection(Akonadi::Collection(45)); + + // THEN + QCOMPARE(result->data().size(), 0); + } + + void shouldReactToCollectionChangesForSelectedSources() + { + // GIVEN + AkonadiFakeData data; + + // Two top level collections and one child collection + data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); + data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); + data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); + data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); + + QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, + Akonadi::StorageInterface::Ptr(data.createStorage()), + Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), + Akonadi::MonitorInterface::Ptr(data.createMonitor()))); + + Domain::QueryResult::Ptr result = queries->findAllSelected(); + + bool replaceHandlerCalled = false; + result->addPostReplaceHandler([&replaceHandlerCalled](const Domain::DataSource::Ptr &, int) { + replaceHandlerCalled = true; + }); + TestHelpers::waitForEmptyJobQueue(); + QCOMPARE(result->data().size(), 2); + + // WHEN + data.modifyCollection(GenCollection(data.collection(43)).withName(QStringLiteral("43TaskBis"))); + data.modifyCollection(GenCollection(data.collection(45)).withName(QStringLiteral("45NoteBis"))); + TestHelpers::waitForEmptyJobQueue(); + + // THEN + QCOMPARE(result->data().size(), 2); + QCOMPARE(result->data().at(0)->name(), QStringLiteral("43TaskBis")); + QCOMPARE(result->data().at(1)->name(), QStringLiteral("45NoteBis")); + QVERIFY(replaceHandlerCalled); + } + + void shouldNotCrashDuringFindAllSelectedWhenFetchJobFailedOrEmpty_data() + { + QTest::addColumn("colErrorCode"); + QTest::addColumn("colFetchBehavior"); + QTest::addColumn("deleteQuery"); + + QTest::newRow("No error with empty collection list") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) + << false; + + QTest::newRow("No error with empty collection list (+ query delete)") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) + << true; + + QTest::newRow("Error with empty collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) + << false; + + QTest::newRow("Error with empty collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) + << true; + + QTest::newRow("Error with collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) + << false; + + QTest::newRow("Error with collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) + << true; + } + + void shouldNotCrashDuringFindAllSelectedWhenFetchJobFailedOrEmpty() + { + // GIVEN + AkonadiFakeData data; + + // Two top level collections and two child collections + data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); + data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); + data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); + data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); + + QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, + Akonadi::StorageInterface::Ptr(data.createStorage()), + Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), + Akonadi::MonitorInterface::Ptr(data.createMonitor()))); + QFETCH(int, colErrorCode); + QFETCH(int, colFetchBehavior); + data.storageBehavior().setFetchCollectionsErrorCode(Akonadi::Collection::root().id(), colErrorCode); + data.storageBehavior().setFetchCollectionsBehavior(Akonadi::Collection::root().id(), + AkonadiFakeStorageBehavior::FetchBehavior(colFetchBehavior)); + + QFETCH(bool, deleteQuery); + + // WHEN + Domain::QueryResult::Ptr result = queries->findAllSelected(); + + if (deleteQuery) + delete queries.take(); + + // THEN + QVERIFY(result->data().isEmpty()); + TestHelpers::waitForEmptyJobQueue(); + QCOMPARE(result->data().size(), 0); + } + void shouldLookInCollectionForProjects() { // GIVEN AkonadiFakeData data; // Two top level collections data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); data.createCollection(GenCollection().withId(43).withRootAsParent().withTaskContent()); // One project in the first collection data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); // Two projects in the second collection data.createItem(GenTodo().withId(43).withParent(43).withTitle(QStringLiteral("43")).asProject()); data.createItem(GenTodo().withId(44).withParent(43).withTitle(QStringLiteral("44")).asProject()); // WHEN auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::DataSource::Ptr dataSource1 = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); auto result1 = queries->findProjects(dataSource1); result1->data(); result1 = queries->findProjects(dataSource1); // Should not cause any problem or wrong data // THEN QVERIFY(result1->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result1->data().size(), 1); QCOMPARE(result1->data().at(0)->name(), QStringLiteral("42")); // WHEN Domain::DataSource::Ptr dataSource2 = serializer->createDataSourceFromCollection(data.collection(43), Akonadi::SerializerInterface::BaseName); auto result2 = queries->findProjects(dataSource2); result2->data(); result2 = queries->findProjects(dataSource2); // Should not cause any problem or wrong data // THEN QVERIFY(result2->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result1->data().size(), 1); QCOMPARE(result1->data().at(0)->name(), QStringLiteral("42")); QCOMPARE(result2->data().size(), 2); QCOMPARE(result2->data().at(0)->name(), QStringLiteral("43")); QCOMPARE(result2->data().at(1)->name(), QStringLiteral("44")); } void shouldIgnoreItemsWhichAreNotProjects() { // GIVEN AkonadiFakeData data; // One top level collection data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // One project and one regular task in the collection data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43"))); // WHEN auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::DataSource::Ptr dataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); auto result = queries->findProjects(dataSource); QVERIFY(result->data().isEmpty()); TestHelpers::waitForEmptyJobQueue(); // THEN QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); } void shouldReactToItemAddsForProjectsOnly() { // GIVEN AkonadiFakeData data; // One empty collection data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::DataSource::Ptr dataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); auto result = queries->findProjects(dataSource); TestHelpers::waitForEmptyJobQueue(); QVERIFY(result->data().isEmpty()); // WHEN data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43"))); // THEN QCOMPARE(result->data().size(), 1); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); } void shouldReactToItemRemovesForProjects() { // GIVEN AkonadiFakeData data; // One top level collection data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // Three projects in the collection data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43")).asProject()); data.createItem(GenTodo().withId(44).withParent(42).withTitle(QStringLiteral("44")).asProject()); auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::DataSource::Ptr dataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); auto result = queries->findProjects(dataSource); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 3); // WHEN data.removeItem(Akonadi::Item(43)); // THEN QCOMPARE(result->data().size(), 2); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); QCOMPARE(result->data().at(1)->name(), QStringLiteral("44")); } void shouldReactToItemChangesForProjects() { // GIVEN AkonadiFakeData data; // One top level collection data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); // Three projects in the collection data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43")).asProject()); data.createItem(GenTodo().withId(44).withParent(42).withTitle(QStringLiteral("44")).asProject()); auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); QScopedPointer queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks | Akonadi::StorageInterface::Notes, Akonadi::StorageInterface::Ptr(data.createStorage()), serializer, Akonadi::MonitorInterface::Ptr(data.createMonitor()))); Domain::DataSource::Ptr dataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); auto result = queries->findProjects(dataSource); // Even though the pointer didn't change it's convenient to user if we call // the replace handlers bool replaceHandlerCalled = false; result->addPostReplaceHandler([&replaceHandlerCalled](const Domain::Project::Ptr &, int) { replaceHandlerCalled = true; }); TestHelpers::waitForEmptyJobQueue(); QCOMPARE(result->data().size(), 3); // WHEN data.modifyItem(GenTodo(data.item(43)).withTitle(QStringLiteral("43bis"))); // THEN QCOMPARE(result->data().size(), 3); QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); QCOMPARE(result->data().at(1)->name(), QStringLiteral("43bis")); QCOMPARE(result->data().at(2)->name(), QStringLiteral("44")); QVERIFY(replaceHandlerCalled); } }; ZANSHIN_TEST_MAIN(AkonadiDataSourceQueriesTest) #include "akonadidatasourcequeriestest.moc"