File indexing completed on 2024-06-02 05:33:08
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 "akonadifakestorage.h" 0008 0009 #include <QTimer> 0010 0011 #include "akonadi/akonadistoragesettings.h" 0012 #include "akonadifakedata.h" 0013 #include "akonadifakejobs.h" 0014 0015 #include "utils/jobhandler.h" 0016 0017 using namespace Testlib; 0018 0019 class AkonadiFakeTransaction : public FakeJob 0020 { 0021 Q_OBJECT 0022 public: 0023 explicit AkonadiFakeTransaction(QObject *parent = nullptr) 0024 : FakeJob(parent), 0025 m_nextIdx(0) 0026 { 0027 } 0028 0029 private slots: 0030 void onTimeout() override 0031 { 0032 auto jobs = childJobs(); 0033 if (m_nextIdx == 0) { 0034 const auto it = std::find_if(jobs.constBegin(), jobs.constEnd(), 0035 [] (FakeJob *job) { return job->expectedError() != 0; }); 0036 if (it != jobs.constEnd()) { 0037 setError((*it)->expectedError()); 0038 setErrorText((*it)->expectedErrorText()); 0039 emitResult(); 0040 return; 0041 } 0042 } 0043 0044 if (m_nextIdx >= jobs.size()) { 0045 emitResult(); 0046 return; 0047 } 0048 0049 auto job = jobs[m_nextIdx]; 0050 connect(job, &KJob::result, this, &AkonadiFakeTransaction::onTimeout); 0051 job->start(); 0052 m_nextIdx++; 0053 } 0054 0055 private: 0056 QList<FakeJob*> childJobs() const 0057 { 0058 QList<FakeJob*> jobs = findChildren<FakeJob*>(); 0059 jobs.erase(std::remove_if(jobs.begin(), jobs.end(), 0060 [this] (FakeJob *job) { 0061 return job->parent() != this; 0062 }), 0063 jobs.end()); 0064 return jobs; 0065 } 0066 0067 int m_nextIdx; 0068 }; 0069 0070 Utils::JobHandler::StartMode startModeForParent(QObject *parent) 0071 { 0072 bool isTransaction = qobject_cast<AkonadiFakeTransaction*>(parent); 0073 return isTransaction ? Utils::JobHandler::ManualStart 0074 : Utils::JobHandler::AutoStart; 0075 } 0076 0077 void noop() {} 0078 0079 AkonadiFakeStorage::AkonadiFakeStorage(AkonadiFakeData *data) 0080 : m_data(data) 0081 { 0082 } 0083 0084 Akonadi::Collection AkonadiFakeStorage::defaultCollection() 0085 { 0086 return Akonadi::StorageSettings::instance().defaultCollection(); 0087 } 0088 0089 KJob *AkonadiFakeStorage::createItem(Akonadi::Item item, Akonadi::Collection collection) 0090 { 0091 Q_ASSERT(!item.isValid()); 0092 0093 auto job = new FakeJob; 0094 if (!m_data->item(item.id()).isValid()) { 0095 job->setExpectedError(m_data->storageBehavior().createNextItemErrorCode(), 0096 m_data->storageBehavior().createNextItemErrorText()); 0097 Utils::JobHandler::install(job, [=] () mutable { 0098 if (!job->error()) { 0099 item.setId(m_data->maxItemId() + 1); 0100 item.setParentCollection(collection); 0101 // Force payload detach 0102 item.setPayloadFromData(item.payloadData()); 0103 m_data->createItem(item); 0104 } 0105 }); 0106 } else { 0107 job->setExpectedError(1, QStringLiteral("Item already exists")); 0108 Utils::JobHandler::install(job, noop); 0109 } 0110 return job; 0111 } 0112 0113 KJob *AkonadiFakeStorage::updateItem(Akonadi::Item item, QObject *parent) 0114 { 0115 auto job = new FakeJob(parent); 0116 auto startMode = startModeForParent(parent); 0117 0118 if (m_data->item(item.id()).isValid()) { 0119 job->setExpectedError(m_data->storageBehavior().updateNextItemErrorCode(), 0120 m_data->storageBehavior().updateNextItemErrorText()); 0121 Utils::JobHandler::install(job, [=] () mutable { 0122 if (!job->error()) { 0123 // Force payload detach 0124 item.setPayloadFromData(item.payloadData()); 0125 m_data->modifyItem(item); 0126 } 0127 }, startMode); 0128 } else { 0129 job->setExpectedError(1, QStringLiteral("Item doesn't exist")); 0130 Utils::JobHandler::install(job, noop, startMode); 0131 } 0132 return job; 0133 } 0134 0135 KJob *AkonadiFakeStorage::removeItem(Akonadi::Item item, QObject *parent) 0136 { 0137 auto job = new FakeJob(parent); 0138 if (m_data->item(item.id()).isValid()) { 0139 Utils::JobHandler::install(job, [=] { 0140 if (!job->error()) { 0141 m_data->removeItem(item); 0142 } 0143 }); 0144 } else { 0145 job->setExpectedError(1, QStringLiteral("Item doesn't exist")); 0146 Utils::JobHandler::install(job, noop); 0147 } 0148 return job; 0149 } 0150 0151 KJob *AkonadiFakeStorage::removeItems(Akonadi::Item::List items, QObject *parent) 0152 { 0153 auto job = new FakeJob; 0154 auto startMode = startModeForParent(parent); 0155 bool allItemsExist = std::all_of(items.constBegin(), items.constEnd(), 0156 [=] (const Akonadi::Item &item) { 0157 return m_data->item(item.id()).isValid(); 0158 }); 0159 0160 if (allItemsExist) { 0161 job->setExpectedError(m_data->storageBehavior().deleteNextItemErrorCode(), 0162 m_data->storageBehavior().deleteNextItemErrorText()); 0163 Utils::JobHandler::install(job, [=] { 0164 if (!job->error()) { 0165 foreach (const Akonadi::Item &item, items) { 0166 m_data->removeItem(item); 0167 } 0168 } 0169 }, startMode); 0170 } else { 0171 job->setExpectedError(1, QStringLiteral("At least one item doesn't exist")); 0172 Utils::JobHandler::install(job, noop, startMode); 0173 } 0174 return job; 0175 } 0176 0177 KJob *AkonadiFakeStorage::moveItem(Akonadi::Item item, Akonadi::Collection collection, QObject *parent) 0178 { 0179 auto job = new FakeJob(parent); 0180 auto startMode = startModeForParent(parent); 0181 if (m_data->item(item.id()).isValid() 0182 && m_data->collection(collection.id()).isValid()) { 0183 Utils::JobHandler::install(job, [=] () mutable { 0184 if (!job->error()) { 0185 item.setParentCollection(collection); 0186 // Force payload detach 0187 item.setPayloadFromData(item.payloadData()); 0188 m_data->modifyItem(item); 0189 } 0190 }, startMode); 0191 } else { 0192 job->setExpectedError(1, QStringLiteral("The item or the collection doesn't exist")); 0193 Utils::JobHandler::install(job, noop, startMode); 0194 } 0195 return job; 0196 } 0197 0198 KJob *AkonadiFakeStorage::moveItems(Akonadi::Item::List items, Akonadi::Collection collection, QObject *parent) 0199 { 0200 using namespace std::placeholders; 0201 0202 auto job = new FakeJob(parent); 0203 auto startMode = startModeForParent(parent); 0204 bool allItemsExist = std::all_of(items.constBegin(), items.constEnd(), 0205 [=] (const Akonadi::Item &item) { 0206 return m_data->item(item.id()).isValid(); 0207 }); 0208 0209 if (allItemsExist 0210 && m_data->collection(collection.id()).isValid()) { 0211 Utils::JobHandler::install(job, [=] () mutable { 0212 if (!job->error()) { 0213 std::transform(items.constBegin(), items.constEnd(), 0214 items.begin(), 0215 [=] (const Akonadi::Item &item) { 0216 auto result = item; 0217 result.setParentCollection(collection); 0218 // Force payload detach 0219 result.setPayloadFromData(result.payloadData()); 0220 return result; 0221 }); 0222 0223 foreach (const Akonadi::Item &item, items) { 0224 m_data->modifyItem(item); 0225 } 0226 } 0227 }, startMode); 0228 } else { 0229 job->setExpectedError(1, QStringLiteral("One of the items or the collection doesn't exist")); 0230 Utils::JobHandler::install(job, noop, startMode); 0231 } 0232 return job; 0233 } 0234 0235 KJob *AkonadiFakeStorage::createCollection(Akonadi::Collection collection, QObject *parent) 0236 { 0237 Q_ASSERT(!collection.isValid()); 0238 0239 auto job = new FakeJob(parent); 0240 auto startMode = startModeForParent(parent); 0241 if (!m_data->collection(collection.id()).isValid()) { 0242 Utils::JobHandler::install(job, [=] () mutable { 0243 if (!job->error()) { 0244 collection.setId(m_data->maxCollectionId() + 1); 0245 m_data->createCollection(collection); 0246 } 0247 }, startMode); 0248 } else { 0249 job->setExpectedError(1, QStringLiteral("The collection already exists")); 0250 Utils::JobHandler::install(job, noop, startMode); 0251 } 0252 return job; 0253 } 0254 0255 KJob *AkonadiFakeStorage::updateCollection(Akonadi::Collection collection, QObject *parent) 0256 { 0257 auto job = new FakeJob(parent); 0258 auto startMode = startModeForParent(parent); 0259 if (m_data->collection(collection.id()).isValid()) { 0260 Utils::JobHandler::install(job, [=] { 0261 if (!job->error()) { 0262 m_data->modifyCollection(collection); 0263 } 0264 }, startMode); 0265 } else { 0266 job->setExpectedError(1, QStringLiteral("The collection doesn't exist")); 0267 Utils::JobHandler::install(job, noop, startMode); 0268 } 0269 return job; 0270 } 0271 0272 KJob *AkonadiFakeStorage::removeCollection(Akonadi::Collection collection, QObject *parent) 0273 { 0274 auto job = new FakeJob(parent); 0275 auto startMode = startModeForParent(parent); 0276 if (m_data->collection(collection.id()).isValid()) { 0277 Utils::JobHandler::install(job, [=] { 0278 if (!job->error()) { 0279 m_data->removeCollection(collection); 0280 } 0281 }, startMode); 0282 } else { 0283 job->setExpectedError(1, QStringLiteral("The collection doesn't exist")); 0284 Utils::JobHandler::install(job, noop, startMode); 0285 } 0286 return job; 0287 } 0288 0289 KJob *AkonadiFakeStorage::createTransaction(QObject *parent) 0290 { 0291 auto job = new AkonadiFakeTransaction(parent); 0292 Utils::JobHandler::install(job, noop); 0293 return job; 0294 } 0295 0296 Akonadi::CollectionFetchJobInterface *AkonadiFakeStorage::fetchCollections(Akonadi::Collection collection, 0297 Akonadi::StorageInterface::FetchDepth depth, 0298 QObject *parent) 0299 { 0300 auto job = new AkonadiFakeCollectionFetchJob(parent); 0301 auto children = Akonadi::Collection::List(); 0302 0303 switch (depth) { 0304 case Base: 0305 children << m_data->collection(findId(collection)); 0306 break; 0307 case FirstLevel: 0308 children << m_data->childCollections(findId(collection)); 0309 break; 0310 case Recursive: 0311 children = collectChildren(collection); 0312 break; 0313 } 0314 0315 auto collections = children; 0316 0317 if (depth != Base) { 0318 // Replace the dummy parents in the ancestor chain with proper ones 0319 // full of juicy data 0320 using namespace std::placeholders; 0321 auto completeCollection = std::bind(&AkonadiFakeData::reconstructAncestors, 0322 m_data, _1, collection); 0323 std::transform(collections.begin(), collections.end(), 0324 collections.begin(), completeCollection); 0325 } 0326 0327 const auto behavior = m_data->storageBehavior().fetchCollectionsBehavior(collection.id()); 0328 if (behavior == AkonadiFakeStorageBehavior::NormalFetch) 0329 job->setCollections(collections); 0330 job->setExpectedError(m_data->storageBehavior().fetchCollectionsErrorCode(collection.id())); 0331 Utils::JobHandler::install(job, noop); 0332 return job; 0333 } 0334 0335 Akonadi::ItemFetchJobInterface *AkonadiFakeStorage::fetchItems(Akonadi::Collection collection, QObject *parent) 0336 { 0337 auto items = m_data->childItems(findId(collection)); 0338 std::transform(items.begin(), items.end(), 0339 items.begin(), 0340 [this] (const Akonadi::Item &item) { 0341 auto result = m_data->reconstructItemDependencies(item); 0342 // Force payload detach 0343 result.setPayloadFromData(result.payloadData()); 0344 return result; 0345 }); 0346 0347 auto job = new AkonadiFakeItemFetchJob(parent); 0348 const auto behavior = m_data->storageBehavior().fetchItemsBehavior(collection.id()); 0349 if (behavior == AkonadiFakeStorageBehavior::NormalFetch) 0350 job->setItems(items); 0351 job->setExpectedError(m_data->storageBehavior().fetchItemsErrorCode(collection.id())); 0352 Utils::JobHandler::install(job, noop); 0353 return job; 0354 } 0355 0356 Akonadi::ItemFetchJobInterface *AkonadiFakeStorage::fetchItem(Akonadi::Item item, QObject *parent) 0357 { 0358 auto fullItem = m_data->item(findId(item)); 0359 fullItem = m_data->reconstructItemDependencies(fullItem); 0360 // Force payload detach 0361 fullItem.setPayloadFromData(fullItem.payloadData()); 0362 0363 auto job = new AkonadiFakeItemFetchJob(parent); 0364 const auto behavior = m_data->storageBehavior().fetchItemBehavior(item.id()); 0365 if (behavior == AkonadiFakeStorageBehavior::NormalFetch) 0366 job->setItems(Akonadi::Item::List() << fullItem); 0367 job->setExpectedError(m_data->storageBehavior().fetchItemErrorCode(item.id())); 0368 Utils::JobHandler::install(job, noop); 0369 return job; 0370 } 0371 0372 Akonadi::Collection::Id AkonadiFakeStorage::findId(const Akonadi::Collection &collection) 0373 { 0374 if (collection.isValid() || collection.remoteId().isEmpty()) 0375 return collection.id(); 0376 0377 const auto remoteId = collection.remoteId(); 0378 auto collections = m_data->collections(); 0379 auto result = std::find_if(collections.constBegin(), collections.constEnd(), 0380 [remoteId] (const Akonadi::Collection &collection) { 0381 return collection.remoteId() == remoteId; 0382 }); 0383 return (result != collections.constEnd()) ? result->id() : collection.id(); 0384 } 0385 0386 Akonadi::Item::Id AkonadiFakeStorage::findId(const Akonadi::Item &item) 0387 { 0388 if (item.isValid() || item.remoteId().isEmpty()) 0389 return item.id(); 0390 0391 const auto remoteId = item.remoteId(); 0392 auto items = m_data->items(); 0393 auto result = std::find_if(items.constBegin(), items.constEnd(), 0394 [remoteId] (const Akonadi::Item &item) { 0395 return item.remoteId() == remoteId; 0396 }); 0397 return (result != items.constEnd()) ? result->id() : item.id(); 0398 0399 } 0400 0401 Akonadi::Collection::List AkonadiFakeStorage::collectChildren(const Akonadi::Collection &root) 0402 { 0403 auto collections = Akonadi::Collection::List(); 0404 0405 foreach (const auto &child, m_data->childCollections(findId(root))) { 0406 if (child.enabled()) 0407 collections << m_data->collection(findId(child)); 0408 collections += collectChildren(child); 0409 } 0410 0411 return collections; 0412 } 0413 0414 #include "akonadifakestorage.moc"