1 /*  This file is part of the KDE project
2     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
3     SPDX-FileContributor: Kevin Krammer <krake@kdab.com>
4 
5     SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "abstractlocalstore.h"
9 
10 #include "collectioncreatejob.h"
11 #include "collectiondeletejob.h"
12 #include "collectionfetchjob.h"
13 #include "collectionmodifyjob.h"
14 #include "collectionmovejob.h"
15 #include "itemcreatejob.h"
16 #include "itemdeletejob.h"
17 #include "itemfetchjob.h"
18 #include "itemmodifyjob.h"
19 #include "itemmovejob.h"
20 #include "sessionimpls_p.h"
21 #include "storecompactjob.h"
22 
23 #include <KRandom>
24 #include <QRandomGenerator>
25 
26 #include <QTest>
27 
28 using namespace Akonadi;
29 using namespace Akonadi::FileStore;
30 
31 class TestStore : public AbstractLocalStore
32 {
33     Q_OBJECT
34 
35 public:
TestStore()36     TestStore()
37         : mLastCheckedJob(nullptr)
38         , mLastProcessedJob(nullptr)
39         , mErrorCode(0)
40     {
41     }
42 
43 public:
44     mutable Akonadi::FileStore::Job *mLastCheckedJob = nullptr;
45     Akonadi::FileStore::Job *mLastProcessedJob = nullptr;
46 
47     Collection mTopLevelCollection;
48 
49     int mErrorCode;
50     QString mErrorText;
51 
52 protected:
53     void processJob(Akonadi::FileStore::Job *job) override;
54 
55 protected:
setTopLevelCollection(const Collection & collection)56     void setTopLevelCollection(const Collection &collection) override
57     {
58         mTopLevelCollection = collection;
59 
60         Collection modifiedCollection = collection;
61         modifiedCollection.setContentMimeTypes(QStringList() << Collection::mimeType());
62 
63         AbstractLocalStore::setTopLevelCollection(modifiedCollection);
64     }
65 
checkCollectionCreate(Akonadi::FileStore::CollectionCreateJob * job,int & errorCode,QString & errorText) const66     void checkCollectionCreate(Akonadi::FileStore::CollectionCreateJob *job, int &errorCode, QString &errorText) const override
67     {
68         mLastCheckedJob = job;
69         errorCode = mErrorCode;
70         errorText = mErrorText;
71     }
72 
checkCollectionDelete(Akonadi::FileStore::CollectionDeleteJob * job,int & errorCode,QString & errorText) const73     void checkCollectionDelete(Akonadi::FileStore::CollectionDeleteJob *job, int &errorCode, QString &errorText) const override
74     {
75         mLastCheckedJob = job;
76         errorCode = mErrorCode;
77         errorText = mErrorText;
78     }
79 
checkCollectionFetch(Akonadi::FileStore::CollectionFetchJob * job,int & errorCode,QString & errorText) const80     void checkCollectionFetch(Akonadi::FileStore::CollectionFetchJob *job, int &errorCode, QString &errorText) const override
81     {
82         mLastCheckedJob = job;
83         errorCode = mErrorCode;
84         errorText = mErrorText;
85     }
86 
checkCollectionModify(Akonadi::FileStore::CollectionModifyJob * job,int & errorCode,QString & errorText) const87     void checkCollectionModify(Akonadi::FileStore::CollectionModifyJob *job, int &errorCode, QString &errorText) const override
88     {
89         mLastCheckedJob = job;
90         errorCode = mErrorCode;
91         errorText = mErrorText;
92     }
93 
checkCollectionMove(Akonadi::FileStore::CollectionMoveJob * job,int & errorCode,QString & errorText) const94     void checkCollectionMove(Akonadi::FileStore::CollectionMoveJob *job, int &errorCode, QString &errorText) const override
95     {
96         mLastCheckedJob = job;
97         errorCode = mErrorCode;
98         errorText = mErrorText;
99     }
100 
checkItemCreate(Akonadi::FileStore::ItemCreateJob * job,int & errorCode,QString & errorText) const101     void checkItemCreate(Akonadi::FileStore::ItemCreateJob *job, int &errorCode, QString &errorText) const override
102     {
103         mLastCheckedJob = job;
104         errorCode = mErrorCode;
105         errorText = mErrorText;
106     }
107 
checkItemDelete(Akonadi::FileStore::ItemDeleteJob * job,int & errorCode,QString & errorText) const108     void checkItemDelete(Akonadi::FileStore::ItemDeleteJob *job, int &errorCode, QString &errorText) const override
109     {
110         mLastCheckedJob = job;
111         errorCode = mErrorCode;
112         errorText = mErrorText;
113     }
114 
checkItemFetch(Akonadi::FileStore::ItemFetchJob * job,int & errorCode,QString & errorText) const115     void checkItemFetch(Akonadi::FileStore::ItemFetchJob *job, int &errorCode, QString &errorText) const override
116     {
117         mLastCheckedJob = job;
118         errorCode = mErrorCode;
119         errorText = mErrorText;
120     }
121 
checkItemModify(Akonadi::FileStore::ItemModifyJob * job,int & errorCode,QString & errorText) const122     void checkItemModify(Akonadi::FileStore::ItemModifyJob *job, int &errorCode, QString &errorText) const override
123     {
124         mLastCheckedJob = job;
125         errorCode = mErrorCode;
126         errorText = mErrorText;
127     }
128 
checkItemMove(Akonadi::FileStore::ItemMoveJob * job,int & errorCode,QString & errorText) const129     void checkItemMove(Akonadi::FileStore::ItemMoveJob *job, int &errorCode, QString &errorText) const override
130     {
131         mLastCheckedJob = job;
132         errorCode = mErrorCode;
133         errorText = mErrorText;
134     }
135 
checkStoreCompact(Akonadi::FileStore::StoreCompactJob * job,int & errorCode,QString & errorText) const136     void checkStoreCompact(Akonadi::FileStore::StoreCompactJob *job, int &errorCode, QString &errorText) const override
137     {
138         mLastCheckedJob = job;
139         errorCode = mErrorCode;
140         errorText = mErrorText;
141     }
142 };
143 
processJob(Akonadi::FileStore::Job * job)144 void TestStore::processJob(Akonadi::FileStore::Job *job)
145 {
146     mLastProcessedJob = job;
147 
148     QCOMPARE(currentJob(), job);
149     QVERIFY(job->error() == 0);
150 
151     if (mErrorCode != 0) {
152         notifyError(mErrorCode, mErrorText);
153     }
154 }
155 
156 class AbstractLocalStoreTest : public QObject
157 {
158     Q_OBJECT
159 
160 public:
AbstractLocalStoreTest()161     AbstractLocalStoreTest()
162         : QObject()
163         , mStore(nullptr)
164     {
165     }
166 
~AbstractLocalStoreTest()167     ~AbstractLocalStoreTest() override
168     {
169         delete mStore;
170     }
171 
172 private:
173     TestStore *mStore = nullptr;
174 
175 private Q_SLOTS:
176     void init();
177     void testSetPath();
178     void testCreateCollection();
179     void testDeleteCollection();
180     void testFetchCollection();
181     void testModifyCollection();
182     void testMoveCollection();
183     void testFetchItems();
184     void testFetchItem();
185     void testCreateItem();
186     void testDeleteItem();
187     void testModifyItem();
188     void testMoveItem();
189     void testCompactStore();
190 };
191 
init()192 void AbstractLocalStoreTest::init()
193 {
194     delete mStore;
195     mStore = new TestStore;
196 }
197 
testSetPath()198 void AbstractLocalStoreTest::testSetPath()
199 {
200     const QString file = KRandom::randomString(10);
201     const QString path = QLatin1String("/tmp/test/") + file;
202 
203     // check that setTopLevelCollection() has been called
204     mStore->setPath(path);
205     QCOMPARE(mStore->mTopLevelCollection.remoteId(), path);
206 
207     // check that the modified collection is the one returned by topLevelCollection()
208     QVERIFY(mStore->mTopLevelCollection.contentMimeTypes().isEmpty());
209     QCOMPARE(mStore->topLevelCollection().remoteId(), path);
210     QCOMPARE(mStore->topLevelCollection().contentMimeTypes(), QStringList() << Collection::mimeType());
211     QCOMPARE(mStore->topLevelCollection().name(), file);
212 
213     // check that calling with the same path again, does not call the template method
214     mStore->mTopLevelCollection = Collection();
215     mStore->setPath(path);
216     QVERIFY(mStore->mTopLevelCollection.remoteId().isEmpty());
217     QCOMPARE(mStore->topLevelCollection().remoteId(), path);
218     QCOMPARE(mStore->topLevelCollection().contentMimeTypes(), QStringList() << Collection::mimeType());
219     QCOMPARE(mStore->topLevelCollection().name(), file);
220 
221     // check that calling with a different path works like the first call
222     const QString file2 = KRandom::randomString(10);
223     const QString path2 = QLatin1String("/tmp/test2/") + file2;
224 
225     mStore->setPath(path2);
226     QCOMPARE(mStore->mTopLevelCollection.remoteId(), path2);
227     QCOMPARE(mStore->topLevelCollection().remoteId(), path2);
228     QCOMPARE(mStore->topLevelCollection().contentMimeTypes(), QStringList() << Collection::mimeType());
229     QCOMPARE(mStore->topLevelCollection().name(), file2);
230 }
231 
testCreateCollection()232 void AbstractLocalStoreTest::testCreateCollection()
233 {
234     Akonadi::FileStore::CollectionCreateJob *job = nullptr;
235 
236     // test without setPath()
237     job = mStore->createCollection(Collection(), Collection());
238     QVERIFY(job != nullptr);
239     QCOMPARE(mStore->mLastCheckedJob, job);
240     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidStoreState);
241     QVERIFY(!job->errorText().isEmpty());
242 
243     QVERIFY(!job->exec());
244     QVERIFY(mStore->mLastProcessedJob == nullptr);
245 
246     // test with path but with invalid collections
247     mStore->setPath(QStringLiteral("/tmp/test"));
248     job = mStore->createCollection(Collection(), Collection());
249     QVERIFY(job != nullptr);
250     QCOMPARE(mStore->mLastCheckedJob, job);
251     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
252     QVERIFY(!job->errorText().isEmpty());
253 
254     QVERIFY(!job->exec());
255     QVERIFY(mStore->mLastProcessedJob == nullptr);
256 
257     // test with potentially valid collection (has remoteId), but invalid target parent
258     Collection collection;
259     collection.setRemoteId(QStringLiteral("/tmp/test/foo"));
260     job = mStore->createCollection(collection, Collection());
261     QVERIFY(job != nullptr);
262     QCOMPARE(mStore->mLastCheckedJob, job);
263     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
264     QVERIFY(!job->errorText().isEmpty());
265 
266     QVERIFY(!job->exec());
267     QVERIFY(mStore->mLastProcessedJob == nullptr);
268 
269     // test with potentially valid collections
270     Collection targetParent;
271     targetParent.setRemoteId(QStringLiteral("/tmp/test2"));
272     job = mStore->createCollection(collection, targetParent);
273     QVERIFY(job != nullptr);
274     QCOMPARE(mStore->mLastCheckedJob, job);
275     QCOMPARE(job->error(), 0);
276     QVERIFY(job->errorText().isEmpty());
277 
278     QVERIFY(job->exec());
279     QCOMPARE(mStore->mLastProcessedJob, job);
280     mStore->mLastProcessedJob = nullptr;
281 
282     // test template check method
283     mStore->mErrorCode = QRandomGenerator::global()->generate() + 1;
284     mStore->mErrorText = KRandom::randomString(10);
285 
286     job = mStore->createCollection(collection, targetParent);
287     QVERIFY(job != nullptr);
288     QCOMPARE(mStore->mLastCheckedJob, job);
289     QCOMPARE(job->error(), mStore->mErrorCode);
290     QCOMPARE(job->errorText(), mStore->mErrorText);
291 
292     QVERIFY(!job->exec());
293     QVERIFY(mStore->mLastProcessedJob == nullptr);
294     mStore->mErrorCode = 0;
295     mStore->mErrorText = QString();
296 }
297 
testDeleteCollection()298 void AbstractLocalStoreTest::testDeleteCollection()
299 {
300     Akonadi::FileStore::CollectionDeleteJob *job = nullptr;
301 
302     // test without setPath()
303     job = mStore->deleteCollection(Collection());
304     QVERIFY(job != nullptr);
305     QCOMPARE(mStore->mLastCheckedJob, job);
306     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidStoreState);
307     QVERIFY(!job->errorText().isEmpty());
308 
309     QVERIFY(!job->exec());
310     QVERIFY(mStore->mLastProcessedJob == nullptr);
311 
312     // test with path but with invalid collection
313     mStore->setPath(QStringLiteral("/tmp/test"));
314     job = mStore->deleteCollection(Collection());
315     QVERIFY(job != nullptr);
316     QCOMPARE(mStore->mLastCheckedJob, job);
317     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
318     QVERIFY(!job->errorText().isEmpty());
319 
320     QVERIFY(!job->exec());
321     QVERIFY(mStore->mLastProcessedJob == nullptr);
322 
323     // test with ivalid collection (has remoteId, but no parent collection remoteId)
324     Collection collection;
325     collection.setRemoteId(QStringLiteral("/tmp/test/foo"));
326     job = mStore->deleteCollection(collection);
327     QVERIFY(job != nullptr);
328     QCOMPARE(mStore->mLastCheckedJob, job);
329     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
330     QVERIFY(!job->errorText().isEmpty());
331 
332     QVERIFY(!job->exec());
333     QVERIFY(mStore->mLastProcessedJob == nullptr);
334 
335     // test with potentially valid collection (has remoteId and parent collection remoteId)
336     Collection parentCollection;
337     parentCollection.setRemoteId(QStringLiteral("/tmp/test"));
338     collection.setParentCollection(parentCollection);
339     job = mStore->deleteCollection(collection);
340     QVERIFY(job != nullptr);
341     QCOMPARE(mStore->mLastCheckedJob, job);
342     QCOMPARE(job->error(), 0);
343     QVERIFY(job->errorText().isEmpty());
344 
345     QVERIFY(job->exec());
346     QCOMPARE(mStore->mLastProcessedJob, job);
347     mStore->mLastProcessedJob = nullptr;
348 
349     // test template check method
350     mStore->mErrorCode = QRandomGenerator::global()->generate() + 1;
351     mStore->mErrorText = KRandom::randomString(10);
352 
353     job = mStore->deleteCollection(collection);
354     QVERIFY(job != nullptr);
355     QCOMPARE(mStore->mLastCheckedJob, job);
356     QCOMPARE(job->error(), mStore->mErrorCode);
357     QCOMPARE(job->errorText(), mStore->mErrorText);
358 
359     QVERIFY(!job->exec());
360     QVERIFY(mStore->mLastProcessedJob == nullptr);
361     mStore->mErrorCode = 0;
362     mStore->mErrorText = QString();
363 }
364 
testFetchCollection()365 void AbstractLocalStoreTest::testFetchCollection()
366 {
367     Akonadi::FileStore::CollectionFetchJob *job = nullptr;
368 
369     // test without setPath()
370     job = mStore->fetchCollections(Collection());
371     QVERIFY(job != nullptr);
372     QCOMPARE(mStore->mLastCheckedJob, job);
373     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidStoreState);
374     QVERIFY(!job->errorText().isEmpty());
375 
376     QVERIFY(!job->exec());
377     QVERIFY(mStore->mLastProcessedJob == nullptr);
378 
379     // test with path but with invalid collection
380     mStore->setPath(QStringLiteral("/tmp/test"));
381     job = mStore->fetchCollections(Collection());
382     QVERIFY(job != nullptr);
383     QCOMPARE(mStore->mLastCheckedJob, job);
384     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
385     QVERIFY(!job->errorText().isEmpty());
386 
387     QVERIFY(!job->exec());
388     QVERIFY(mStore->mLastProcessedJob == nullptr);
389 
390     // test with potentially valid collection (has remoteId)
391     Collection collection;
392     collection.setRemoteId(QStringLiteral("/tmp/test/foo"));
393     job = mStore->fetchCollections(collection);
394     QVERIFY(job != nullptr);
395     QCOMPARE(mStore->mLastCheckedJob, job);
396     QCOMPARE(job->error(), 0);
397     QVERIFY(job->errorText().isEmpty());
398 
399     QVERIFY(job->exec());
400     QCOMPARE(mStore->mLastProcessedJob, job);
401     mStore->mLastProcessedJob = nullptr;
402 
403     // test template check method
404     mStore->mErrorCode = QRandomGenerator::global()->generate() + 1;
405     mStore->mErrorText = KRandom::randomString(10);
406 
407     job = mStore->fetchCollections(collection);
408     QVERIFY(job != nullptr);
409     QCOMPARE(mStore->mLastCheckedJob, job);
410     QCOMPARE(job->error(), mStore->mErrorCode);
411     QCOMPARE(job->errorText(), mStore->mErrorText);
412 
413     QVERIFY(!job->exec());
414     QVERIFY(mStore->mLastProcessedJob == nullptr);
415     mStore->mErrorCode = 0;
416     mStore->mErrorText = QString();
417 
418     // test fetch of top level collection only
419     collection.setRemoteId(mStore->topLevelCollection().remoteId());
420     job = mStore->fetchCollections(collection, Akonadi::FileStore::CollectionFetchJob::Base);
421     QVERIFY(job != nullptr);
422     QCOMPARE(mStore->mLastCheckedJob, job);
423     QCOMPARE(job->error(), 0);
424     QVERIFY(job->errorText().isEmpty());
425 
426     QVERIFY(job->exec());
427     QVERIFY(mStore->mLastProcessedJob == nullptr); // job not handed to subclass because it is full processed in base class
428     QCOMPARE(job->collections().count(), 1);
429     QCOMPARE(job->collections()[0], mStore->topLevelCollection());
430 }
431 
testModifyCollection()432 void AbstractLocalStoreTest::testModifyCollection()
433 {
434     Akonadi::FileStore::CollectionModifyJob *job = nullptr;
435 
436     // test without setPath()
437     job = mStore->modifyCollection(Collection());
438     QVERIFY(job != nullptr);
439     QCOMPARE(mStore->mLastCheckedJob, job);
440     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidStoreState);
441     QVERIFY(!job->errorText().isEmpty());
442 
443     QVERIFY(!job->exec());
444     QVERIFY(mStore->mLastProcessedJob == nullptr);
445 
446     // test with path but with invalid item
447     mStore->setPath(QStringLiteral("/tmp/test"));
448     job = mStore->modifyCollection(Collection());
449     QVERIFY(job != nullptr);
450     QCOMPARE(mStore->mLastCheckedJob, job);
451     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
452     QVERIFY(!job->errorText().isEmpty());
453 
454     QVERIFY(!job->exec());
455     QVERIFY(mStore->mLastProcessedJob == nullptr);
456 
457     // test with potentially valid collection (has remoteId, but no parent remoteId)
458     Collection collection;
459     collection.setRemoteId(QStringLiteral("/tmp/test/foo"));
460     job = mStore->modifyCollection(collection);
461     QVERIFY(job != nullptr);
462     QCOMPARE(mStore->mLastCheckedJob, job);
463     QCOMPARE(job->error(), 0);
464     QVERIFY(job->errorText().isEmpty());
465 
466     QVERIFY(job->exec());
467     QCOMPARE(mStore->mLastProcessedJob, job);
468     mStore->mLastProcessedJob = nullptr;
469 
470     // test with potentially valid collection (has remoteId and parent remoteId)
471     Collection parentCollection;
472     parentCollection.setRemoteId(QStringLiteral("/tmp/test"));
473     collection.setParentCollection(parentCollection);
474     job = mStore->modifyCollection(collection);
475     QVERIFY(job != nullptr);
476     QCOMPARE(mStore->mLastCheckedJob, job);
477     QCOMPARE(job->error(), 0);
478     QVERIFY(job->errorText().isEmpty());
479 
480     QVERIFY(job->exec());
481     QCOMPARE(mStore->mLastProcessedJob, job);
482     mStore->mLastProcessedJob = nullptr;
483 
484     // test template check method
485     mStore->mErrorCode = QRandomGenerator::global()->generate() + 1;
486     mStore->mErrorText = KRandom::randomString(10);
487 
488     job = mStore->modifyCollection(collection);
489     QVERIFY(job != nullptr);
490     QCOMPARE(mStore->mLastCheckedJob, job);
491     QCOMPARE(job->error(), mStore->mErrorCode);
492     QCOMPARE(job->errorText(), mStore->mErrorText);
493 
494     QVERIFY(!job->exec());
495     QVERIFY(mStore->mLastProcessedJob == nullptr);
496 }
497 
testMoveCollection()498 void AbstractLocalStoreTest::testMoveCollection()
499 {
500     Akonadi::FileStore::CollectionMoveJob *job = nullptr;
501 
502     // test without setPath()
503     job = mStore->moveCollection(Collection(), Collection());
504     QVERIFY(job != nullptr);
505     QCOMPARE(mStore->mLastCheckedJob, job);
506     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidStoreState);
507     QVERIFY(!job->errorText().isEmpty());
508 
509     QVERIFY(!job->exec());
510     QVERIFY(mStore->mLastProcessedJob == nullptr);
511 
512     // test with path but with invalid collections
513     mStore->setPath(QStringLiteral("/tmp/test"));
514     job = mStore->moveCollection(Collection(), Collection());
515     QVERIFY(job != nullptr);
516     QCOMPARE(mStore->mLastCheckedJob, job);
517     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
518     QVERIFY(!job->errorText().isEmpty());
519 
520     QVERIFY(!job->exec());
521     QVERIFY(mStore->mLastProcessedJob == nullptr);
522 
523     // test with potentially valid collection (has remoteId and parent remoteId), but invalid target parent
524     Collection collection;
525     collection.setRemoteId(QStringLiteral("/tmp/test/foo"));
526     Collection parentCollection;
527     parentCollection.setRemoteId(QStringLiteral("/tmp/test"));
528     collection.setParentCollection(parentCollection);
529     job = mStore->moveCollection(collection, Collection());
530     QVERIFY(job != nullptr);
531     QCOMPARE(mStore->mLastCheckedJob, job);
532     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
533     QVERIFY(!job->errorText().isEmpty());
534 
535     QVERIFY(!job->exec());
536     QVERIFY(mStore->mLastProcessedJob == nullptr);
537 
538     // test with invalid parent collection, but with potentially valid collection and target parent
539     collection.setParentCollection(Collection());
540     job = mStore->moveCollection(collection, parentCollection);
541     QVERIFY(job != nullptr);
542     QCOMPARE(mStore->mLastCheckedJob, job);
543     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
544     QVERIFY(!job->errorText().isEmpty());
545 
546     QVERIFY(!job->exec());
547     QVERIFY(mStore->mLastProcessedJob == nullptr);
548 
549     // test with potentially valid collections
550     Collection targetParent;
551     targetParent.setRemoteId(QStringLiteral("/tmp/test2"));
552     collection.setParentCollection(parentCollection);
553     job = mStore->moveCollection(collection, targetParent);
554     QVERIFY(job != nullptr);
555     QCOMPARE(mStore->mLastCheckedJob, job);
556     QCOMPARE(job->error(), 0);
557     QVERIFY(job->errorText().isEmpty());
558 
559     QVERIFY(job->exec());
560     QCOMPARE(mStore->mLastProcessedJob, job);
561     mStore->mLastProcessedJob = nullptr;
562 
563     // test template check method
564     mStore->mErrorCode = QRandomGenerator::global()->generate() + 1;
565     mStore->mErrorText = KRandom::randomString(10);
566 
567     job = mStore->moveCollection(collection, targetParent);
568     QVERIFY(job != nullptr);
569     QCOMPARE(mStore->mLastCheckedJob, job);
570     QCOMPARE(job->error(), mStore->mErrorCode);
571     QCOMPARE(job->errorText(), mStore->mErrorText);
572 
573     QVERIFY(!job->exec());
574     QVERIFY(mStore->mLastProcessedJob == nullptr);
575 }
576 
testFetchItems()577 void AbstractLocalStoreTest::testFetchItems()
578 {
579     Akonadi::FileStore::ItemFetchJob *job = nullptr;
580 
581     // test without setPath()
582     job = mStore->fetchItems(Collection());
583     QVERIFY(job != nullptr);
584     QCOMPARE(mStore->mLastCheckedJob, job);
585     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidStoreState);
586     QVERIFY(!job->errorText().isEmpty());
587 
588     QVERIFY(!job->exec());
589     QVERIFY(mStore->mLastProcessedJob == nullptr);
590 
591     // test with path but with invalid collection
592     mStore->setPath(QStringLiteral("/tmp/test"));
593     job = mStore->fetchItems(Collection());
594     QVERIFY(job != nullptr);
595     QCOMPARE(mStore->mLastCheckedJob, job);
596     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
597     QVERIFY(!job->errorText().isEmpty());
598 
599     QVERIFY(!job->exec());
600     QVERIFY(mStore->mLastProcessedJob == nullptr);
601 
602     // test with potentially valid collection (has remoteId)
603     Collection collection;
604     collection.setRemoteId(QStringLiteral("/tmp/test/foo"));
605     job = mStore->fetchItems(collection);
606     QVERIFY(job != nullptr);
607     QCOMPARE(mStore->mLastCheckedJob, job);
608     QCOMPARE(job->error(), 0);
609     QVERIFY(job->errorText().isEmpty());
610 
611     QVERIFY(job->exec());
612     QCOMPARE(mStore->mLastProcessedJob, job);
613     mStore->mLastProcessedJob = nullptr;
614 
615     // test template check method
616     mStore->mErrorCode = QRandomGenerator::global()->generate() + 1;
617     mStore->mErrorText = KRandom::randomString(10);
618 
619     job = mStore->fetchItems(collection);
620     QVERIFY(job != nullptr);
621     QCOMPARE(mStore->mLastCheckedJob, job);
622     QCOMPARE(job->error(), mStore->mErrorCode);
623     QCOMPARE(job->errorText(), mStore->mErrorText);
624 
625     QVERIFY(!job->exec());
626     QVERIFY(mStore->mLastProcessedJob == nullptr);
627     mStore->mErrorCode = 0;
628     mStore->mErrorText = QString();
629 }
630 
testFetchItem()631 void AbstractLocalStoreTest::testFetchItem()
632 {
633     Akonadi::FileStore::ItemFetchJob *job = nullptr;
634 
635     // test without setPath()
636     job = mStore->fetchItem(Item());
637     QVERIFY(job != nullptr);
638     QCOMPARE(mStore->mLastCheckedJob, job);
639     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidStoreState);
640     QVERIFY(!job->errorText().isEmpty());
641 
642     QVERIFY(!job->exec());
643     QVERIFY(mStore->mLastProcessedJob == nullptr);
644 
645     // test with path but with invalid item
646     mStore->setPath(QStringLiteral("/tmp/test"));
647     job = mStore->fetchItem(Item());
648     QVERIFY(job != nullptr);
649     QCOMPARE(mStore->mLastCheckedJob, job);
650     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
651     QVERIFY(!job->errorText().isEmpty());
652 
653     QVERIFY(!job->exec());
654     QVERIFY(mStore->mLastProcessedJob == nullptr);
655 
656     // test with potentially valid item (has remoteId)
657     Item item;
658     item.setRemoteId(QStringLiteral("/tmp/test/foo"));
659     job = mStore->fetchItem(item);
660     QVERIFY(job != nullptr);
661     QCOMPARE(mStore->mLastCheckedJob, job);
662     QCOMPARE(job->error(), 0);
663     QVERIFY(job->errorText().isEmpty());
664 
665     QVERIFY(job->exec());
666     QCOMPARE(mStore->mLastProcessedJob, job);
667     mStore->mLastProcessedJob = nullptr;
668 
669     // test template check method
670     mStore->mErrorCode = QRandomGenerator::global()->generate() + 1;
671     mStore->mErrorText = KRandom::randomString(10);
672 
673     job = mStore->fetchItem(item);
674     QVERIFY(job != nullptr);
675     QCOMPARE(mStore->mLastCheckedJob, job);
676     QCOMPARE(job->error(), mStore->mErrorCode);
677     QCOMPARE(job->errorText(), mStore->mErrorText);
678 
679     QVERIFY(!job->exec());
680     QVERIFY(mStore->mLastProcessedJob == nullptr);
681     mStore->mErrorCode = 0;
682     mStore->mErrorText = QString();
683 }
684 
testCreateItem()685 void AbstractLocalStoreTest::testCreateItem()
686 {
687     Akonadi::FileStore::ItemCreateJob *job = nullptr;
688 
689     // test without setPath()
690     job = mStore->createItem(Item(), Collection());
691     QVERIFY(job != nullptr);
692     QCOMPARE(mStore->mLastCheckedJob, job);
693     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidStoreState);
694     QVERIFY(!job->errorText().isEmpty());
695 
696     QVERIFY(!job->exec());
697     QVERIFY(mStore->mLastProcessedJob == nullptr);
698 
699     // test with path but with invalid collection
700     mStore->setPath(QStringLiteral("/tmp/test"));
701     job = mStore->createItem(Item(), Collection());
702     QVERIFY(job != nullptr);
703     QCOMPARE(mStore->mLastCheckedJob, job);
704     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
705     QVERIFY(!job->errorText().isEmpty());
706 
707     QVERIFY(!job->exec());
708     QVERIFY(mStore->mLastProcessedJob == nullptr);
709 
710     // test with potentially valid collection (has remoteId)
711     Collection collection;
712     collection.setRemoteId(QStringLiteral("/tmp/test/foo"));
713     job = mStore->createItem(Item(), collection);
714     QVERIFY(job != nullptr);
715     QCOMPARE(mStore->mLastCheckedJob, job);
716     QCOMPARE(job->error(), 0);
717     QVERIFY(job->errorText().isEmpty());
718 
719     QVERIFY(job->exec());
720     QCOMPARE(mStore->mLastProcessedJob, job);
721     mStore->mLastProcessedJob = nullptr;
722 
723     // test template check method
724     mStore->mErrorCode = QRandomGenerator::global()->generate() + 1;
725     mStore->mErrorText = KRandom::randomString(10);
726 
727     job = mStore->createItem(Item(), collection);
728     QVERIFY(job != nullptr);
729     QCOMPARE(mStore->mLastCheckedJob, job);
730     QCOMPARE(job->error(), mStore->mErrorCode);
731     QCOMPARE(job->errorText(), mStore->mErrorText);
732 
733     QVERIFY(!job->exec());
734     QVERIFY(mStore->mLastProcessedJob == nullptr);
735     mStore->mErrorCode = 0;
736     mStore->mErrorText = QString();
737 }
738 
testDeleteItem()739 void AbstractLocalStoreTest::testDeleteItem()
740 {
741     Akonadi::FileStore::ItemDeleteJob *job = nullptr;
742 
743     // test without setPath()
744     job = mStore->deleteItem(Item());
745     QVERIFY(job != nullptr);
746     QCOMPARE(mStore->mLastCheckedJob, job);
747     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidStoreState);
748     QVERIFY(!job->errorText().isEmpty());
749 
750     QVERIFY(!job->exec());
751     QVERIFY(mStore->mLastProcessedJob == nullptr);
752 
753     // test with path but with invalid item
754     mStore->setPath(QStringLiteral("/tmp/test"));
755     job = mStore->deleteItem(Item());
756     QVERIFY(job != nullptr);
757     QCOMPARE(mStore->mLastCheckedJob, job);
758     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
759     QVERIFY(!job->errorText().isEmpty());
760 
761     QVERIFY(!job->exec());
762     QVERIFY(mStore->mLastProcessedJob == nullptr);
763 
764     // test with potentially valid item (has remoteId)
765     Item item;
766     item.setRemoteId(QStringLiteral("/tmp/test/foo"));
767     job = mStore->deleteItem(item);
768     QVERIFY(job != nullptr);
769     QCOMPARE(mStore->mLastCheckedJob, job);
770     QCOMPARE(job->error(), 0);
771     QVERIFY(job->errorText().isEmpty());
772 
773     QVERIFY(job->exec());
774     QCOMPARE(mStore->mLastProcessedJob, job);
775     mStore->mLastProcessedJob = nullptr;
776 
777     // test template check method
778     mStore->mErrorCode = QRandomGenerator::global()->generate() + 1;
779     mStore->mErrorText = KRandom::randomString(10);
780 
781     job = mStore->deleteItem(item);
782     QVERIFY(job != nullptr);
783     QCOMPARE(mStore->mLastCheckedJob, job);
784     QCOMPARE(job->error(), mStore->mErrorCode);
785     QCOMPARE(job->errorText(), mStore->mErrorText);
786 
787     QVERIFY(!job->exec());
788     QVERIFY(mStore->mLastProcessedJob == nullptr);
789     mStore->mErrorCode = 0;
790     mStore->mErrorText = QString();
791 }
792 
testModifyItem()793 void AbstractLocalStoreTest::testModifyItem()
794 {
795     Akonadi::FileStore::ItemModifyJob *job = nullptr;
796 
797     // test without setPath()
798     job = mStore->modifyItem(Item());
799     QVERIFY(job != nullptr);
800     QCOMPARE(mStore->mLastCheckedJob, job);
801     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidStoreState);
802     QVERIFY(!job->errorText().isEmpty());
803 
804     QVERIFY(!job->exec());
805     QVERIFY(mStore->mLastProcessedJob == nullptr);
806 
807     // test with path but with invalid item
808     mStore->setPath(QStringLiteral("/tmp/test"));
809     job = mStore->modifyItem(Item());
810     QVERIFY(job != nullptr);
811     QCOMPARE(mStore->mLastCheckedJob, job);
812     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
813     QVERIFY(!job->errorText().isEmpty());
814 
815     QVERIFY(!job->exec());
816     QVERIFY(mStore->mLastProcessedJob == nullptr);
817 
818     // test with potentially valid item (has remoteId)
819     Item item;
820     item.setRemoteId(QStringLiteral("/tmp/test/foo"));
821     job = mStore->modifyItem(item);
822     QVERIFY(job != nullptr);
823     QCOMPARE(mStore->mLastCheckedJob, job);
824     QCOMPARE(job->error(), 0);
825     QVERIFY(job->errorText().isEmpty());
826 
827     QVERIFY(job->exec());
828     QCOMPARE(mStore->mLastProcessedJob, job);
829     mStore->mLastProcessedJob = nullptr;
830 
831     // test template check method
832     mStore->mErrorCode = QRandomGenerator::global()->generate() + 1;
833     mStore->mErrorText = KRandom::randomString(10);
834 
835     job = mStore->modifyItem(item);
836     QVERIFY(job != nullptr);
837     QCOMPARE(mStore->mLastCheckedJob, job);
838     QCOMPARE(job->error(), mStore->mErrorCode);
839     QCOMPARE(job->errorText(), mStore->mErrorText);
840 
841     QVERIFY(!job->exec());
842     QVERIFY(mStore->mLastProcessedJob == nullptr);
843 }
844 
testMoveItem()845 void AbstractLocalStoreTest::testMoveItem()
846 {
847     Akonadi::FileStore::ItemMoveJob *job = nullptr;
848 
849     // test without setPath()
850     job = mStore->moveItem(Item(), Collection());
851     QVERIFY(job != nullptr);
852     QCOMPARE(mStore->mLastCheckedJob, job);
853     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidStoreState);
854     QVERIFY(!job->errorText().isEmpty());
855 
856     QVERIFY(!job->exec());
857     QVERIFY(mStore->mLastProcessedJob == nullptr);
858 
859     // test with path but with invalid item and collection
860     mStore->setPath(QStringLiteral("/tmp/test"));
861     job = mStore->moveItem(Item(), Collection());
862     QVERIFY(job != nullptr);
863     QCOMPARE(mStore->mLastCheckedJob, job);
864     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
865     QVERIFY(!job->errorText().isEmpty());
866 
867     QVERIFY(!job->exec());
868     QVERIFY(mStore->mLastProcessedJob == nullptr);
869 
870     // test with potentially valid item (has remoteId and parent remoteId), but invalid target parent
871     Item item;
872     item.setRemoteId(QStringLiteral("/tmp/test/foo"));
873     Collection parentCollection;
874     parentCollection.setRemoteId(QStringLiteral("/tmp/test"));
875     item.setParentCollection(parentCollection);
876     job = mStore->moveItem(item, Collection());
877     QVERIFY(job != nullptr);
878     QCOMPARE(mStore->mLastCheckedJob, job);
879     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
880     QVERIFY(!job->errorText().isEmpty());
881 
882     QVERIFY(!job->exec());
883     QVERIFY(mStore->mLastProcessedJob == nullptr);
884 
885     // test with invalid parent collection, but with potentially valid item and target parent
886     item.setParentCollection(Collection());
887     job = mStore->moveItem(item, parentCollection);
888     QVERIFY(job != nullptr);
889     QCOMPARE(mStore->mLastCheckedJob, job);
890     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidJobContext);
891     QVERIFY(!job->errorText().isEmpty());
892 
893     QVERIFY(!job->exec());
894     QVERIFY(mStore->mLastProcessedJob == nullptr);
895 
896     // test with potentially valid item and collections
897     Collection targetParent;
898     targetParent.setRemoteId(QStringLiteral("/tmp/test2"));
899     item.setParentCollection(parentCollection);
900     job = mStore->moveItem(item, targetParent);
901     QVERIFY(job != nullptr);
902     QCOMPARE(mStore->mLastCheckedJob, job);
903     QCOMPARE(job->error(), 0);
904     QVERIFY(job->errorText().isEmpty());
905 
906     QVERIFY(job->exec());
907     QCOMPARE(mStore->mLastProcessedJob, job);
908     mStore->mLastProcessedJob = nullptr;
909 
910     // test template check method
911     mStore->mErrorCode = QRandomGenerator::global()->generate() + 1;
912     mStore->mErrorText = KRandom::randomString(10);
913 
914     job = mStore->moveItem(item, targetParent);
915     QVERIFY(job != nullptr);
916     QCOMPARE(mStore->mLastCheckedJob, job);
917     QCOMPARE(job->error(), mStore->mErrorCode);
918     QCOMPARE(job->errorText(), mStore->mErrorText);
919 
920     QVERIFY(!job->exec());
921     QVERIFY(mStore->mLastProcessedJob == nullptr);
922 }
923 
testCompactStore()924 void AbstractLocalStoreTest::testCompactStore()
925 {
926     Akonadi::FileStore::StoreCompactJob *job = nullptr;
927 
928     // test without setPath()
929     job = mStore->compactStore();
930     QVERIFY(job != nullptr);
931     QCOMPARE(mStore->mLastCheckedJob, job);
932     QCOMPARE(job->error(), (int)Akonadi::FileStore::Job::InvalidStoreState);
933     QVERIFY(!job->errorText().isEmpty());
934 
935     QVERIFY(!job->exec());
936     QVERIFY(mStore->mLastProcessedJob == nullptr);
937 
938     // test with path
939     mStore->setPath(QStringLiteral("/tmp/test"));
940     job = mStore->compactStore();
941     QVERIFY(job != nullptr);
942     QCOMPARE(mStore->mLastCheckedJob, job);
943     QCOMPARE(job->error(), 0);
944     QVERIFY(job->errorText().isEmpty());
945 
946     QVERIFY(job->exec());
947     QCOMPARE(mStore->mLastProcessedJob, job);
948     mStore->mLastProcessedJob = nullptr;
949 
950     // test template check method
951     mStore->mErrorCode = QRandomGenerator::global()->generate() + 1;
952     mStore->mErrorText = KRandom::randomString(10);
953 
954     job = mStore->compactStore();
955     QVERIFY(job != nullptr);
956     QCOMPARE(mStore->mLastCheckedJob, job);
957     QCOMPARE(job->error(), mStore->mErrorCode);
958     QCOMPARE(job->errorText(), mStore->mErrorText);
959 
960     QVERIFY(!job->exec());
961     QVERIFY(mStore->mLastProcessedJob == nullptr);
962     mStore->mErrorCode = 0;
963     mStore->mErrorText = QString();
964 }
965 
966 QTEST_MAIN(AbstractLocalStoreTest)
967 
968 #include "abstractlocalstoretest.moc"
969