File indexing completed on 2025-01-05 04:59:52
0001 /* 0002 * SPDX-FileCopyrightText: 2015 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/akonadilivequeryhelpers.h" 0010 0011 #include <functional> 0012 0013 #include <KCalendarCore/Todo> 0014 0015 #include "akonadi/akonadiserializer.h" 0016 0017 #include "testlib/akonadifakedata.h" 0018 #include "testlib/gencollection.h" 0019 #include "testlib/gentodo.h" 0020 #include "testlib/testhelpers.h" 0021 0022 using namespace Testlib; 0023 0024 using namespace std::placeholders; 0025 0026 static QString titleFromItem(const Akonadi::Item &item) 0027 { 0028 if (item.hasPayload<KCalendarCore::Todo::Ptr>()) { 0029 const auto todo = item.payload<KCalendarCore::Todo::Ptr>(); 0030 return todo->summary(); 0031 } else { 0032 return QString(); 0033 } 0034 } 0035 0036 class AkonadiLiveQueryHelpersTest : public QObject 0037 { 0038 Q_OBJECT 0039 0040 private: 0041 Akonadi::LiveQueryHelpers::Ptr createHelpers(AkonadiFakeData &data) 0042 { 0043 return Akonadi::LiveQueryHelpers::Ptr(new Akonadi::LiveQueryHelpers(createSerializer(), createStorage(data))); 0044 } 0045 0046 Akonadi::StorageInterface::Ptr createStorage(AkonadiFakeData &data) 0047 { 0048 return Akonadi::StorageInterface::Ptr(data.createStorage()); 0049 } 0050 0051 Akonadi::SerializerInterface::Ptr createSerializer() 0052 { 0053 return Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer); 0054 } 0055 0056 private slots: 0057 void shouldFetchAllCollections() 0058 { 0059 // GIVEN 0060 auto data = AkonadiFakeData(); 0061 auto helpers = createHelpers(data); 0062 0063 // Three top level collections (any content, tasks and notes) 0064 data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); 0065 data.createCollection(GenCollection().withId(43).withRootAsParent().withName(QStringLiteral("43")).withTaskContent()); 0066 data.createCollection(GenCollection().withId(44).withRootAsParent().withName(QStringLiteral("44")).withNoteContent()); 0067 0068 // Three children under each of the top level for each content type 0069 data.createCollection(GenCollection().withId(45).withParent(42).withName(QStringLiteral("45"))); 0070 data.createCollection(GenCollection().withId(46).withParent(42).withName(QStringLiteral("46")).withTaskContent()); 0071 data.createCollection(GenCollection().withId(47).withParent(42).withName(QStringLiteral("47")).withNoteContent()); 0072 data.createCollection(GenCollection().withId(48).withParent(43).withName(QStringLiteral("48"))); 0073 data.createCollection(GenCollection().withId(49).withParent(43).withName(QStringLiteral("49")).withTaskContent()); 0074 data.createCollection(GenCollection().withId(50).withParent(43).withName(QStringLiteral("50")).withNoteContent()); 0075 data.createCollection(GenCollection().withId(51).withParent(44).withName(QStringLiteral("51"))); 0076 data.createCollection(GenCollection().withId(52).withParent(44).withName(QStringLiteral("52")).withTaskContent()); 0077 data.createCollection(GenCollection().withId(53).withParent(44).withName(QStringLiteral("53")).withNoteContent()); 0078 0079 // The list which will be filled by the fetch function 0080 auto collections = Akonadi::Collection::List(); 0081 auto add = [&collections] (const Akonadi::Collection &collection) { 0082 collections.append(collection); 0083 }; 0084 0085 // WHEN 0086 auto fetch = helpers->fetchAllCollections(nullptr); 0087 fetch(add); 0088 TestHelpers::waitForEmptyJobQueue(); 0089 0090 auto result = QStringList(); 0091 std::transform(collections.constBegin(), collections.constEnd(), 0092 std::back_inserter(result), 0093 std::bind(&Akonadi::Collection::displayName, _1)); 0094 result.sort(); 0095 0096 // THEN 0097 auto expected = QStringList(); 0098 expected << QStringLiteral("43") << QStringLiteral("46") << QStringLiteral("49") << QStringLiteral("52"); 0099 0100 expected.sort(); 0101 QCOMPARE(result, expected); 0102 0103 // WHEN (should not crash when the helpers object is deleted) 0104 helpers.clear(); 0105 collections.clear(); 0106 fetch(add); 0107 TestHelpers::waitForEmptyJobQueue(); 0108 0109 // THEN 0110 result.clear(); 0111 std::transform(collections.constBegin(), collections.constEnd(), 0112 std::back_inserter(result), 0113 std::bind(&Akonadi::Collection::displayName, _1)); 0114 result.sort(); 0115 QCOMPARE(result, expected); 0116 } 0117 0118 void shouldFetchCollectionsForRoot_data() 0119 { 0120 QTest::addColumn<Akonadi::Collection>("root"); 0121 0122 QTest::newRow("all collections from root") << Akonadi::Collection::root(); 0123 QTest::newRow("all collections from 'all branch'") << Akonadi::Collection(42); 0124 QTest::newRow("all collections from 'task branch'") << Akonadi::Collection(43); 0125 QTest::newRow("all collections from 'note branch'") << Akonadi::Collection(44); 0126 } 0127 0128 void shouldFetchCollectionsForRoot() 0129 { 0130 // GIVEN 0131 auto data = AkonadiFakeData(); 0132 auto helpers = createHelpers(data); 0133 0134 // Three top level collections (any content, tasks and notes) 0135 data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); 0136 data.createCollection(GenCollection().withId(43).withRootAsParent().withName(QStringLiteral("43")).withTaskContent()); 0137 data.createCollection(GenCollection().withId(44).withRootAsParent().withName(QStringLiteral("44")).withNoteContent()); 0138 0139 // Three children under each of the top level for each content type 0140 data.createCollection(GenCollection().withId(45).withParent(42).withName(QStringLiteral("45"))); 0141 data.createCollection(GenCollection().withId(46).withParent(42).withName(QStringLiteral("46")).withTaskContent()); 0142 data.createCollection(GenCollection().withId(47).withParent(42).withName(QStringLiteral("47")).withNoteContent()); 0143 data.createCollection(GenCollection().withId(48).withParent(43).withName(QStringLiteral("48"))); 0144 data.createCollection(GenCollection().withId(49).withParent(43).withName(QStringLiteral("49")).withTaskContent()); 0145 data.createCollection(GenCollection().withId(50).withParent(43).withName(QStringLiteral("50")).withNoteContent()); 0146 data.createCollection(GenCollection().withId(51).withParent(44).withName(QStringLiteral("51"))); 0147 data.createCollection(GenCollection().withId(52).withParent(44).withName(QStringLiteral("52")).withTaskContent()); 0148 data.createCollection(GenCollection().withId(53).withParent(44).withName(QStringLiteral("53")).withNoteContent()); 0149 0150 // The list which will be filled by the fetch function 0151 auto collections = Akonadi::Collection::List(); 0152 auto add = [&collections] (const Akonadi::Collection &collection) { 0153 collections.append(collection); 0154 }; 0155 0156 // WHEN 0157 QFETCH(Akonadi::Collection, root); 0158 auto fetch = helpers->fetchCollections(root, nullptr); 0159 fetch(add); 0160 TestHelpers::waitForEmptyJobQueue(); 0161 0162 auto result = QStringList(); 0163 std::transform(collections.constBegin(), collections.constEnd(), 0164 std::back_inserter(result), 0165 std::bind(&Akonadi::Collection::displayName, _1)); 0166 result.sort(); 0167 0168 // THEN 0169 auto expected = QStringList(); 0170 0171 if (root == Akonadi::Collection::root()) { 0172 expected << QStringLiteral("42") << QStringLiteral("43") << QStringLiteral("44"); 0173 } else { 0174 const qint64 baseId = root.id() == 42 ? 45 0175 : root.id() == 43 ? 48 0176 : root.id() == 44 ? 51 0177 : -1; 0178 QVERIFY(baseId > 0); 0179 0180 expected << QString::number(baseId + 1); 0181 } 0182 0183 expected.sort(); 0184 QCOMPARE(result, expected); 0185 0186 // WHEN (should not crash when the helpers object is deleted) 0187 helpers.clear(); 0188 collections.clear(); 0189 fetch(add); 0190 TestHelpers::waitForEmptyJobQueue(); 0191 0192 // THEN 0193 result.clear(); 0194 std::transform(collections.constBegin(), collections.constEnd(), 0195 std::back_inserter(result), 0196 std::bind(&Akonadi::Collection::displayName, _1)); 0197 result.sort(); 0198 QCOMPARE(result, expected); 0199 } 0200 0201 void shouldFetchItems() 0202 { 0203 // GIVEN 0204 auto data = AkonadiFakeData(); 0205 auto helpers = createHelpers(data); 0206 0207 // Two top level collections, one with no particular content, one with tasks 0208 data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42"))); 0209 data.createCollection(GenCollection().withId(43).withRootAsParent().withName(QStringLiteral("43")).withTaskContent()); 0210 0211 // One note collection as child of the first one 0212 data.createCollection(GenCollection().withId(44).withParent(42).withName(QStringLiteral("44")).withNoteContent()); 0213 0214 // One task collection as child of the note collection 0215 data.createCollection(GenCollection().withId(45).withParent(44).withName(QStringLiteral("45")).withTaskContent()); 0216 0217 // One task in the first collection 0218 data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42"))); 0219 0220 // Two tasks in all the other collections 0221 data.createItem(GenTodo().withId(43).withParent(43).withTitle(QStringLiteral("43"))); 0222 data.createItem(GenTodo().withId(44).withParent(43).withTitle(QStringLiteral("44"))); 0223 data.createItem(GenTodo().withId(45).withParent(44).withTitle(QStringLiteral("45"))); 0224 data.createItem(GenTodo().withId(46).withParent(44).withTitle(QStringLiteral("46"))); 0225 data.createItem(GenTodo().withId(47).withParent(45).withTitle(QStringLiteral("47"))); 0226 data.createItem(GenTodo().withId(48).withParent(45).withTitle(QStringLiteral("48"))); 0227 0228 // The list which will be filled by the fetch function 0229 auto items = Akonadi::Item::List(); 0230 auto add = [&items] (const Akonadi::Item &item) { 0231 items.append(item); 0232 }; 0233 0234 // WHEN 0235 auto fetch = helpers->fetchItems(nullptr); 0236 fetch(add); 0237 TestHelpers::waitForEmptyJobQueue(); 0238 0239 auto result = QStringList(); 0240 std::transform(items.constBegin(), items.constEnd(), 0241 std::back_inserter(result), 0242 titleFromItem); 0243 result.sort(); 0244 0245 // THEN 0246 auto expected = QStringList(); 0247 expected << QStringLiteral("43") << QStringLiteral("44") << QStringLiteral("47") << QStringLiteral("48"); 0248 expected.sort(); 0249 QCOMPARE(result, expected); 0250 0251 // WHEN (should not crash when the helpers object is deleted) 0252 helpers.clear(); 0253 items.clear(); 0254 fetch(add); 0255 TestHelpers::waitForEmptyJobQueue(); 0256 0257 // THEN 0258 result.clear(); 0259 std::transform(items.constBegin(), items.constEnd(), 0260 std::back_inserter(result), 0261 titleFromItem); 0262 result.sort(); 0263 QCOMPARE(result, expected); 0264 } 0265 0266 void shouldFetchItemsByCollection_data() 0267 { 0268 QTest::addColumn<Akonadi::Collection>("collection"); 0269 0270 QTest::newRow("first collection") << Akonadi::Collection(42); 0271 QTest::newRow("second collection") << Akonadi::Collection(43); 0272 } 0273 0274 void shouldFetchItemsByCollection() 0275 { 0276 // GIVEN 0277 auto data = AkonadiFakeData(); 0278 auto helpers = createHelpers(data); 0279 0280 // Two top level collections with tasks 0281 data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42")).withTaskContent()); 0282 data.createCollection(GenCollection().withId(43).withRootAsParent().withName(QStringLiteral("43")).withTaskContent()); 0283 0284 // Two items in each collection 0285 data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42"))); 0286 data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43"))); 0287 data.createItem(GenTodo().withId(44).withParent(43).withTitle(QStringLiteral("44"))); 0288 data.createItem(GenTodo().withId(45).withParent(43).withTitle(QStringLiteral("45"))); 0289 0290 // The list which will be filled by the fetch function 0291 auto items = Akonadi::Item::List(); 0292 auto add = [&items] (const Akonadi::Item &item) { 0293 items.append(item); 0294 }; 0295 0296 // WHEN 0297 QFETCH(Akonadi::Collection, collection); 0298 auto fetch = helpers->fetchItems(collection, nullptr); 0299 fetch(add); 0300 TestHelpers::waitForEmptyJobQueue(); 0301 0302 auto result = QStringList(); 0303 std::transform(items.constBegin(), items.constEnd(), 0304 std::back_inserter(result), 0305 titleFromItem); 0306 result.sort(); 0307 0308 // THEN 0309 auto expected = QStringList(); 0310 0311 switch (collection.id()) { 0312 case 42: 0313 expected << QStringLiteral("42") << QStringLiteral("43"); 0314 break; 0315 case 43: 0316 expected << QStringLiteral("44") << QStringLiteral("45"); 0317 break; 0318 } 0319 QVERIFY(!expected.isEmpty()); 0320 0321 expected.sort(); 0322 QCOMPARE(result, expected); 0323 0324 // WHEN (should not crash when the helpers object is deleted) 0325 helpers.clear(); 0326 items.clear(); 0327 fetch(add); 0328 TestHelpers::waitForEmptyJobQueue(); 0329 0330 // THEN 0331 result.clear(); 0332 std::transform(items.constBegin(), items.constEnd(), 0333 std::back_inserter(result), 0334 titleFromItem); 0335 result.sort(); 0336 QCOMPARE(result, expected); 0337 } 0338 0339 void shouldFetchItemsForContext_data() 0340 { 0341 QTest::addColumn<Domain::Context::Ptr>("context"); 0342 0343 auto context1 = Domain::Context::Ptr::create(); 0344 context1->setProperty("todoUid", "ctx-42"); 0345 auto context2 = Domain::Context::Ptr::create(); 0346 context2->setProperty("todoUid", "ctx-43"); 0347 0348 QTest::newRow("first") << context1; 0349 QTest::newRow("second") << context2; 0350 } 0351 0352 void shouldFetchItemsForContext() 0353 { 0354 // GIVEN 0355 auto data = AkonadiFakeData(); 0356 auto helpers = createHelpers(data); 0357 0358 // Two top level collections with tasks 0359 data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42")).withTaskContent()); 0360 data.createCollection(GenCollection().withId(43).withRootAsParent().withName(QStringLiteral("43")).withTaskContent()); 0361 0362 // Two contexts 0363 data.createItem(GenTodo().withId(142).withUid("ctx-42").asContext()); 0364 data.createItem(GenTodo().withId(143).withUid("ctx-43").asContext()); 0365 0366 // Four items in each collection, one with no context, one with the first context, 0367 // one with the second context, last one with both contexts 0368 data.createItem(GenTodo().withId(42).withParent(42).withContexts({}).withTitle(QStringLiteral("42"))); 0369 data.createItem(GenTodo().withId(43).withParent(42).withContexts({"ctx-42"}).withTitle(QStringLiteral("43"))); 0370 data.createItem(GenTodo().withId(44).withParent(42).withContexts({"ctx-43"}).withTitle(QStringLiteral("44"))); 0371 data.createItem(GenTodo().withId(45).withParent(42).withContexts({"ctx-42", "ctx-43"}).withTitle(QStringLiteral("45"))); 0372 data.createItem(GenTodo().withId(46).withParent(43).withContexts({}).withTitle(QStringLiteral("46"))); 0373 data.createItem(GenTodo().withId(47).withParent(43).withContexts({"ctx-42"}).withTitle(QStringLiteral("47"))); 0374 data.createItem(GenTodo().withId(48).withParent(43).withContexts({"ctx-43"}).withTitle(QStringLiteral("48"))); 0375 data.createItem(GenTodo().withId(49).withParent(43).withContexts({"ctx-42", "ctx-43"}).withTitle(QStringLiteral("49"))); 0376 0377 // The list which will be filled by the fetch function 0378 auto items = Akonadi::Item::List(); 0379 auto add = [&items] (const Akonadi::Item &item) { 0380 items.append(item); 0381 }; 0382 0383 // WHEN 0384 QFETCH(Domain::Context::Ptr, context); 0385 auto fetch = helpers->fetchItemsForContext(context, nullptr); 0386 fetch(add); 0387 TestHelpers::waitForEmptyJobQueue(); 0388 0389 auto result = QStringList(); 0390 std::transform(items.constBegin(), items.constEnd(), 0391 std::back_inserter(result), 0392 titleFromItem); 0393 result.sort(); 0394 0395 // THEN 0396 auto expected = QStringList(); 0397 0398 if (context->property("todoUid") == "ctx-42") 0399 expected << QStringLiteral("43") << QStringLiteral("45") << QStringLiteral("47") << QStringLiteral("49"); 0400 else if (context->property("todoUid") == "ctx-43") 0401 expected << QStringLiteral("44") << QStringLiteral("45") << QStringLiteral("48") << QStringLiteral("49"); 0402 QVERIFY(!expected.isEmpty()); 0403 0404 expected.sort(); 0405 QCOMPARE(result, expected); 0406 0407 // WHEN (should not crash when the helpers object is deleted) 0408 helpers.clear(); 0409 items.clear(); 0410 fetch(add); 0411 TestHelpers::waitForEmptyJobQueue(); 0412 0413 // THEN 0414 result.clear(); 0415 std::transform(items.constBegin(), items.constEnd(), 0416 std::back_inserter(result), 0417 titleFromItem); 0418 result.sort(); 0419 QCOMPARE(result, expected); 0420 } 0421 0422 void shouldFetchSiblings_data() 0423 { 0424 QTest::addColumn<Akonadi::Item>("item"); 0425 0426 QTest::newRow("item in first collection") << Akonadi::Item(43); 0427 QTest::newRow("item in second collection") << Akonadi::Item(48); 0428 } 0429 0430 void shouldFetchSiblings() 0431 { 0432 // GIVEN 0433 auto data = AkonadiFakeData(); 0434 auto helpers = createHelpers(data); 0435 0436 // Two top level collections (one with notes, one with tasks) 0437 data.createCollection(GenCollection().withId(42).withRootAsParent().withName(QStringLiteral("42")).withTaskContent()); 0438 data.createCollection(GenCollection().withId(43).withRootAsParent().withName(QStringLiteral("43")).withNoteContent()); 0439 0440 // Four items in each collection 0441 data.createItem(GenTodo().withId(42).withParent(42).withTitle(QStringLiteral("42"))); 0442 data.createItem(GenTodo().withId(43).withParent(42).withTitle(QStringLiteral("43"))); 0443 data.createItem(GenTodo().withId(44).withParent(42).withTitle(QStringLiteral("44"))); 0444 data.createItem(GenTodo().withId(45).withParent(42).withTitle(QStringLiteral("45"))); 0445 data.createItem(GenTodo().withId(46).withParent(43).withTitle(QStringLiteral("46"))); 0446 data.createItem(GenTodo().withId(47).withParent(43).withTitle(QStringLiteral("47"))); 0447 data.createItem(GenTodo().withId(48).withParent(43).withTitle(QStringLiteral("48"))); 0448 data.createItem(GenTodo().withId(49).withParent(43).withTitle(QStringLiteral("49"))); 0449 0450 // The list which will be filled by the fetch function 0451 auto items = Akonadi::Item::List(); 0452 auto add = [&items] (const Akonadi::Item &item) { 0453 items.append(item); 0454 }; 0455 0456 // WHEN 0457 QFETCH(Akonadi::Item, item); 0458 auto fetch = helpers->fetchSiblings(item, nullptr); 0459 fetch(add); 0460 TestHelpers::waitForEmptyJobQueue(); 0461 0462 auto result = QStringList(); 0463 std::transform(items.constBegin(), items.constEnd(), 0464 std::back_inserter(result), 0465 titleFromItem); 0466 result.sort(); 0467 0468 // THEN 0469 auto expected = QStringList(); 0470 0471 switch (item.id()) { 0472 case 43: 0473 expected << QStringLiteral("42") << QStringLiteral("43") << QStringLiteral("44") << QStringLiteral("45"); 0474 break; 0475 case 48: 0476 expected << QStringLiteral("46") << QStringLiteral("47") << QStringLiteral("48") << QStringLiteral("49"); 0477 break; 0478 } 0479 QVERIFY(!expected.isEmpty()); 0480 0481 expected.sort(); 0482 QCOMPARE(result, expected); 0483 0484 // WHEN (should not crash when the helpers object is deleted) 0485 helpers.clear(); 0486 items.clear(); 0487 fetch(add); 0488 TestHelpers::waitForEmptyJobQueue(); 0489 0490 // THEN 0491 result.clear(); 0492 std::transform(items.constBegin(), items.constEnd(), 0493 std::back_inserter(result), 0494 titleFromItem); 0495 result.sort(); 0496 QCOMPARE(result, expected); 0497 } 0498 }; 0499 0500 ZANSHIN_TEST_MAIN(AkonadiLiveQueryHelpersTest) 0501 0502 #include "akonadilivequeryhelperstest.moc"