File indexing completed on 2025-01-05 04:59:50
0001 /* 0002 * SPDX-FileCopyrightText: 2017 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/akonadicachingstorage.h" 0010 #include "akonadi/akonadiserializer.h" 0011 0012 #include "akonadi/akonadicollectionfetchjobinterface.h" 0013 #include "akonadi/akonadiitemfetchjobinterface.h" 0014 0015 #include "testlib/akonadifakedata.h" 0016 #include "testlib/gencollection.h" 0017 #include "testlib/gentodo.h" 0018 #include "testlib/testhelpers.h" 0019 0020 Q_DECLARE_METATYPE(Akonadi::StorageInterface::FetchDepth) 0021 0022 using namespace Testlib; 0023 0024 class AkonadiCachingStorageTest : public QObject 0025 { 0026 Q_OBJECT 0027 public: 0028 explicit AkonadiCachingStorageTest(QObject *parent = nullptr) 0029 : QObject(parent) 0030 { 0031 qRegisterMetaType<Akonadi::Collection>(); 0032 qRegisterMetaType<Akonadi::Item>(); 0033 } 0034 0035 QStringList onlyWithSuffix(const QStringList &list, const QString &suffix) 0036 { 0037 return onlyWithSuffixes(list, {suffix}); 0038 } 0039 0040 QStringList onlyWithSuffixes(const QStringList &list, const QStringList &suffixes) 0041 { 0042 auto res = QStringList(); 0043 std::copy_if(list.cbegin(), list.cend(), 0044 std::back_inserter(res), 0045 [suffixes](const QString &entry) { 0046 for (const auto &suffix : suffixes) { 0047 if (entry.endsWith(suffix)) 0048 return true; 0049 } 0050 return false; 0051 }); 0052 return res; 0053 } 0054 0055 private slots: 0056 void shouldCacheAllCollectionsPerFetchType_data() 0057 { 0058 QTest::addColumn<Akonadi::Collection>("rootCollection"); 0059 QTest::addColumn<Akonadi::StorageInterface::FetchDepth>("fetchDepth"); 0060 QTest::addColumn<QStringList>("expectedFetchNames"); 0061 QTest::addColumn<QStringList>("expectedCachedNames"); 0062 0063 const auto allCollections = QStringList() << "42Task" << "43Task" << "44Note" << "45Stuff" 0064 << "46Note" << "47Task" << "48Note" << "49Stuff" 0065 << "50Stuff" << "51Task" << "52Note" << "53Stuff" 0066 << "54Task" << "55Task" << "56Task" 0067 << "57Note" << "58Note" << "59Note" 0068 << "60Stuff" << "61Stuff" << "62Stuff"; 0069 0070 const auto taskCollections = QStringList() << "42Task" << "43Task" 0071 << "46Note" << "47Task" 0072 << "50Stuff" << "51Task" 0073 << "54Task" << "55Task" << "56Task"; 0074 0075 QTest::newRow("rootRecursiveTask") << Akonadi::Collection::root() << Akonadi::StorageInterface::Recursive 0076 << onlyWithSuffix(taskCollections, "Task") << taskCollections; 0077 0078 QTest::newRow("54RecursiveTask") << Akonadi::Collection(54) << Akonadi::StorageInterface::Recursive 0079 << (QStringList() << "55Task" << "56Task") << taskCollections; 0080 0081 QTest::newRow("54FirstLevelTask") << Akonadi::Collection(54) << Akonadi::StorageInterface::FirstLevel 0082 << (QStringList() << "55Task") << taskCollections; 0083 0084 QTest::newRow("54BaseTask") << Akonadi::Collection(54) << Akonadi::StorageInterface::Base 0085 << (QStringList() << "54Task") << taskCollections; 0086 } 0087 0088 void shouldCacheAllCollectionsPerFetchType() 0089 { 0090 // GIVEN 0091 AkonadiFakeData data; 0092 0093 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Task")).withRootAsParent().withTaskContent()); 0094 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Task")).withParent(42).withTaskContent()); 0095 data.createCollection(GenCollection().withId(44).withName(QStringLiteral("44Note")).withParent(42).withNoteContent()); 0096 data.createCollection(GenCollection().withId(45).withName(QStringLiteral("45Stuff")).withParent(42)); 0097 0098 data.createCollection(GenCollection().withId(46).withName(QStringLiteral("46Note")).withRootAsParent().withNoteContent()); 0099 data.createCollection(GenCollection().withId(47).withName(QStringLiteral("47Task")).withParent(46).withTaskContent()); 0100 data.createCollection(GenCollection().withId(48).withName(QStringLiteral("48Note")).withParent(46).withNoteContent()); 0101 data.createCollection(GenCollection().withId(49).withName(QStringLiteral("49Stuff")).withParent(46)); 0102 0103 data.createCollection(GenCollection().withId(50).withName(QStringLiteral("50Stuff")).withRootAsParent()); 0104 data.createCollection(GenCollection().withId(51).withName(QStringLiteral("51Task")).withParent(50).withTaskContent()); 0105 data.createCollection(GenCollection().withId(52).withName(QStringLiteral("52Note")).withParent(50).withNoteContent()); 0106 data.createCollection(GenCollection().withId(53).withName(QStringLiteral("53Stuff")).withParent(50)); 0107 0108 data.createCollection(GenCollection().withId(54).withName(QStringLiteral("54Task")).withRootAsParent().withTaskContent()); 0109 data.createCollection(GenCollection().withId(55).withName(QStringLiteral("55Task")).withParent(54).withTaskContent()); 0110 data.createCollection(GenCollection().withId(56).withName(QStringLiteral("56Task")).withParent(55).withTaskContent()); 0111 0112 data.createCollection(GenCollection().withId(57).withName(QStringLiteral("57Note")).withRootAsParent().withNoteContent()); 0113 data.createCollection(GenCollection().withId(58).withName(QStringLiteral("58Note")).withParent(57).withNoteContent()); 0114 data.createCollection(GenCollection().withId(59).withName(QStringLiteral("59Note")).withParent(58).withNoteContent()); 0115 0116 data.createCollection(GenCollection().withId(60).withName(QStringLiteral("60Stuff")).withRootAsParent()); 0117 data.createCollection(GenCollection().withId(61).withName(QStringLiteral("61Stuff")).withParent(60)); 0118 data.createCollection(GenCollection().withId(62).withName(QStringLiteral("62Stuff")).withParent(61)); 0119 0120 auto cache = Akonadi::Cache::Ptr::create(Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0121 Akonadi::MonitorInterface::Ptr(data.createMonitor())); 0122 Akonadi::CachingStorage storage(cache, Akonadi::StorageInterface::Ptr(data.createStorage())); 0123 0124 // WHEN 0125 QFETCH(Akonadi::Collection, rootCollection); 0126 QFETCH(Akonadi::StorageInterface::FetchDepth, fetchDepth); 0127 auto job = storage.fetchCollections(rootCollection, fetchDepth, nullptr); 0128 QVERIFY2(job->kjob()->exec(), qPrintable(job->kjob()->errorString())); 0129 0130 // THEN 0131 const auto toCollectionNames = [](const Akonadi::Collection::List &collections) { 0132 auto res = QStringList(); 0133 res.reserve(collections.size()); 0134 std::transform(collections.cbegin(), collections.cend(), 0135 std::back_inserter(res), 0136 std::mem_fn(&Akonadi::Collection::name)); 0137 res.sort(); 0138 return res; 0139 }; 0140 0141 QFETCH(QStringList, expectedFetchNames); 0142 QFETCH(QStringList, expectedCachedNames); 0143 0144 { 0145 const auto collectionFetchNames = toCollectionNames(job->collections()); 0146 QCOMPARE(collectionFetchNames, expectedFetchNames); 0147 0148 const auto collections = cache->allCollections(); 0149 const auto collectionCachedNames = toCollectionNames(collections); 0150 QCOMPARE(collectionCachedNames, expectedCachedNames); 0151 } 0152 0153 // WHEN (second time shouldn't hit the original storage) 0154 data.storageBehavior().setFetchCollectionsBehavior(rootCollection.id(), AkonadiFakeStorageBehavior::EmptyFetch); 0155 data.storageBehavior().setFetchCollectionsErrorCode(rootCollection.id(), 128); 0156 job = storage.fetchCollections(rootCollection, fetchDepth, nullptr); 0157 QVERIFY2(job->kjob()->exec(), qPrintable(job->kjob()->errorString())); 0158 0159 { 0160 const auto collectionFetchNames = toCollectionNames(job->collections()); 0161 QCOMPARE(collectionFetchNames, expectedFetchNames); 0162 0163 const auto collections = cache->allCollections(); 0164 const auto collectionCachedNames = toCollectionNames(collections); 0165 QCOMPARE(collectionCachedNames, expectedCachedNames); 0166 } 0167 } 0168 0169 void shouldCacheAllItemsPerCollection() 0170 { 0171 // GIVEN 0172 AkonadiFakeData data; 0173 0174 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Col")).withRootAsParent().withTaskContent()); 0175 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Col")).withRootAsParent().withTaskContent()); 0176 0177 data.createItem(GenTodo().withId(42).withTitle(QStringLiteral("42Task")).withParent(42)); 0178 data.createItem(GenTodo().withId(45).withTitle(QStringLiteral("45Task")).withParent(42)); 0179 data.createItem(GenTodo().withId(52).withTitle(QStringLiteral("52Task")).withParent(42)); 0180 0181 data.createItem(GenTodo().withId(44).withTitle(QStringLiteral("44Task")).withParent(43)); 0182 data.createItem(GenTodo().withId(48).withTitle(QStringLiteral("48Task")).withParent(43)); 0183 data.createItem(GenTodo().withId(50).withTitle(QStringLiteral("50Task")).withParent(43)); 0184 0185 auto cache = Akonadi::Cache::Ptr::create(Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0186 Akonadi::MonitorInterface::Ptr(data.createMonitor())); 0187 Akonadi::CachingStorage storage(cache, Akonadi::StorageInterface::Ptr(data.createStorage())); 0188 0189 // WHEN 0190 auto job = storage.fetchItems(Akonadi::Collection(42), nullptr); 0191 QVERIFY2(job->kjob()->exec(), qPrintable(job->kjob()->errorString())); 0192 0193 // THEN 0194 const auto toItemIds = [](const Akonadi::Item::List &items) { 0195 auto res = QList<Akonadi::Item::Id>(); 0196 res.reserve(items.size()); 0197 std::transform(items.cbegin(), items.cend(), 0198 std::back_inserter(res), 0199 std::mem_fn(&Akonadi::Item::id)); 0200 std::sort(res.begin(), res.end()); 0201 return res; 0202 }; 0203 0204 auto expectedIds = QList<Akonadi::Item::Id>() << 42 << 45 << 52; 0205 { 0206 const auto itemFetchIds = toItemIds(job->items()); 0207 QCOMPARE(itemFetchIds, expectedIds); 0208 0209 const auto items = cache->items(Akonadi::Collection(42)); 0210 const auto itemCachedIds = toItemIds(items); 0211 QCOMPARE(itemCachedIds, expectedIds); 0212 } 0213 0214 // WHEN (second time shouldn't hit the original storage) 0215 data.storageBehavior().setFetchItemsBehavior(42, AkonadiFakeStorageBehavior::EmptyFetch); 0216 data.storageBehavior().setFetchItemsErrorCode(42, 128); 0217 job = storage.fetchItems(Akonadi::Collection(42), nullptr); 0218 QVERIFY2(job->kjob()->exec(), qPrintable(job->kjob()->errorString())); 0219 0220 { 0221 const auto itemFetchIds = toItemIds(job->items()); 0222 QCOMPARE(itemFetchIds, expectedIds); 0223 0224 const auto items = cache->items(Akonadi::Collection(42)); 0225 const auto itemCachedIds = toItemIds(items); 0226 QCOMPARE(itemCachedIds, expectedIds); 0227 } 0228 } 0229 0230 void shouldCacheSingleItems() 0231 { 0232 // GIVEN 0233 AkonadiFakeData data; 0234 0235 data.createCollection(GenCollection().withId(42).withName(QStringLiteral("42Col")).withRootAsParent().withTaskContent()); 0236 data.createCollection(GenCollection().withId(43).withName(QStringLiteral("43Col")).withRootAsParent().withTaskContent()); 0237 0238 data.createItem(GenTodo().withId(42).withTitle(QStringLiteral("42Task")).withParent(42)); 0239 data.createItem(GenTodo().withId(45).withTitle(QStringLiteral("45Task")).withParent(42)); 0240 data.createItem(GenTodo().withId(52).withTitle(QStringLiteral("52Task")).withParent(42)); 0241 0242 data.createItem(GenTodo().withId(44).withTitle(QStringLiteral("44Task")).withParent(43)); 0243 data.createItem(GenTodo().withId(48).withTitle(QStringLiteral("48Task")).withParent(43)); 0244 data.createItem(GenTodo().withId(50).withTitle(QStringLiteral("50Task")).withParent(43)); 0245 0246 auto cache = Akonadi::Cache::Ptr::create(Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), 0247 Akonadi::MonitorInterface::Ptr(data.createMonitor())); 0248 Akonadi::CachingStorage storage(cache, Akonadi::StorageInterface::Ptr(data.createStorage())); 0249 0250 // WHEN 0251 auto job = storage.fetchItem(Akonadi::Item(44), nullptr); 0252 QVERIFY2(job->kjob()->exec(), qPrintable(job->kjob()->errorString())); 0253 0254 // THEN 0255 const auto toItemIds = [](const Akonadi::Item::List &items) { 0256 auto res = QList<Akonadi::Item::Id>(); 0257 res.reserve(items.size()); 0258 std::transform(items.cbegin(), items.cend(), 0259 std::back_inserter(res), 0260 std::mem_fn(&Akonadi::Item::id)); 0261 std::sort(res.begin(), res.end()); 0262 return res; 0263 }; 0264 0265 auto expectedIds = QList<Akonadi::Item::Id>() << 44; 0266 { 0267 const auto itemFetchIds = toItemIds(job->items()); 0268 QCOMPARE(itemFetchIds, expectedIds); 0269 QVERIFY(!cache->item(44).isValid()); 0270 } 0271 0272 // WHEN (if collection is populated, shouldn't hit the original storage) 0273 job = storage.fetchItems(Akonadi::Collection(43), nullptr); 0274 QVERIFY2(job->kjob()->exec(), qPrintable(job->kjob()->errorString())); 0275 0276 data.storageBehavior().setFetchItemBehavior(44, AkonadiFakeStorageBehavior::EmptyFetch); 0277 data.storageBehavior().setFetchItemErrorCode(44, 128); 0278 job = storage.fetchItem(Akonadi::Item(44), nullptr); 0279 QVERIFY2(job->kjob()->exec(), qPrintable(job->kjob()->errorString())); 0280 0281 { 0282 const auto itemFetchIds = toItemIds(job->items()); 0283 QCOMPARE(itemFetchIds, expectedIds); 0284 QVERIFY(cache->item(44).isValid()); 0285 } 0286 } 0287 }; 0288 0289 ZANSHIN_TEST_MAIN(AkonadiCachingStorageTest) 0290 0291 #include "akonadicachingstoragetest.moc"