File indexing completed on 2025-01-05 04:59:51
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Kevin Ottens <ervin@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0004 */ 0005 0006 0007 #include <testlib/qtest_zanshin.h> 0008 0009 #include "akonadi/akonadidatasourcequeries.h" 0010 #include "akonadi/akonadiserializer.h" 0011 #include "akonadi/akonadistoragesettings.h" 0012 0013 #include "utils/jobhandler.h" 0014 #include "utils/mem_fn.h" 0015 0016 #include "testlib/akonadifakedata.h" 0017 #include "testlib/gencollection.h" 0018 #include "testlib/gentodo.h" 0019 #include "testlib/testhelpers.h" 0020 0021 using namespace Testlib; 0022 0023 typedef std::function<Domain::QueryResult<Domain::DataSource::Ptr>::Ptr(Domain::DataSourceQueries*)> QueryFunction; 0024 Q_DECLARE_METATYPE(QueryFunction) 0025 typedef std::function<void(Akonadi::StorageSettings *, const Akonadi::Collection &)> SetDefaultCollectionFunction; 0026 Q_DECLARE_METATYPE(SetDefaultCollectionFunction) 0027 typedef std::function<Akonadi::Collection(Akonadi::StorageSettings *)> GetDefaultCollectionFunction; 0028 Q_DECLARE_METATYPE(GetDefaultCollectionFunction) 0029 0030 class AkonadiDataSourceQueriesTest : public QObject 0031 { 0032 Q_OBJECT 0033 public: 0034 explicit AkonadiDataSourceQueriesTest(QObject *parent = nullptr) 0035 : QObject(parent) 0036 { 0037 qRegisterMetaType<QueryFunction>(); 0038 } 0039 0040 private slots: 0041 void shouldCheckIfASourceIsDefaultFromSettings() 0042 { 0043 // GIVEN 0044 const auto minId = qint64(42); 0045 const auto maxId = qint64(44); 0046 const auto defaultId = qint64(43); 0047 0048 // A default collection for saving 0049 Akonadi::StorageSettings::instance().setDefaultCollection(Akonadi::Collection(defaultId)); 0050 0051 // A few data sources 0052 AkonadiFakeData data; 0053 for (auto id = minId; id <= maxId; ++id) { 0054 auto col = GenCollection().withId(id).withName(QString::number(id)).withRootAsParent(); 0055 data.createCollection(col.withTaskContent()); 0056 } 0057 0058 // WHEN 0059 auto serializer = Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer); 0060 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0061 serializer, 0062 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0063 0064 // THEN 0065 for (auto id = minId; id <= maxId; ++id) { 0066 auto source = serializer->createDataSourceFromCollection(data.collection(id), Akonadi::SerializerInterface::BaseName); 0067 QCOMPARE(queries->isDefaultSource(source), id == defaultId); 0068 } 0069 } 0070 0071 void shouldStoreDefaultSourceInTheSettingsAndNotify() 0072 { 0073 // GIVEN 0074 const auto minId = qint64(42); 0075 const auto maxId = qint64(44); 0076 const auto defaultId = qint64(43); 0077 0078 // A default collection for saving 0079 Akonadi::StorageSettings::instance().setDefaultCollection(Akonadi::Collection(minId)); 0080 0081 // A few data sources 0082 AkonadiFakeData data; 0083 for (auto id = minId; id <= maxId; ++id) { 0084 auto col = GenCollection().withId(id).withName(QString::number(id)).withRootAsParent(); 0085 data.createCollection(col.withTaskContent()); 0086 } 0087 0088 // WHEN 0089 auto serializer = Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer); 0090 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0091 serializer, 0092 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0093 QSignalSpy spy(queries->notifier(), &Domain::DataSourceQueriesNotifier::defaultSourceChanged); 0094 auto defaultSource = serializer->createDataSourceFromCollection(data.collection(defaultId), Akonadi::SerializerInterface::BaseName); 0095 queries->setDefaultSource(defaultSource); 0096 0097 // THEN 0098 QCOMPARE(Akonadi::StorageSettings::instance().defaultCollection().id(), defaultId); 0099 QCOMPARE(spy.count(), 1); 0100 } 0101 0102 void shouldLookInAllReportedForTopLevelSources() 0103 { 0104 // GIVEN 0105 AkonadiFakeData data; 0106 0107 // Two top level collections, one with tasks, one with notes 0108 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); 0109 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent()); 0110 0111 // One with a note child collection 0112 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskChild")).withParent(42).withTaskContent()); 0113 0114 // One with a task child collection 0115 data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45NoteChild")).withParent(44).withNoteContent()); 0116 0117 // WHEN 0118 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0119 Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0120 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0121 0122 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findTopLevel(); 0123 result->data(); 0124 result = queries->findTopLevel(); // Should not cause any problem or wrong data 0125 0126 // THEN 0127 QVERIFY(result->data().isEmpty()); 0128 TestHelpers::waitForEmptyJobQueue(); 0129 0130 const auto sources = result->data(); 0131 auto actualNames = QStringList(); 0132 std::transform(sources.constBegin(), sources.constEnd(), 0133 std::back_inserter(actualNames), 0134 [] (const Domain::DataSource::Ptr &source) { return source->name(); }); 0135 actualNames.sort(); 0136 0137 QCOMPARE(actualNames, QStringList() << "42Task"); 0138 } 0139 0140 void shouldReactToCollectionAddsForTopLevelSources() 0141 { 0142 // GIVEN 0143 AkonadiFakeData data; 0144 0145 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0146 Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0147 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0148 0149 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findTopLevel(); 0150 TestHelpers::waitForEmptyJobQueue(); 0151 QVERIFY(result->data().isEmpty()); 0152 0153 // WHEN 0154 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); 0155 0156 // THEN 0157 QCOMPARE(result->data().size(), 1); 0158 QCOMPARE(result->data().at(0)->name(), QStringLiteral("42Task")); 0159 } 0160 0161 void shouldReactToCollectionRemovesForTopLevelSources() 0162 { 0163 // GIVEN 0164 AkonadiFakeData data; 0165 0166 // Two top level collections and two child collections 0167 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); 0168 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Note")).withRootAsParent().withNoteContent()); 0169 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("43TaskChild")).withParent(42).withTaskContent()); 0170 data.createCollection(GenCollection().withId(45).withName(QStringLiteral("43NoteChild")).withParent(43).withNoteContent()); 0171 0172 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0173 Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0174 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0175 0176 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findTopLevel(); 0177 TestHelpers::waitForEmptyJobQueue(); 0178 QCOMPARE(result->data().size(), 1); 0179 0180 // WHEN 0181 data.removeCollection(Akonadi::Collection(42)); 0182 data.removeCollection(Akonadi::Collection(43)); 0183 0184 // THEN 0185 QCOMPARE(result->data().size(), 0); 0186 } 0187 0188 void shouldReactToItemChangesForTopLevelTasks() 0189 { 0190 // GIVEN 0191 AkonadiFakeData data; 0192 0193 // Two top level collections and one child collection 0194 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); 0195 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Note")).withRootAsParent().withNoteContent()); 0196 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44NoteChild")).withParent(43).withNoteContent()); 0197 0198 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0199 Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0200 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0201 0202 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findTopLevel(); 0203 0204 bool replaceHandlerCalled = false; 0205 result->addPostReplaceHandler([&replaceHandlerCalled](const Domain::DataSource::Ptr &, int) { 0206 replaceHandlerCalled = true; 0207 }); 0208 TestHelpers::waitForEmptyJobQueue(); 0209 QCOMPARE(result->data().size(), 1); 0210 0211 // WHEN 0212 data.modifyCollection(GenCollection(data.collection(42)).withName(QStringLiteral("42TaskBis"))); 0213 TestHelpers::waitForEmptyJobQueue(); 0214 0215 // THEN 0216 QCOMPARE(result->data().size(), 1); 0217 QCOMPARE(result->data().at(0)->name(), QStringLiteral("42TaskBis")); 0218 QVERIFY(replaceHandlerCalled); 0219 } 0220 0221 void shouldNotCrashDuringFindTopLevelWhenFetchJobFailedOrEmpty_data() 0222 { 0223 QTest::addColumn<int>("colErrorCode"); 0224 QTest::addColumn<int>("colFetchBehavior"); 0225 QTest::addColumn<bool>("deleteQuery"); 0226 0227 QTest::newRow("No error with empty collection list") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) 0228 << false; 0229 0230 QTest::newRow("No error with empty collection list (+ query delete)") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) 0231 << true; 0232 0233 QTest::newRow("Error with empty collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) 0234 << false; 0235 0236 QTest::newRow("Error with empty collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) 0237 << true; 0238 0239 QTest::newRow("Error with collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) 0240 << false; 0241 0242 QTest::newRow("Error with collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) 0243 << true; 0244 } 0245 0246 void shouldNotCrashDuringFindTopLevelWhenFetchJobFailedOrEmpty() 0247 { 0248 // GIVEN 0249 AkonadiFakeData data; 0250 0251 // Two top level collections 0252 data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); 0253 data.createCollection(GenCollection().withId(43).withRootAsParent().withNoteContent()); 0254 0255 std::unique_ptr<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0256 Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0257 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0258 QFETCH(int, colErrorCode); 0259 QFETCH(int, colFetchBehavior); 0260 data.storageBehavior().setFetchCollectionsErrorCode(Akonadi::Collection::root().id(), colErrorCode); 0261 data.storageBehavior().setFetchCollectionsBehavior(Akonadi::Collection::root().id(), 0262 AkonadiFakeStorageBehavior::FetchBehavior(colFetchBehavior)); 0263 0264 QFETCH(bool, deleteQuery); 0265 0266 // WHEN 0267 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findTopLevel(); 0268 0269 if (deleteQuery) 0270 delete queries.release(); 0271 0272 // THEN 0273 QVERIFY(result->data().isEmpty()); 0274 TestHelpers::waitForEmptyJobQueue(); 0275 QCOMPARE(result->data().size(), 0); 0276 } 0277 0278 void shouldLookInAllReportedForChildSources() 0279 { 0280 // GIVEN 0281 AkonadiFakeData data; 0282 0283 // One top level collection with two children (one of them also having a child) 0284 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); 0285 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskFirstChild")).withParent(42).withTaskContent()); 0286 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44TaskFirstChildChild")).withParent(43).withTaskContent()); 0287 data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45NoteSecondChild")).withParent(42).withNoteContent()); 0288 0289 // Serializer 0290 auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); 0291 Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); 0292 0293 // WHEN 0294 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0295 serializer, 0296 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0297 0298 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findChildren(topLevelDataSource); 0299 result->data(); 0300 result = queries->findChildren(topLevelDataSource); // Should not cause any problem or wrong data 0301 0302 // THEN 0303 QVERIFY(result->data().isEmpty()); 0304 TestHelpers::waitForEmptyJobQueue(); 0305 0306 const auto sources = result->data(); 0307 auto actualNames = QStringList(); 0308 std::transform(sources.constBegin(), sources.constEnd(), 0309 std::back_inserter(actualNames), 0310 [] (const Domain::DataSource::Ptr &source) { return source->name(); }); 0311 actualNames.sort(); 0312 0313 QCOMPARE(actualNames, QStringList() << QStringLiteral("43TaskFirstChild")); 0314 } 0315 0316 void shouldReactToCollectionAddsForChildSources() 0317 { 0318 // GIVEN 0319 AkonadiFakeData data; 0320 0321 // One top level collection with no child yet 0322 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); 0323 0324 // Serializer 0325 auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); 0326 Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); 0327 0328 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0329 serializer, 0330 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0331 0332 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findChildren(topLevelDataSource); 0333 result->data(); 0334 result = queries->findChildren(topLevelDataSource); // Should not cause any problem or wrong data 0335 TestHelpers::waitForEmptyJobQueue(); 0336 QCOMPARE(result->data().size(), 0); 0337 0338 // WHEN 0339 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskChild")).withParent(42).withTaskContent()); 0340 0341 // THEN 0342 QCOMPARE(result->data().size(), 1); 0343 QCOMPARE(result->data().first()->name(), QStringLiteral("43TaskChild")); 0344 } 0345 0346 void shouldReactToCollectionRemovesForChildSources() 0347 { 0348 // GIVEN 0349 AkonadiFakeData data; 0350 0351 // One top level collection with two children 0352 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); 0353 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskFirstChild")).withParent(42).withTaskContent()); 0354 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("45NoteSecondChild")).withParent(42).withNoteContent()); 0355 0356 // Serializer 0357 auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); 0358 Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); 0359 0360 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0361 serializer, 0362 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0363 0364 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findChildren(topLevelDataSource); 0365 TestHelpers::waitForEmptyJobQueue(); 0366 QCOMPARE(result->data().size(), 1); 0367 0368 // WHEN 0369 data.removeCollection(Akonadi::Collection(44)); 0370 0371 // THEN 0372 QCOMPARE(result->data().size(), 1); 0373 QCOMPARE(result->data().first()->name(), QStringLiteral("43TaskFirstChild")); 0374 } 0375 0376 void shouldReactToCollectionChangesForChildSources() 0377 { 0378 // GIVEN 0379 AkonadiFakeData data; 0380 0381 // One top level collection with two children 0382 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); 0383 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskFirstChild")).withParent(42).withTaskContent()); 0384 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44NoteSecondChild")).withParent(42).withNoteContent()); 0385 0386 // Serializer 0387 auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); 0388 Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); 0389 0390 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0391 serializer, 0392 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0393 0394 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findChildren(topLevelDataSource); 0395 bool replaceHandlerCalled = false; 0396 result->addPostReplaceHandler([&replaceHandlerCalled](const Domain::DataSource::Ptr &, int) { 0397 replaceHandlerCalled = true; 0398 }); 0399 TestHelpers::waitForEmptyJobQueue(); 0400 QCOMPARE(result->data().size(), 1); 0401 0402 // WHEN 0403 data.modifyCollection(GenCollection(data.collection(43)).withName(QStringLiteral("43TaskFirstChildBis"))); 0404 0405 // THEN 0406 TestHelpers::waitForEmptyJobQueue(); 0407 QCOMPARE(result->data().size(), 1); 0408 QCOMPARE(result->data().first()->name(), QStringLiteral("43TaskFirstChildBis")); 0409 QVERIFY(replaceHandlerCalled); 0410 } 0411 0412 void shouldNotCrashDuringFindChildrenWhenFetchJobFailedOrEmpty_data() 0413 { 0414 QTest::addColumn<int>("colErrorCode"); 0415 QTest::addColumn<int>("colFetchBehavior"); 0416 QTest::addColumn<bool>("deleteQuery"); 0417 0418 QTest::newRow("No error with empty collection list") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) 0419 << false; 0420 0421 QTest::newRow("No error with empty collection list (+ query delete)") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) 0422 << true; 0423 0424 QTest::newRow("Error with empty collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) 0425 << false; 0426 0427 QTest::newRow("Error with empty collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) 0428 << true; 0429 0430 QTest::newRow("Error with collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) 0431 << false; 0432 0433 QTest::newRow("Error with collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) 0434 << true; 0435 } 0436 0437 void shouldNotCrashDuringFindChildrenWhenFetchJobFailedOrEmpty() 0438 { 0439 // GIVEN 0440 AkonadiFakeData data; 0441 0442 // One top level collection with two children 0443 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); 0444 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43TaskFirstChild")).withParent(42).withTaskContent()); 0445 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44NoteSecondChild")).withParent(42).withNoteContent()); 0446 0447 // Serializer 0448 auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); 0449 Domain::DataSource::Ptr topLevelDataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); 0450 0451 QFETCH(bool, deleteQuery); 0452 QFETCH(int, colErrorCode); 0453 QFETCH(int, colFetchBehavior); 0454 data.storageBehavior().setFetchCollectionsErrorCode(data.collection(42).id(), colErrorCode); 0455 data.storageBehavior().setFetchCollectionsBehavior(data.collection(42).id(), 0456 AkonadiFakeStorageBehavior::FetchBehavior(colFetchBehavior)); 0457 0458 // WHEN 0459 std::unique_ptr<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0460 serializer, 0461 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0462 0463 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findChildren(topLevelDataSource); 0464 0465 if (deleteQuery) 0466 delete queries.release(); 0467 0468 // THEN 0469 QVERIFY(result->data().isEmpty()); 0470 TestHelpers::waitForEmptyJobQueue(); 0471 QCOMPARE(result->data().size(), 0); 0472 } 0473 0474 void shouldLookInAllReportedForSelectedSources() 0475 { 0476 // GIVEN 0477 AkonadiFakeData data; 0478 0479 // Two top level collections, one with tasks, one with notes and two child collections 0480 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); 0481 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); 0482 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); 0483 data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); 0484 0485 // WHEN 0486 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0487 Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0488 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0489 0490 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findAllSelected(); 0491 result->data(); 0492 result = queries->findAllSelected(); // Should not cause any problem or wrong data 0493 0494 // THEN 0495 QVERIFY(result->data().isEmpty()); 0496 TestHelpers::waitForEmptyJobQueue(); 0497 0498 const auto sources = result->data(); 0499 auto actualNames = QStringList(); 0500 std::transform(sources.constBegin(), sources.constEnd(), 0501 std::back_inserter(actualNames), 0502 [] (const Domain::DataSource::Ptr &source) { return source->name(); }); 0503 actualNames.sort(); 0504 0505 QCOMPARE(actualNames, QStringList() << QStringLiteral("42Task ยป 43Task")); 0506 } 0507 0508 void shouldReactToCollectionAddsForSelectedSources() 0509 { 0510 // GIVEN 0511 AkonadiFakeData data; 0512 0513 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0514 Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0515 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0516 0517 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findAllSelected(); 0518 TestHelpers::waitForEmptyJobQueue(); 0519 QVERIFY(result->data().isEmpty()); 0520 0521 // WHEN 0522 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); 0523 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); 0524 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); 0525 data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); 0526 0527 // THEN 0528 QCOMPARE(result->data().size(), 1); 0529 QCOMPARE(result->data().at(0)->name(), QStringLiteral("42Task ยป 43Task")); 0530 } 0531 0532 void shouldReactToCollectionRemovesForSelectedSources() 0533 { 0534 // GIVEN 0535 AkonadiFakeData data; 0536 0537 // Two top level collections and two child collections 0538 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); 0539 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); 0540 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); 0541 data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); 0542 0543 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0544 Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0545 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0546 0547 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findAllSelected(); 0548 TestHelpers::waitForEmptyJobQueue(); 0549 QCOMPARE(result->data().size(), 1); 0550 0551 // WHEN 0552 data.removeCollection(Akonadi::Collection(43)); 0553 data.removeCollection(Akonadi::Collection(45)); 0554 0555 // THEN 0556 QCOMPARE(result->data().size(), 0); 0557 } 0558 0559 void shouldReactToCollectionChangesForSelectedSources() 0560 { 0561 // GIVEN 0562 AkonadiFakeData data; 0563 0564 // Two top level collections and one child collection 0565 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); 0566 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); 0567 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); 0568 data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); 0569 0570 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0571 Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0572 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0573 0574 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findAllSelected(); 0575 0576 bool replaceHandlerCalled = false; 0577 result->addPostReplaceHandler([&replaceHandlerCalled](const Domain::DataSource::Ptr &, int) { 0578 replaceHandlerCalled = true; 0579 }); 0580 TestHelpers::waitForEmptyJobQueue(); 0581 QCOMPARE(result->data().size(), 1); 0582 0583 // WHEN 0584 data.modifyCollection(GenCollection(data.collection(43)).withName(QStringLiteral("43TaskBis"))); 0585 data.modifyCollection(GenCollection(data.collection(45)).withName(QStringLiteral("45NoteBis"))); 0586 TestHelpers::waitForEmptyJobQueue(); 0587 0588 // THEN 0589 QCOMPARE(result->data().size(), 1); 0590 QCOMPARE(result->data().at(0)->name(), QStringLiteral("42Task ยป 43TaskBis")); 0591 QVERIFY(replaceHandlerCalled); 0592 } 0593 0594 void shouldNotCrashDuringFindAllSelectedWhenFetchJobFailedOrEmpty_data() 0595 { 0596 QTest::addColumn<int>("colErrorCode"); 0597 QTest::addColumn<int>("colFetchBehavior"); 0598 QTest::addColumn<bool>("deleteQuery"); 0599 0600 QTest::newRow("No error with empty collection list") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) 0601 << false; 0602 0603 QTest::newRow("No error with empty collection list (+ query delete)") << int(KJob::NoError) << int(AkonadiFakeStorageBehavior::EmptyFetch) 0604 << true; 0605 0606 QTest::newRow("Error with empty collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) 0607 << false; 0608 0609 QTest::newRow("Error with empty collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::EmptyFetch) 0610 << true; 0611 0612 QTest::newRow("Error with collection list") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) 0613 << false; 0614 0615 QTest::newRow("Error with collection list (+ query delete)") << int(KJob::KilledJobError) << int(AkonadiFakeStorageBehavior::NormalFetch) 0616 << true; 0617 } 0618 0619 void shouldNotCrashDuringFindAllSelectedWhenFetchJobFailedOrEmpty() 0620 { 0621 // GIVEN 0622 AkonadiFakeData data; 0623 0624 // Two top level collections and two child collections 0625 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent().selected(false)); 0626 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent().selected(true)); 0627 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withRootAsParent().withNoteContent().selected(false)); 0628 data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Note")).withParent(44).withNoteContent().selected(true)); 0629 0630 std::unique_ptr<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0631 Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0632 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0633 QFETCH(int, colErrorCode); 0634 QFETCH(int, colFetchBehavior); 0635 data.storageBehavior().setFetchCollectionsErrorCode(Akonadi::Collection::root().id(), colErrorCode); 0636 data.storageBehavior().setFetchCollectionsBehavior(Akonadi::Collection::root().id(), 0637 AkonadiFakeStorageBehavior::FetchBehavior(colFetchBehavior)); 0638 0639 QFETCH(bool, deleteQuery); 0640 0641 // WHEN 0642 Domain::QueryResult<Domain::DataSource::Ptr>::Ptr result = queries->findAllSelected(); 0643 0644 if (deleteQuery) 0645 delete queries.release(); 0646 0647 // THEN 0648 QVERIFY(result->data().isEmpty()); 0649 TestHelpers::waitForEmptyJobQueue(); 0650 QCOMPARE(result->data().size(), 0); 0651 } 0652 0653 void shouldLookInCollectionForProjects() 0654 { 0655 // GIVEN 0656 AkonadiFakeData data; 0657 0658 // Two top level collections 0659 data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); 0660 data.createCollection(GenCollection().withId(43).withRootAsParent().withTaskContent()); 0661 0662 // One project in the first collection 0663 data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); 0664 0665 // Two projects in the second collection 0666 data.createItem(GenTodo().withId(43).withParent(43).withTitle(QStringLiteral("43")).asProject()); 0667 data.createItem(GenTodo().withId(44).withParent(43).withTitle(QStringLiteral("44")).asProject()); 0668 0669 // WHEN 0670 auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); 0671 std::unique_ptr<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0672 serializer, 0673 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0674 Domain::DataSource::Ptr dataSource1 = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); 0675 auto result1 = queries->findProjects(dataSource1); 0676 result1->data(); 0677 result1 = queries->findProjects(dataSource1); // Should not cause any problem or wrong data 0678 0679 // THEN 0680 QVERIFY(result1->data().isEmpty()); 0681 TestHelpers::waitForEmptyJobQueue(); 0682 0683 QCOMPARE(result1->data().size(), 1); 0684 QCOMPARE(result1->data().at(0)->name(), QStringLiteral("42")); 0685 0686 // WHEN 0687 Domain::DataSource::Ptr dataSource2 = serializer->createDataSourceFromCollection(data.collection(43), Akonadi::SerializerInterface::BaseName); 0688 auto result2 = queries->findProjects(dataSource2); 0689 result2->data(); 0690 result2 = queries->findProjects(dataSource2); // Should not cause any problem or wrong data 0691 0692 // THEN 0693 QVERIFY(result2->data().isEmpty()); 0694 TestHelpers::waitForEmptyJobQueue(); 0695 0696 QCOMPARE(result1->data().size(), 1); 0697 QCOMPARE(result1->data().at(0)->name(), QStringLiteral("42")); 0698 0699 QCOMPARE(result2->data().size(), 2); 0700 QCOMPARE(result2->data().at(0)->name(), QStringLiteral("43")); 0701 QCOMPARE(result2->data().at(1)->name(), QStringLiteral("44")); 0702 } 0703 0704 void shouldIgnoreItemsWhichAreNotProjects() 0705 { 0706 // GIVEN 0707 AkonadiFakeData data; 0708 0709 // One top level collection 0710 data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); 0711 0712 // One project and one regular task in the collection 0713 data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); 0714 data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43"))); 0715 0716 // WHEN 0717 auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); 0718 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0719 serializer, 0720 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0721 Domain::DataSource::Ptr dataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); 0722 auto result = queries->findProjects(dataSource); 0723 QVERIFY(result->data().isEmpty()); 0724 TestHelpers::waitForEmptyJobQueue(); 0725 0726 // THEN 0727 QCOMPARE(result->data().size(), 1); 0728 QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); 0729 } 0730 0731 void shouldReactToItemAddsForProjectsOnly() 0732 { 0733 // GIVEN 0734 AkonadiFakeData data; 0735 0736 // One empty collection 0737 data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); 0738 0739 auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); 0740 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0741 serializer, 0742 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0743 Domain::DataSource::Ptr dataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); 0744 auto result = queries->findProjects(dataSource); 0745 TestHelpers::waitForEmptyJobQueue(); 0746 QVERIFY(result->data().isEmpty()); 0747 0748 // WHEN 0749 data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); 0750 data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43"))); 0751 0752 // THEN 0753 QCOMPARE(result->data().size(), 1); 0754 QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); 0755 } 0756 0757 void shouldReactToItemRemovesForProjects() 0758 { 0759 // GIVEN 0760 AkonadiFakeData data; 0761 0762 // One top level collection 0763 data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); 0764 0765 // Three projects in the collection 0766 data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); 0767 data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43")).asProject()); 0768 data.createItem(GenTodo().withId(44).withParent(42).withTitle(QStringLiteral("44")).asProject()); 0769 0770 auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); 0771 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0772 serializer, 0773 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0774 Domain::DataSource::Ptr dataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); 0775 auto result = queries->findProjects(dataSource); 0776 TestHelpers::waitForEmptyJobQueue(); 0777 QCOMPARE(result->data().size(), 3); 0778 0779 // WHEN 0780 data.removeItem(Akonadi::Item(43)); 0781 0782 // THEN 0783 QCOMPARE(result->data().size(), 2); 0784 QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); 0785 QCOMPARE(result->data().at(1)->name(), QStringLiteral("44")); 0786 } 0787 0788 void shouldReactToItemChangesForProjects() 0789 { 0790 // GIVEN 0791 AkonadiFakeData data; 0792 0793 // One top level collection 0794 data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent()); 0795 0796 // Three projects in the collection 0797 data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42")).asProject()); 0798 data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43")).asProject()); 0799 data.createItem(GenTodo().withId(44).withParent(42).withTitle(QStringLiteral("44")).asProject()); 0800 0801 auto serializer = Akonadi::Serializer::Ptr(new Akonadi::Serializer); 0802 QScopedPointer<Domain::DataSourceQueries> queries(new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Ptr(data.createStorage()), 0803 serializer, 0804 Akonadi::MonitorInterface::Ptr(data.createMonitor()))); 0805 Domain::DataSource::Ptr dataSource = serializer->createDataSourceFromCollection(data.collection(42), Akonadi::SerializerInterface::BaseName); 0806 auto result = queries->findProjects(dataSource); 0807 // Even though the pointer didn't change it's convenient to user if we call 0808 // the replace handlers 0809 bool replaceHandlerCalled = false; 0810 result->addPostReplaceHandler([&replaceHandlerCalled](const Domain::Project::Ptr &, int) { 0811 replaceHandlerCalled = true; 0812 }); 0813 TestHelpers::waitForEmptyJobQueue(); 0814 QCOMPARE(result->data().size(), 3); 0815 0816 // WHEN 0817 data.modifyItem(GenTodo(data.item(43)).withTitle(QStringLiteral("43bis"))); 0818 0819 // THEN 0820 QCOMPARE(result->data().size(), 3); 0821 QCOMPARE(result->data().at(0)->name(), QStringLiteral("42")); 0822 QCOMPARE(result->data().at(1)->name(), QStringLiteral("43bis")); 0823 QCOMPARE(result->data().at(2)->name(), QStringLiteral("44")); 0824 QVERIFY(replaceHandlerCalled); 0825 } 0826 }; 0827 0828 ZANSHIN_TEST_MAIN(AkonadiDataSourceQueriesTest) 0829 0830 #include "akonadidatasourcequeriestest.moc"