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 "mixedmaildirstore.h"
9 
10 #include "testdatautil.h"
11 
12 #include "filestore/collectionmovejob.h"
13 #include "filestore/itemfetchjob.h"
14 
15 #include "libmaildir/maildir.h"
16 
17 #include <QTemporaryDir>
18 
19 #include <QDir>
20 #include <QFileInfo>
21 #include <QTest>
22 
23 using namespace Akonadi;
24 
25 class CollectionMoveTest : public QObject
26 {
27     Q_OBJECT
28 
29 public:
CollectionMoveTest()30     CollectionMoveTest()
31         : QObject()
32         , mStore(nullptr)
33         , mDir(nullptr)
34     {
35     }
36 
~CollectionMoveTest()37     ~CollectionMoveTest() override
38     {
39         delete mStore;
40         delete mDir;
41     }
42 
43 private:
44     MixedMaildirStore *mStore = nullptr;
45     QTemporaryDir *mDir = nullptr;
46 
47 private Q_SLOTS:
48     void init();
49     void cleanup();
50     void testMoveToTopLevel();
51     void testMoveToMaildir();
52     void testMoveToMBox();
53 };
54 
init()55 void CollectionMoveTest::init()
56 {
57     mStore = new MixedMaildirStore;
58 
59     mDir = new QTemporaryDir;
60     QVERIFY(mDir->isValid());
61     QVERIFY(QDir(mDir->path()).exists());
62 }
63 
cleanup()64 void CollectionMoveTest::cleanup()
65 {
66     delete mStore;
67     mStore = nullptr;
68     delete mDir;
69     mDir = nullptr;
70 }
71 
testMoveToTopLevel()72 void CollectionMoveTest::testMoveToTopLevel()
73 {
74     QDir topDir(mDir->path());
75 
76     // top level dir
77     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), topDir.path(), QStringLiteral("collection1")));
78     // QFileInfo fileInfo1(topDir, QStringLiteral("collection1"));
79     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), topDir.path(), QStringLiteral("collection2")));
80     // QFileInfo fileInfo2(topDir, QStringLiteral("collection2"));
81 
82     // first level maildir parent
83     QDir subDir1 = topDir;
84     QVERIFY(subDir1.mkdir(QStringLiteral(".collection1.directory")));
85     QVERIFY(subDir1.cd(QStringLiteral(".collection1.directory")));
86     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), subDir1.path(), QStringLiteral("collection1_1")));
87     QFileInfo fileInfo1_1(subDir1.path(), QStringLiteral("collection1_1"));
88     QVERIFY(fileInfo1_1.exists());
89     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), subDir1.path(), QStringLiteral("collection1_2")));
90     QFileInfo fileInfo1_2(subDir1.path(), QStringLiteral("collection1_2"));
91     QVERIFY(fileInfo1_2.exists());
92 
93     // first level mbox parent
94     QDir subDir2 = topDir;
95     QVERIFY(subDir2.mkdir(QStringLiteral(".collection2.directory")));
96     QVERIFY(subDir2.cd(QStringLiteral(".collection2.directory")));
97     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), subDir2.path(), QStringLiteral("collection2_1")));
98     QFileInfo fileInfo2_1(subDir2.path(), QStringLiteral("collection2_1"));
99     QVERIFY(fileInfo2_1.exists());
100     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), subDir2.path(), QStringLiteral("collection2_2")));
101     QFileInfo fileInfo2_2(subDir2.path(), QStringLiteral("collection2_2"));
102     QVERIFY(fileInfo2_2.exists());
103 
104     mStore->setPath(topDir.path());
105 
106     // common variables
107     FileStore::CollectionMoveJob *job = nullptr;
108     FileStore::ItemFetchJob *itemFetch = nullptr;
109     Collection collection;
110     const QVariant colListVar = QVariant::fromValue<Collection::List>(Collection::List());
111     QVariant var;
112     Collection::List collections;
113     Item::List items;
114     QMap<QByteArray, int> flagCounts;
115 
116     // test moving maildir from maildir parent
117     Collection collection1;
118     collection1.setName(QStringLiteral("collection1"));
119     collection1.setRemoteId(QStringLiteral("collection1"));
120     collection1.setParentCollection(mStore->topLevelCollection());
121 
122     Collection collection1_1;
123     collection1_1.setName(QStringLiteral("collection1_1"));
124     collection1_1.setRemoteId(QStringLiteral("collection1_1"));
125     collection1_1.setParentCollection(collection1);
126 
127     job = mStore->moveCollection(collection1_1, mStore->topLevelCollection());
128 
129     QVERIFY(job->exec());
130     QCOMPARE(job->error(), 0);
131 
132     collection = job->collection();
133     QCOMPARE(collection.remoteId(), collection1_1.remoteId());
134     QCOMPARE(collection.parentCollection(), mStore->topLevelCollection());
135 
136     fileInfo1_1.refresh();
137     QVERIFY(!fileInfo1_1.exists());
138     fileInfo1_1 = QFileInfo(topDir.path(), collection.remoteId());
139     QVERIFY(fileInfo1_1.exists());
140 
141     // check for index preservation
142     var = job->property("onDiskIndexInvalidated");
143     QVERIFY(var.isValid());
144     QCOMPARE(var.userType(), colListVar.userType());
145 
146     collections = var.value<Collection::List>();
147     QCOMPARE((int)collections.count(), 1);
148     QCOMPARE(collections.first(), collection);
149 
150     // get the items and check the flags (see data/README)
151     itemFetch = mStore->fetchItems(collection);
152     QVERIFY(itemFetch->exec());
153     QCOMPARE(itemFetch->error(), 0);
154 
155     items = itemFetch->items();
156     QCOMPARE((int)items.count(), 4);
157     for (const Item &item : std::as_const(items)) {
158         const auto flags = item.flags();
159         for (const QByteArray &flag : flags) {
160             ++flagCounts[flag];
161         }
162     }
163 
164     QCOMPARE(flagCounts["\\SEEN"], 2);
165     QCOMPARE(flagCounts["\\FLAGGED"], 1);
166     QCOMPARE(flagCounts["$TODO"], 1);
167     flagCounts.clear();
168 
169     // test moving mbox from maildir parent
170     Collection collection1_2;
171     collection1_2.setName(QStringLiteral("collection1_2"));
172     collection1_2.setRemoteId(QStringLiteral("collection1_2"));
173     collection1_2.setParentCollection(collection1);
174 
175     job = mStore->moveCollection(collection1_2, mStore->topLevelCollection());
176 
177     QVERIFY(job->exec());
178     QCOMPARE(job->error(), 0);
179 
180     collection = job->collection();
181     QCOMPARE(collection.remoteId(), collection1_2.remoteId());
182     QCOMPARE(collection.parentCollection(), mStore->topLevelCollection());
183 
184     fileInfo1_2.refresh();
185     QVERIFY(!fileInfo1_2.exists());
186     fileInfo1_2 = QFileInfo(topDir.path(), collection.remoteId());
187     QVERIFY(fileInfo1_2.exists());
188 
189     // check for index preservation
190     var = job->property("onDiskIndexInvalidated");
191     QVERIFY(var.isValid());
192     QCOMPARE(var.userType(), colListVar.userType());
193 
194     collections = var.value<Collection::List>();
195     QCOMPARE((int)collections.count(), 1);
196     QCOMPARE(collections.first(), collection);
197 
198     // get the items and check the flags (see data/README)
199     itemFetch = mStore->fetchItems(collection);
200     QVERIFY(itemFetch->exec());
201     QCOMPARE(itemFetch->error(), 0);
202 
203     items = itemFetch->items();
204     QCOMPARE((int)items.count(), 4);
205     for (const Item &item : std::as_const(items)) {
206         const auto flags = item.flags();
207         for (const QByteArray &flag : flags) {
208             ++flagCounts[flag];
209         }
210     }
211 
212     QCOMPARE(flagCounts["\\SEEN"], 2);
213     QCOMPARE(flagCounts["\\FLAGGED"], 1);
214     QCOMPARE(flagCounts["$TODO"], 1);
215     flagCounts.clear();
216 
217     // test moving mbox from mbox parent
218     Collection collection2;
219     collection2.setName(QStringLiteral("collection2"));
220     collection2.setRemoteId(QStringLiteral("collection2"));
221     collection2.setParentCollection(mStore->topLevelCollection());
222 
223     Collection collection2_1;
224     collection2_1.setName(QStringLiteral("collection2_1"));
225     collection2_1.setRemoteId(QStringLiteral("collection2_1"));
226     collection2_1.setParentCollection(collection2);
227 
228     job = mStore->moveCollection(collection2_1, mStore->topLevelCollection());
229 
230     QVERIFY(job->exec());
231     QCOMPARE(job->error(), 0);
232 
233     collection = job->collection();
234     QCOMPARE(collection.remoteId(), collection2_1.remoteId());
235     QCOMPARE(collection.parentCollection(), mStore->topLevelCollection());
236 
237     fileInfo2_1.refresh();
238     QVERIFY(!fileInfo2_1.exists());
239     fileInfo2_1 = QFileInfo(topDir.path(), collection.remoteId());
240     QVERIFY(fileInfo2_1.exists());
241 
242     // check for index preservation
243     var = job->property("onDiskIndexInvalidated");
244     QVERIFY(var.isValid());
245     QCOMPARE(var.userType(), colListVar.userType());
246 
247     collections = var.value<Collection::List>();
248     QCOMPARE((int)collections.count(), 1);
249     QCOMPARE(collections.first(), collection);
250 
251     // get the items and check the flags (see data/README)
252     itemFetch = mStore->fetchItems(collection);
253     QVERIFY(itemFetch->exec());
254     QCOMPARE(itemFetch->error(), 0);
255 
256     items = itemFetch->items();
257     QCOMPARE((int)items.count(), 4);
258     for (const Item &item : std::as_const(items)) {
259         const auto flags = item.flags();
260         for (const QByteArray &flag : flags) {
261             ++flagCounts[flag];
262         }
263     }
264 
265     QCOMPARE(flagCounts["\\SEEN"], 2);
266     QCOMPARE(flagCounts["\\FLAGGED"], 1);
267     QCOMPARE(flagCounts["$TODO"], 1);
268     flagCounts.clear();
269 
270     // test moving maildir from mbox parent
271     Collection collection2_2;
272     collection2_2.setName(QStringLiteral("collection2_2"));
273     collection2_2.setRemoteId(QStringLiteral("collection2_2"));
274     collection2_2.setParentCollection(collection2);
275 
276     job = mStore->moveCollection(collection2_2, mStore->topLevelCollection());
277 
278     QVERIFY(job->exec());
279     QCOMPARE(job->error(), 0);
280 
281     collection = job->collection();
282     QCOMPARE(collection.remoteId(), collection2_2.remoteId());
283     QCOMPARE(collection.parentCollection(), mStore->topLevelCollection());
284 
285     fileInfo2_2.refresh();
286     QVERIFY(!fileInfo2_2.exists());
287     fileInfo2_2 = QFileInfo(topDir.path(), collection.remoteId());
288     QVERIFY(fileInfo2_2.exists());
289 
290     // check for index preservation
291     var = job->property("onDiskIndexInvalidated");
292     QVERIFY(var.isValid());
293     QCOMPARE(var.userType(), colListVar.userType());
294 
295     collections = var.value<Collection::List>();
296     QCOMPARE((int)collections.count(), 1);
297     QCOMPARE(collections.first(), collection);
298 
299     // get the items and check the flags (see data/README)
300     itemFetch = mStore->fetchItems(collection);
301     QVERIFY(itemFetch->exec());
302     QCOMPARE(itemFetch->error(), 0);
303 
304     items = itemFetch->items();
305     QCOMPARE((int)items.count(), 4);
306     for (const Item &item : std::as_const(items)) {
307         const auto flags = item.flags();
308         for (const QByteArray &flag : flags) {
309             ++flagCounts[flag];
310         }
311     }
312 
313     QCOMPARE(flagCounts["\\SEEN"], 2);
314     QCOMPARE(flagCounts["\\FLAGGED"], 1);
315     QCOMPARE(flagCounts["$TODO"], 1);
316     flagCounts.clear();
317 }
318 
testMoveToMaildir()319 void CollectionMoveTest::testMoveToMaildir()
320 {
321     QDir topDir(mDir->path());
322 
323     // top level dir
324     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), topDir.path(), QStringLiteral("collection1")));
325     QFileInfo fileInfo1(topDir, QStringLiteral("collection1"));
326     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), topDir.path(), QStringLiteral("collection2")));
327     QFileInfo fileInfo2(topDir, QStringLiteral("collection2"));
328     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), topDir.path(), QStringLiteral("collection3")));
329     QFileInfo fileInfo3(topDir, QStringLiteral("collection3"));
330     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), topDir.path(), QStringLiteral("collection4")));
331     QFileInfo fileInfo4(topDir, QStringLiteral("collection4"));
332 
333     // first level maildir parent
334     QDir subDir1 = topDir;
335     QVERIFY(subDir1.mkdir(QStringLiteral(".collection1.directory")));
336     QVERIFY(subDir1.cd(QStringLiteral(".collection1.directory")));
337     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), subDir1.path(), QStringLiteral("collection1_1")));
338     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), subDir1.path(), QStringLiteral("collection1_2")));
339 
340     // first level mbox parent
341     QDir subDir4 = topDir;
342     QVERIFY(subDir4.mkdir(QStringLiteral(".collection4.directory")));
343     QVERIFY(subDir4.cd(QStringLiteral(".collection4.directory")));
344     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), subDir4.path(), QStringLiteral("collection4_1")));
345     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), subDir4.path(), QStringLiteral("collection4_2")));
346     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), subDir4.path(), QStringLiteral("collection4_3")));
347     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), subDir4.path(), QStringLiteral("collection4_4")));
348 
349     // target maildir
350     KPIM::Maildir topLevelMd(topDir.path(), true);
351     KPIM::Maildir targetMd(topLevelMd.addSubFolder(QStringLiteral("target")), false);
352     QVERIFY(targetMd.isValid());
353     QDir subDirTarget;
354 
355     mStore->setPath(topDir.path());
356 
357     // common variables
358     FileStore::CollectionMoveJob *job = nullptr;
359     FileStore::ItemFetchJob *itemFetch = nullptr;
360     Collection collection;
361     const QVariant colListVar = QVariant::fromValue<Collection::List>(Collection::List());
362     QVariant var;
363     Collection::List collections;
364     Item::List items;
365     QMap<QByteArray, int> flagCounts;
366 
367     Collection target;
368     target.setName(QStringLiteral("target"));
369     target.setRemoteId(QStringLiteral("target"));
370     target.setParentCollection(mStore->topLevelCollection());
371 
372     // test move leaf maildir into sibling
373     Collection collection2;
374     collection2.setName(QStringLiteral("collection2"));
375     collection2.setRemoteId(QStringLiteral("collection2"));
376     collection2.setParentCollection(mStore->topLevelCollection());
377 
378     job = mStore->moveCollection(collection2, target);
379 
380     QVERIFY(job->exec());
381     QCOMPARE(job->error(), 0);
382 
383     subDirTarget = topDir;
384     QVERIFY(subDirTarget.cd(QStringLiteral(".target.directory")));
385 
386     collection = job->collection();
387     QCOMPARE(collection.remoteId(), collection2.remoteId());
388     QCOMPARE(collection.parentCollection(), target);
389 
390     fileInfo2.refresh();
391     QVERIFY(!fileInfo2.exists());
392     fileInfo2 = QFileInfo(subDirTarget, collection.remoteId());
393     QVERIFY(fileInfo2.exists());
394 
395     // check for index preservation
396     var = job->property("onDiskIndexInvalidated");
397     QVERIFY(var.isValid());
398     QCOMPARE(var.userType(), colListVar.userType());
399 
400     collections = var.value<Collection::List>();
401     QCOMPARE((int)collections.count(), 1);
402     QCOMPARE(collections.first(), collection);
403 
404     // get the items and check the flags (see data/README)
405     itemFetch = mStore->fetchItems(collection);
406     QVERIFY(itemFetch->exec());
407     QCOMPARE(itemFetch->error(), 0);
408 
409     items = itemFetch->items();
410     QCOMPARE((int)items.count(), 4);
411     for (const Item &item : std::as_const(items)) {
412         const auto flags{item.flags()};
413         for (const QByteArray &flag : flags) {
414             ++flagCounts[flag];
415         }
416     }
417 
418     QCOMPARE(flagCounts["\\SEEN"], 2);
419     QCOMPARE(flagCounts["\\FLAGGED"], 1);
420     QCOMPARE(flagCounts["$TODO"], 1);
421     flagCounts.clear();
422 
423     // test move leaf mbox into sibling
424     Collection collection3;
425     collection3.setName(QStringLiteral("collection3"));
426     collection3.setRemoteId(QStringLiteral("collection3"));
427     collection3.setParentCollection(mStore->topLevelCollection());
428 
429     job = mStore->moveCollection(collection3, target);
430 
431     QVERIFY(job->exec());
432     QCOMPARE(job->error(), 0);
433 
434     collection = job->collection();
435     QCOMPARE(collection.remoteId(), collection3.remoteId());
436     QCOMPARE(collection.parentCollection(), target);
437 
438     fileInfo3.refresh();
439     QVERIFY(!fileInfo3.exists());
440     fileInfo3 = QFileInfo(subDirTarget, collection.remoteId());
441     QVERIFY(fileInfo3.exists());
442 
443     // check for index preservation
444     var = job->property("onDiskIndexInvalidated");
445     QVERIFY(var.isValid());
446     QCOMPARE(var.userType(), colListVar.userType());
447 
448     collections = var.value<Collection::List>();
449     QCOMPARE((int)collections.count(), 1);
450     QCOMPARE(collections.first(), collection);
451 
452     // get the items and check the flags (see data/README)
453     itemFetch = mStore->fetchItems(collection);
454     QVERIFY(itemFetch->exec());
455     QCOMPARE(itemFetch->error(), 0);
456 
457     items = itemFetch->items();
458     QCOMPARE((int)items.count(), 4);
459     for (const Item &item : std::as_const(items)) {
460         const auto flags = item.flags();
461         for (const QByteArray &flag : flags) {
462             ++flagCounts[flag];
463         }
464     }
465 
466     QCOMPARE(flagCounts["\\SEEN"], 2);
467     QCOMPARE(flagCounts["\\FLAGGED"], 1);
468     QCOMPARE(flagCounts["$TODO"], 1);
469     flagCounts.clear();
470 
471     // test move maildir with subtree into sibling
472     Collection collection1;
473     collection1.setName(QStringLiteral("collection1"));
474     collection1.setRemoteId(QStringLiteral("collection1"));
475     collection1.setParentCollection(mStore->topLevelCollection());
476 
477     // load sub collection index data to check for correct cache updates
478     Collection collection1_1;
479     collection1_1.setName(QStringLiteral("collection1_1"));
480     collection1_1.setRemoteId(QStringLiteral("collection1_1"));
481     collection1_1.setParentCollection(collection1);
482     itemFetch = mStore->fetchItems(collection1_1);
483     QVERIFY(itemFetch->exec());
484 
485     Collection collection1_2;
486     collection1_2.setName(QStringLiteral("collection1_2"));
487     collection1_2.setRemoteId(QStringLiteral("collection1_2"));
488     collection1_2.setParentCollection(collection1);
489     itemFetch = mStore->fetchItems(collection1_2);
490     QVERIFY(itemFetch->exec());
491 
492     job = mStore->moveCollection(collection1, target);
493 
494     QVERIFY(job->exec());
495     QCOMPARE(job->error(), 0);
496 
497     collection = job->collection();
498     QCOMPARE(collection.remoteId(), collection1.remoteId());
499     QCOMPARE(collection.parentCollection(), target);
500 
501     fileInfo1.refresh();
502     QVERIFY(!fileInfo1.exists());
503     fileInfo1 = QFileInfo(subDirTarget, collection.remoteId());
504     QVERIFY(fileInfo1.exists());
505     QVERIFY(!subDir1.exists());
506     subDir1 = subDirTarget;
507     QVERIFY(subDir1.cd(QStringLiteral(".collection1.directory")));
508     QCOMPARE(subDir1.entryList(QStringList() << QStringLiteral("collection*")),
509              QStringList() << QStringLiteral("collection1_1") << QStringLiteral("collection1_2"));
510 
511     // check for index preservation
512     var = job->property("onDiskIndexInvalidated");
513     QVERIFY(var.isValid());
514     QCOMPARE(var.userType(), colListVar.userType());
515 
516     collections = var.value<Collection::List>();
517     QCOMPARE((int)collections.count(), 1);
518     QCOMPARE(collections.first(), collection);
519 
520     // get the items and check the flags (see data/README)
521     itemFetch = mStore->fetchItems(collection);
522     QVERIFY(itemFetch->exec());
523     QCOMPARE(itemFetch->error(), 0);
524 
525     items = itemFetch->items();
526     QCOMPARE((int)items.count(), 4);
527     for (const Item &item : std::as_const(items)) {
528         const auto flags{item.flags()};
529         for (const QByteArray &flag : flags) {
530             ++flagCounts[flag];
531         }
532     }
533 
534     QCOMPARE(flagCounts["\\SEEN"], 2);
535     QCOMPARE(flagCounts["\\FLAGGED"], 1);
536     QCOMPARE(flagCounts["$TODO"], 1);
537     flagCounts.clear();
538 
539     // check for children cache path updates
540     collection1.setParentCollection(target);
541     collection1_1.setParentCollection(collection1);
542     collection1_2.setParentCollection(collection1);
543 
544     itemFetch = mStore->fetchItems(collection1_1);
545     QVERIFY(itemFetch->exec());
546     QCOMPARE(itemFetch->error(), 0);
547 
548     items = itemFetch->items();
549     QCOMPARE((int)items.count(), 4);
550     for (const Item &item : std::as_const(items)) {
551         const auto flags = item.flags();
552         for (const QByteArray &flag : flags) {
553             ++flagCounts[flag];
554         }
555     }
556 
557     QCOMPARE(flagCounts["\\SEEN"], 2);
558     QCOMPARE(flagCounts["\\FLAGGED"], 1);
559     QCOMPARE(flagCounts["$TODO"], 1);
560     flagCounts.clear();
561 
562     itemFetch = mStore->fetchItems(collection1_2);
563     QVERIFY(itemFetch->exec());
564     QCOMPARE(itemFetch->error(), 0);
565 
566     items = itemFetch->items();
567     QCOMPARE((int)items.count(), 4);
568     for (const Item &item : std::as_const(items)) {
569         const auto flags{item.flags()};
570         for (const QByteArray &flag : flags) {
571             ++flagCounts[flag];
572         }
573     }
574 
575     QCOMPARE(flagCounts["\\SEEN"], 2);
576     QCOMPARE(flagCounts["\\FLAGGED"], 1);
577     QCOMPARE(flagCounts["$TODO"], 1);
578     flagCounts.clear();
579 
580     // test move mbox with subtree into sibling
581     Collection collection4;
582     collection4.setName(QStringLiteral("collection4"));
583     collection4.setRemoteId(QStringLiteral("collection4"));
584     collection4.setParentCollection(mStore->topLevelCollection());
585 
586     // load sub collection index data to check for correct cache updates
587     Collection collection4_1;
588     collection4_1.setName(QStringLiteral("collection4_1"));
589     collection4_1.setRemoteId(QStringLiteral("collection4_1"));
590     collection4_1.setParentCollection(collection4);
591     itemFetch = mStore->fetchItems(collection4_1);
592     QVERIFY(itemFetch->exec());
593 
594     Collection collection4_2;
595     collection4_2.setName(QStringLiteral("collection4_2"));
596     collection4_2.setRemoteId(QStringLiteral("collection4_2"));
597     collection4_2.setParentCollection(collection4);
598     itemFetch = mStore->fetchItems(collection4_2);
599     QVERIFY(itemFetch->exec());
600 
601     job = mStore->moveCollection(collection4, target);
602 
603     QVERIFY(job->exec());
604     QCOMPARE(job->error(), 0);
605 
606     collection = job->collection();
607     QCOMPARE(collection.remoteId(), collection4.remoteId());
608     QCOMPARE(collection.parentCollection(), target);
609 
610     fileInfo4.refresh();
611     QVERIFY(!fileInfo4.exists());
612     fileInfo4 = QFileInfo(subDirTarget, collection.remoteId());
613     QVERIFY(fileInfo4.exists());
614     QVERIFY(!subDir4.exists());
615     subDir4 = subDirTarget;
616     QVERIFY(subDir4.cd(QStringLiteral(".collection4.directory")));
617     QCOMPARE(subDir4.entryList(QStringList() << QStringLiteral("collection*")),
618              QStringList() << QStringLiteral("collection4_1") << QStringLiteral("collection4_2") << QStringLiteral("collection4_3")
619                            << QStringLiteral("collection4_4"));
620 
621     // check for index preservation
622     var = job->property("onDiskIndexInvalidated");
623     QVERIFY(var.isValid());
624     QCOMPARE(var.userType(), colListVar.userType());
625 
626     collections = var.value<Collection::List>();
627     QCOMPARE((int)collections.count(), 1);
628     QCOMPARE(collections.first(), collection);
629 
630     // get the items and check the flags (see data/README)
631     itemFetch = mStore->fetchItems(collection);
632     QVERIFY(itemFetch->exec());
633     QCOMPARE(itemFetch->error(), 0);
634 
635     items = itemFetch->items();
636     QCOMPARE((int)items.count(), 4);
637     for (const Item &item : std::as_const(items)) {
638         const auto flags = item.flags();
639         for (const QByteArray &flag : flags) {
640             ++flagCounts[flag];
641         }
642     }
643 
644     QCOMPARE(flagCounts["\\SEEN"], 2);
645     QCOMPARE(flagCounts["\\FLAGGED"], 1);
646     QCOMPARE(flagCounts["$TODO"], 1);
647     flagCounts.clear();
648 
649     // check for children cache path updates
650     collection4.setParentCollection(target);
651     collection4_1.setParentCollection(collection4);
652     collection4_2.setParentCollection(collection4);
653 
654     itemFetch = mStore->fetchItems(collection4_1);
655     QVERIFY(itemFetch->exec());
656     QCOMPARE(itemFetch->error(), 0);
657 
658     items = itemFetch->items();
659     QCOMPARE((int)items.count(), 4);
660     for (const Item &item : std::as_const(items)) {
661         const auto flags{item.flags()};
662         for (const QByteArray &flag : flags) {
663             ++flagCounts[flag];
664         }
665     }
666 
667     QCOMPARE(flagCounts["\\SEEN"], 2);
668     QCOMPARE(flagCounts["\\FLAGGED"], 1);
669     QCOMPARE(flagCounts["$TODO"], 1);
670     flagCounts.clear();
671 
672     itemFetch = mStore->fetchItems(collection4_2);
673     QVERIFY(itemFetch->exec());
674     QCOMPARE(itemFetch->error(), 0);
675 
676     items = itemFetch->items();
677     QCOMPARE((int)items.count(), 4);
678     for (const Item &item : std::as_const(items)) {
679         const auto flags = item.flags();
680         for (const QByteArray &flag : flags) {
681             ++flagCounts[flag];
682         }
683     }
684 
685     QCOMPARE(flagCounts["\\SEEN"], 2);
686     QCOMPARE(flagCounts["\\FLAGGED"], 1);
687     QCOMPARE(flagCounts["$TODO"], 1);
688     flagCounts.clear();
689 
690     // move from parent maildir to parent's sibling
691     collection2.setParentCollection(target);
692 
693     job = mStore->moveCollection(collection1_1, collection2);
694 
695     QVERIFY(job->exec());
696     QCOMPARE(job->error(), 0);
697 
698     QDir subDir2 = subDirTarget;
699     QVERIFY(subDir2.cd(QStringLiteral(".collection2.directory")));
700 
701     collection = job->collection();
702     QCOMPARE(collection.remoteId(), collection1_1.remoteId());
703     QCOMPARE(collection.parentCollection(), collection2);
704 
705     QFileInfo fileInfo1_1(subDir1, collection.remoteId());
706     QVERIFY(!fileInfo1_1.exists());
707     fileInfo1_1 = QFileInfo(subDir2, collection.remoteId());
708     QVERIFY(fileInfo1_1.exists());
709 
710     // check for index preservation
711     var = job->property("onDiskIndexInvalidated");
712     QVERIFY(var.isValid());
713     QCOMPARE(var.userType(), colListVar.userType());
714 
715     collections = var.value<Collection::List>();
716     QCOMPARE((int)collections.count(), 1);
717     QCOMPARE(collections.first(), collection);
718 
719     // get the items and check the flags (see data/README)
720     itemFetch = mStore->fetchItems(collection);
721     QVERIFY(itemFetch->exec());
722     QCOMPARE(itemFetch->error(), 0);
723 
724     items = itemFetch->items();
725     QCOMPARE((int)items.count(), 4);
726     for (const Item &item : std::as_const(items)) {
727         const auto flags = item.flags();
728         for (const QByteArray &flag : flags) {
729             ++flagCounts[flag];
730         }
731     }
732 
733     QCOMPARE(flagCounts["\\SEEN"], 2);
734     QCOMPARE(flagCounts["\\FLAGGED"], 1);
735     QCOMPARE(flagCounts["$TODO"], 1);
736     flagCounts.clear();
737 
738     // move from parent maildir to parent's sibling
739     job = mStore->moveCollection(collection1_2, collection2);
740 
741     QVERIFY(job->exec());
742     QCOMPARE(job->error(), 0);
743 
744     collection = job->collection();
745     QCOMPARE(collection.remoteId(), collection1_2.remoteId());
746     QCOMPARE(collection.parentCollection(), collection2);
747 
748     QFileInfo fileInfo1_2(subDir1, collection.remoteId());
749     QVERIFY(!fileInfo1_2.exists());
750     fileInfo1_2 = QFileInfo(subDir2, collection.remoteId());
751     QVERIFY(fileInfo1_2.exists());
752 
753     // check for index preservation
754     var = job->property("onDiskIndexInvalidated");
755     QVERIFY(var.isValid());
756     QCOMPARE(var.userType(), colListVar.userType());
757 
758     collections = var.value<Collection::List>();
759     QCOMPARE((int)collections.count(), 1);
760     QCOMPARE(collections.first(), collection);
761 
762     // get the items and check the flags (see data/README)
763     itemFetch = mStore->fetchItems(collection);
764     QVERIFY(itemFetch->exec());
765     QCOMPARE(itemFetch->error(), 0);
766 
767     items = itemFetch->items();
768     QCOMPARE((int)items.count(), 4);
769     for (const Item &item : std::as_const(items)) {
770         const auto flags = item.flags();
771         for (const QByteArray &flag : flags) {
772             ++flagCounts[flag];
773         }
774     }
775 
776     QCOMPARE(flagCounts["\\SEEN"], 2);
777     QCOMPARE(flagCounts["\\FLAGGED"], 1);
778     QCOMPARE(flagCounts["$TODO"], 1);
779     flagCounts.clear();
780 
781     // move from parent mbox to parent's sibling
782     job = mStore->moveCollection(collection4_1, collection2);
783 
784     QVERIFY(job->exec());
785     QCOMPARE(job->error(), 0);
786 
787     collection = job->collection();
788     QCOMPARE(collection.remoteId(), collection4_1.remoteId());
789     QCOMPARE(collection.parentCollection(), collection2);
790 
791     QFileInfo fileInfo4_1(subDir4, collection.remoteId());
792     QVERIFY(!fileInfo4_1.exists());
793     fileInfo4_1 = QFileInfo(subDir2, collection.remoteId());
794     QVERIFY(fileInfo4_1.exists());
795 
796     // check for index preservation
797     var = job->property("onDiskIndexInvalidated");
798     QVERIFY(var.isValid());
799     QCOMPARE(var.userType(), colListVar.userType());
800 
801     collections = var.value<Collection::List>();
802     QCOMPARE((int)collections.count(), 1);
803     QCOMPARE(collections.first(), collection);
804 
805     // get the items and check the flags (see data/README)
806     itemFetch = mStore->fetchItems(collection);
807     QVERIFY(itemFetch->exec());
808     QCOMPARE(itemFetch->error(), 0);
809 
810     items = itemFetch->items();
811     QCOMPARE((int)items.count(), 4);
812     for (const Item &item : std::as_const(items)) {
813         const auto flags = item.flags();
814         for (const QByteArray &flag : flags) {
815             ++flagCounts[flag];
816         }
817     }
818 
819     QCOMPARE(flagCounts["\\SEEN"], 2);
820     QCOMPARE(flagCounts["\\FLAGGED"], 1);
821     QCOMPARE(flagCounts["$TODO"], 1);
822     flagCounts.clear();
823 
824     // move from parent mbox to parent's sibling
825     job = mStore->moveCollection(collection4_2, collection2);
826 
827     QVERIFY(job->exec());
828     QCOMPARE(job->error(), 0);
829 
830     collection = job->collection();
831     QCOMPARE(collection.remoteId(), collection4_2.remoteId());
832     QCOMPARE(collection.parentCollection(), collection2);
833 
834     QFileInfo fileInfo4_2(subDir4, collection.remoteId());
835     QVERIFY(!fileInfo4_2.exists());
836     fileInfo4_2 = QFileInfo(subDir2, collection.remoteId());
837     QVERIFY(fileInfo4_2.exists());
838 
839     // check for index preservation
840     var = job->property("onDiskIndexInvalidated");
841     QVERIFY(var.isValid());
842     QCOMPARE(var.userType(), colListVar.userType());
843 
844     collections = var.value<Collection::List>();
845     QCOMPARE((int)collections.count(), 1);
846     QCOMPARE(collections.first(), collection);
847 
848     // get the items and check the flags (see data/README)
849     itemFetch = mStore->fetchItems(collection);
850     QVERIFY(itemFetch->exec());
851     QCOMPARE(itemFetch->error(), 0);
852 
853     items = itemFetch->items();
854     QCOMPARE((int)items.count(), 4);
855     for (const Item &item : std::as_const(items)) {
856         const auto flags = item.flags();
857         for (const QByteArray &flag : flags) {
858             ++flagCounts[flag];
859         }
860     }
861 
862     QCOMPARE(flagCounts["\\SEEN"], 2);
863     QCOMPARE(flagCounts["\\FLAGGED"], 1);
864     QCOMPARE(flagCounts["$TODO"], 1);
865     flagCounts.clear();
866 
867     // move from parent maildir to grandparent
868     collection1_1.setParentCollection(collection2);
869 
870     job = mStore->moveCollection(collection1_1, target);
871 
872     QVERIFY(job->exec());
873     QCOMPARE(job->error(), 0);
874 
875     collection = job->collection();
876     QCOMPARE(collection.remoteId(), collection1_1.remoteId());
877     QCOMPARE(collection.parentCollection(), target);
878 
879     fileInfo1_1.refresh();
880     QVERIFY(!fileInfo1_1.exists());
881     fileInfo1_1 = QFileInfo(subDirTarget, collection.remoteId());
882     QVERIFY(fileInfo1_1.exists());
883 
884     // check for index preservation
885     var = job->property("onDiskIndexInvalidated");
886     QVERIFY(var.isValid());
887     QCOMPARE(var.userType(), colListVar.userType());
888 
889     collections = var.value<Collection::List>();
890     QCOMPARE((int)collections.count(), 1);
891     QCOMPARE(collections.first(), collection);
892 
893     // get the items and check the flags (see data/README)
894     itemFetch = mStore->fetchItems(collection);
895     QVERIFY(itemFetch->exec());
896     QCOMPARE(itemFetch->error(), 0);
897 
898     items = itemFetch->items();
899     QCOMPARE((int)items.count(), 4);
900     for (const Item &item : std::as_const(items)) {
901         const auto flags{item.flags()};
902         for (const QByteArray &flag : flags) {
903             ++flagCounts[flag];
904         }
905     }
906 
907     QCOMPARE(flagCounts["\\SEEN"], 2);
908     QCOMPARE(flagCounts["\\FLAGGED"], 1);
909     QCOMPARE(flagCounts["$TODO"], 1);
910     flagCounts.clear();
911 
912     // move from parent maildir to grandparent
913     collection1_2.setParentCollection(collection2);
914     job = mStore->moveCollection(collection1_2, target);
915 
916     QVERIFY(job->exec());
917     QCOMPARE(job->error(), 0);
918 
919     collection = job->collection();
920     QCOMPARE(collection.remoteId(), collection1_2.remoteId());
921     QCOMPARE(collection.parentCollection(), target);
922 
923     fileInfo1_2.refresh();
924     QVERIFY(!fileInfo1_2.exists());
925     fileInfo1_2 = QFileInfo(subDirTarget, collection.remoteId());
926     QVERIFY(fileInfo1_2.exists());
927 
928     // check for index preservation
929     var = job->property("onDiskIndexInvalidated");
930     QVERIFY(var.isValid());
931     QCOMPARE(var.userType(), colListVar.userType());
932 
933     collections = var.value<Collection::List>();
934     QCOMPARE((int)collections.count(), 1);
935     QCOMPARE(collections.first(), collection);
936 
937     // get the items and check the flags (see data/README)
938     itemFetch = mStore->fetchItems(collection);
939     QVERIFY(itemFetch->exec());
940     QCOMPARE(itemFetch->error(), 0);
941 
942     items = itemFetch->items();
943     QCOMPARE((int)items.count(), 4);
944     for (const Item &item : std::as_const(items)) {
945         const auto flags{item.flags()};
946         for (const QByteArray &flag : flags) {
947             ++flagCounts[flag];
948         }
949     }
950 
951     QCOMPARE(flagCounts["\\SEEN"], 2);
952     QCOMPARE(flagCounts["\\FLAGGED"], 1);
953     QCOMPARE(flagCounts["$TODO"], 1);
954     flagCounts.clear();
955 
956     // move from parent mbox to grandparent
957     Collection collection4_3;
958     collection4_3.setName(QStringLiteral("collection4_3"));
959     collection4_3.setRemoteId(QStringLiteral("collection4_3"));
960     collection4_3.setParentCollection(collection4);
961 
962     job = mStore->moveCollection(collection4_3, target);
963 
964     QVERIFY(job->exec());
965     QCOMPARE(job->error(), 0);
966 
967     collection = job->collection();
968     QCOMPARE(collection.remoteId(), collection4_3.remoteId());
969     QCOMPARE(collection.parentCollection(), target);
970 
971     QFileInfo fileInfo4_3(subDir4, collection.remoteId());
972     QVERIFY(!fileInfo4_3.exists());
973     fileInfo4_3 = QFileInfo(subDirTarget, collection.remoteId());
974     QVERIFY(fileInfo4_3.exists());
975 
976     // check for index preservation
977     var = job->property("onDiskIndexInvalidated");
978     QVERIFY(var.isValid());
979     QCOMPARE(var.userType(), colListVar.userType());
980 
981     collections = var.value<Collection::List>();
982     QCOMPARE((int)collections.count(), 1);
983     QCOMPARE(collections.first(), collection);
984 
985     // get the items and check the flags (see data/README)
986     itemFetch = mStore->fetchItems(collection);
987     QVERIFY(itemFetch->exec());
988     QCOMPARE(itemFetch->error(), 0);
989 
990     items = itemFetch->items();
991     QCOMPARE((int)items.count(), 4);
992     for (const Item &item : std::as_const(items)) {
993         const auto flags{item.flags()};
994         for (const QByteArray &flag : flags) {
995             ++flagCounts[flag];
996         }
997     }
998 
999     QCOMPARE(flagCounts["\\SEEN"], 2);
1000     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1001     QCOMPARE(flagCounts["$TODO"], 1);
1002     flagCounts.clear();
1003 
1004     // move from parent mbox to grandparent
1005     Collection collection4_4;
1006     collection4_4.setName(QStringLiteral("collection4_4"));
1007     collection4_4.setRemoteId(QStringLiteral("collection4_4"));
1008     collection4_4.setParentCollection(collection4);
1009 
1010     job = mStore->moveCollection(collection4_4, target);
1011 
1012     QVERIFY(job->exec());
1013     QCOMPARE(job->error(), 0);
1014 
1015     collection = job->collection();
1016     QCOMPARE(collection.remoteId(), collection4_4.remoteId());
1017     QCOMPARE(collection.parentCollection(), target);
1018 
1019     QFileInfo fileInfo4_4(subDir4, collection.remoteId());
1020     QVERIFY(!fileInfo4_4.exists());
1021     fileInfo4_4 = QFileInfo(subDirTarget, collection.remoteId());
1022     QVERIFY(fileInfo4_4.exists());
1023 
1024     // check for index preservation
1025     var = job->property("onDiskIndexInvalidated");
1026     QVERIFY(var.isValid());
1027     QCOMPARE(var.userType(), colListVar.userType());
1028 
1029     collections = var.value<Collection::List>();
1030     QCOMPARE((int)collections.count(), 1);
1031     QCOMPARE(collections.first(), collection);
1032 
1033     // get the items and check the flags (see data/README)
1034     itemFetch = mStore->fetchItems(collection);
1035     QVERIFY(itemFetch->exec());
1036     QCOMPARE(itemFetch->error(), 0);
1037 
1038     items = itemFetch->items();
1039     QCOMPARE((int)items.count(), 4);
1040     for (const Item &item : std::as_const(items)) {
1041         const auto flags{item.flags()};
1042         for (const QByteArray &flag : flags) {
1043             ++flagCounts[flag];
1044         }
1045     }
1046 
1047     QCOMPARE(flagCounts["\\SEEN"], 2);
1048     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1049     QCOMPARE(flagCounts["$TODO"], 1);
1050     flagCounts.clear();
1051 
1052     // move from maildir to grandchild
1053     collection1_1.setParentCollection(target);
1054 
1055     job = mStore->moveCollection(collection1_1, collection2);
1056 
1057     QVERIFY(job->exec());
1058     QCOMPARE(job->error(), 0);
1059 
1060     collection = job->collection();
1061     QCOMPARE(collection.remoteId(), collection1_1.remoteId());
1062     QCOMPARE(collection.parentCollection(), collection2);
1063 
1064     fileInfo1_1.refresh();
1065     QVERIFY(!fileInfo1_1.exists());
1066     fileInfo1_1 = QFileInfo(subDir2, collection.remoteId());
1067     QVERIFY(fileInfo1_1.exists());
1068 
1069     // check for index preservation
1070     var = job->property("onDiskIndexInvalidated");
1071     QVERIFY(var.isValid());
1072     QCOMPARE(var.userType(), colListVar.userType());
1073 
1074     collections = var.value<Collection::List>();
1075     QCOMPARE((int)collections.count(), 1);
1076     QCOMPARE(collections.first(), collection);
1077 
1078     // get the items and check the flags (see data/README)
1079     itemFetch = mStore->fetchItems(collection);
1080     QVERIFY(itemFetch->exec());
1081     QCOMPARE(itemFetch->error(), 0);
1082 
1083     items = itemFetch->items();
1084     QCOMPARE((int)items.count(), 4);
1085     for (const Item &item : std::as_const(items)) {
1086         const auto flags{item.flags()};
1087         for (const QByteArray &flag : flags) {
1088             ++flagCounts[flag];
1089         }
1090     }
1091 
1092     QCOMPARE(flagCounts["\\SEEN"], 2);
1093     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1094     QCOMPARE(flagCounts["$TODO"], 1);
1095     flagCounts.clear();
1096 
1097     // move from maildir to grandchild
1098     collection1_2.setParentCollection(target);
1099 
1100     job = mStore->moveCollection(collection1_2, collection2);
1101 
1102     QVERIFY(job->exec());
1103     QCOMPARE(job->error(), 0);
1104 
1105     collection = job->collection();
1106     QCOMPARE(collection.remoteId(), collection1_2.remoteId());
1107     QCOMPARE(collection.parentCollection(), collection2);
1108 
1109     fileInfo1_2.refresh();
1110     QVERIFY(!fileInfo1_2.exists());
1111     fileInfo1_2 = QFileInfo(subDir2, collection.remoteId());
1112     QVERIFY(fileInfo1_2.exists());
1113 
1114     // check for index preservation
1115     var = job->property("onDiskIndexInvalidated");
1116     QVERIFY(var.isValid());
1117     QCOMPARE(var.userType(), colListVar.userType());
1118 
1119     collections = var.value<Collection::List>();
1120     QCOMPARE((int)collections.count(), 1);
1121     QCOMPARE(collections.first(), collection);
1122 
1123     // get the items and check the flags (see data/README)
1124     itemFetch = mStore->fetchItems(collection);
1125     QVERIFY(itemFetch->exec());
1126     QCOMPARE(itemFetch->error(), 0);
1127 
1128     items = itemFetch->items();
1129     QCOMPARE((int)items.count(), 4);
1130     for (const Item &item : std::as_const(items)) {
1131         const auto flags{item.flags()};
1132         for (const QByteArray &flag : flags) {
1133             ++flagCounts[flag];
1134         }
1135     }
1136 
1137     QCOMPARE(flagCounts["\\SEEN"], 2);
1138     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1139     QCOMPARE(flagCounts["$TODO"], 1);
1140     flagCounts.clear();
1141 }
1142 
testMoveToMBox()1143 void CollectionMoveTest::testMoveToMBox()
1144 {
1145     QDir topDir(mDir->path());
1146 
1147     // top level dir
1148     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), topDir.path(), QStringLiteral("collection1")));
1149     QFileInfo fileInfo1(topDir, QStringLiteral("collection1"));
1150     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), topDir.path(), QStringLiteral("collection2")));
1151     QFileInfo fileInfo2(topDir, QStringLiteral("collection2"));
1152     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), topDir.path(), QStringLiteral("collection3")));
1153     QFileInfo fileInfo3(topDir, QStringLiteral("collection3"));
1154     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), topDir.path(), QStringLiteral("collection4")));
1155     QFileInfo fileInfo4(topDir, QStringLiteral("collection4"));
1156     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), topDir.path(), QStringLiteral("collection5")));
1157     QFileInfo fileInfo5(topDir, QStringLiteral("collection5"));
1158     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), topDir.path(), QStringLiteral("collection6")));
1159 
1160     // first level maildir parent
1161     QDir subDir1 = topDir;
1162     QVERIFY(subDir1.mkdir(QStringLiteral(".collection1.directory")));
1163     QVERIFY(subDir1.cd(QStringLiteral(".collection1.directory")));
1164     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), subDir1.path(), QStringLiteral("collection1_1")));
1165     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), subDir1.path(), QStringLiteral("collection1_2")));
1166     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), subDir1.path(), QStringLiteral("collection1_3")));
1167     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), subDir1.path(), QStringLiteral("collection1_4")));
1168 
1169     // first level mbox parent
1170     QDir subDir4 = topDir;
1171     QVERIFY(subDir4.mkdir(QStringLiteral(".collection4.directory")));
1172     QVERIFY(subDir4.cd(QStringLiteral(".collection4.directory")));
1173     QVERIFY(TestDataUtil::installFolder(QStringLiteral("mbox"), subDir4.path(), QStringLiteral("collection4_1")));
1174     QVERIFY(TestDataUtil::installFolder(QStringLiteral("maildir"), subDir4.path(), QStringLiteral("collection4_2")));
1175 
1176     // target mbox
1177     QFileInfo fileInfoTarget(topDir.path(), QStringLiteral("target"));
1178     QFile fileTarget(fileInfoTarget.absoluteFilePath());
1179     QVERIFY(fileTarget.open(QIODevice::WriteOnly));
1180     fileTarget.close();
1181     QVERIFY(fileInfoTarget.exists());
1182 
1183     QDir subDirTarget;
1184 
1185     mStore->setPath(topDir.path());
1186 
1187     // common variables
1188     FileStore::CollectionMoveJob *job = nullptr;
1189     FileStore::ItemFetchJob *itemFetch = nullptr;
1190     Collection collection;
1191     const QVariant colListVar = QVariant::fromValue<Collection::List>(Collection::List());
1192     QVariant var;
1193     Collection::List collections;
1194     Item::List items;
1195     QMap<QByteArray, int> flagCounts;
1196 
1197     Collection target;
1198     target.setName(QStringLiteral("target"));
1199     target.setRemoteId(QStringLiteral("target"));
1200     target.setParentCollection(mStore->topLevelCollection());
1201 
1202     // test move leaf maildir into sibling
1203     Collection collection2;
1204     collection2.setName(QStringLiteral("collection2"));
1205     collection2.setRemoteId(QStringLiteral("collection2"));
1206     collection2.setParentCollection(mStore->topLevelCollection());
1207 
1208     job = mStore->moveCollection(collection2, target);
1209 
1210     QVERIFY(job->exec());
1211     QCOMPARE(job->error(), 0);
1212 
1213     subDirTarget = topDir;
1214     QVERIFY(subDirTarget.cd(QStringLiteral(".target.directory")));
1215 
1216     collection = job->collection();
1217     QCOMPARE(collection.remoteId(), collection2.remoteId());
1218     QCOMPARE(collection.parentCollection(), target);
1219 
1220     fileInfo2.refresh();
1221     QVERIFY(!fileInfo2.exists());
1222     fileInfo2 = QFileInfo(subDirTarget, collection.remoteId());
1223     QVERIFY(fileInfo2.exists());
1224 
1225     // check for index preservation
1226     var = job->property("onDiskIndexInvalidated");
1227     QVERIFY(var.isValid());
1228     QCOMPARE(var.userType(), colListVar.userType());
1229 
1230     collections = var.value<Collection::List>();
1231     QCOMPARE((int)collections.count(), 1);
1232     QCOMPARE(collections.first(), collection);
1233 
1234     // get the items and check the flags (see data/README)
1235     itemFetch = mStore->fetchItems(collection);
1236     QVERIFY(itemFetch->exec());
1237     QCOMPARE(itemFetch->error(), 0);
1238 
1239     items = itemFetch->items();
1240     QCOMPARE((int)items.count(), 4);
1241     for (const Item &item : std::as_const(items)) {
1242         const auto flags{item.flags()};
1243         for (const QByteArray &flag : flags) {
1244             ++flagCounts[flag];
1245         }
1246     }
1247 
1248     QCOMPARE(flagCounts["\\SEEN"], 2);
1249     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1250     QCOMPARE(flagCounts["$TODO"], 1);
1251     flagCounts.clear();
1252 
1253     // test move leaf mbox into sibling
1254     Collection collection3;
1255     collection3.setName(QStringLiteral("collection3"));
1256     collection3.setRemoteId(QStringLiteral("collection3"));
1257     collection3.setParentCollection(mStore->topLevelCollection());
1258 
1259     job = mStore->moveCollection(collection3, target);
1260 
1261     QVERIFY(job->exec());
1262     QCOMPARE(job->error(), 0);
1263 
1264     collection = job->collection();
1265     QCOMPARE(collection.remoteId(), collection3.remoteId());
1266     QCOMPARE(collection.parentCollection(), target);
1267 
1268     fileInfo3.refresh();
1269     QVERIFY(!fileInfo3.exists());
1270     fileInfo3 = QFileInfo(subDirTarget, collection.remoteId());
1271     QVERIFY(fileInfo3.exists());
1272 
1273     // check for index preservation
1274     var = job->property("onDiskIndexInvalidated");
1275     QVERIFY(var.isValid());
1276     QCOMPARE(var.userType(), colListVar.userType());
1277 
1278     collections = var.value<Collection::List>();
1279     QCOMPARE((int)collections.count(), 1);
1280     QCOMPARE(collections.first(), collection);
1281 
1282     // get the items and check the flags (see data/README)
1283     itemFetch = mStore->fetchItems(collection);
1284     QVERIFY(itemFetch->exec());
1285     QCOMPARE(itemFetch->error(), 0);
1286 
1287     items = itemFetch->items();
1288     QCOMPARE((int)items.count(), 4);
1289     for (const Item &item : std::as_const(items)) {
1290         const auto flags{item.flags()};
1291         for (const QByteArray &flag : flags) {
1292             ++flagCounts[flag];
1293         }
1294     }
1295 
1296     QCOMPARE(flagCounts["\\SEEN"], 2);
1297     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1298     QCOMPARE(flagCounts["$TODO"], 1);
1299     flagCounts.clear();
1300 
1301     // test move leaf mbox into sibling without subtree
1302     Collection collection5;
1303     collection5.setName(QStringLiteral("collection5"));
1304     collection5.setRemoteId(QStringLiteral("collection5"));
1305     collection5.setParentCollection(mStore->topLevelCollection());
1306 
1307     Collection collection6;
1308     collection6.setName(QStringLiteral("collection6"));
1309     collection6.setRemoteId(QStringLiteral("collection6"));
1310     collection6.setParentCollection(mStore->topLevelCollection());
1311 
1312     job = mStore->moveCollection(collection5, collection6);
1313 
1314     QVERIFY(job->exec());
1315     QCOMPARE(job->error(), 0);
1316 
1317     collection = job->collection();
1318     QCOMPARE(collection.remoteId(), collection5.remoteId());
1319     QCOMPARE(collection.parentCollection(), collection6);
1320 
1321     fileInfo5.refresh();
1322     QVERIFY(!fileInfo5.exists());
1323     QDir subDir6 = topDir;
1324     QVERIFY(subDir6.cd(QStringLiteral(".collection6.directory")));
1325     fileInfo5 = QFileInfo(subDir6, collection.remoteId());
1326     QVERIFY(fileInfo5.exists());
1327 
1328     // check for index preservation
1329     var = job->property("onDiskIndexInvalidated");
1330     QVERIFY(var.isValid());
1331     QCOMPARE(var.userType(), colListVar.userType());
1332 
1333     collections = var.value<Collection::List>();
1334     QCOMPARE((int)collections.count(), 1);
1335     QCOMPARE(collections.first(), collection);
1336 
1337     // get the items and check the flags (see data/README)
1338     itemFetch = mStore->fetchItems(collection);
1339     QVERIFY(itemFetch->exec());
1340     QCOMPARE(itemFetch->error(), 0);
1341 
1342     items = itemFetch->items();
1343     QCOMPARE((int)items.count(), 4);
1344     for (const Item &item : std::as_const(items)) {
1345         const auto flags{item.flags()};
1346         for (const QByteArray &flag : flags) {
1347             ++flagCounts[flag];
1348         }
1349     }
1350 
1351     QCOMPARE(flagCounts["\\SEEN"], 2);
1352     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1353     QCOMPARE(flagCounts["$TODO"], 1);
1354     flagCounts.clear();
1355 
1356     // test move maildir with subtree into sibling
1357     Collection collection1;
1358     collection1.setName(QStringLiteral("collection1"));
1359     collection1.setRemoteId(QStringLiteral("collection1"));
1360     collection1.setParentCollection(mStore->topLevelCollection());
1361 
1362     // load sub collection index data to check for correct cache updates
1363     Collection collection1_1;
1364     collection1_1.setName(QStringLiteral("collection1_1"));
1365     collection1_1.setRemoteId(QStringLiteral("collection1_1"));
1366     collection1_1.setParentCollection(collection1);
1367     itemFetch = mStore->fetchItems(collection1_1);
1368     QVERIFY(itemFetch->exec());
1369 
1370     Collection collection1_2;
1371     collection1_2.setName(QStringLiteral("collection1_2"));
1372     collection1_2.setRemoteId(QStringLiteral("collection1_2"));
1373     collection1_2.setParentCollection(collection1);
1374     itemFetch = mStore->fetchItems(collection1_2);
1375     QVERIFY(itemFetch->exec());
1376 
1377     job = mStore->moveCollection(collection1, target);
1378 
1379     QVERIFY(job->exec());
1380     QCOMPARE(job->error(), 0);
1381 
1382     collection = job->collection();
1383     QCOMPARE(collection.remoteId(), collection1.remoteId());
1384     QCOMPARE(collection.parentCollection(), target);
1385 
1386     fileInfo1.refresh();
1387     QVERIFY(!fileInfo1.exists());
1388     fileInfo1 = QFileInfo(subDirTarget, collection.remoteId());
1389     QVERIFY(fileInfo1.exists());
1390     QVERIFY(!subDir1.exists());
1391     subDir1 = subDirTarget;
1392     QVERIFY(subDir1.cd(QStringLiteral(".collection1.directory")));
1393     QCOMPARE(subDir1.entryList(QStringList() << QStringLiteral("collection*")),
1394              QStringList() << QStringLiteral("collection1_1") << QStringLiteral("collection1_2") << QStringLiteral("collection1_3")
1395                            << QStringLiteral("collection1_4"));
1396 
1397     // check for index preservation
1398     var = job->property("onDiskIndexInvalidated");
1399     QVERIFY(var.isValid());
1400     QCOMPARE(var.userType(), colListVar.userType());
1401 
1402     collections = var.value<Collection::List>();
1403     QCOMPARE((int)collections.count(), 1);
1404     QCOMPARE(collections.first(), collection);
1405 
1406     // get the items and check the flags (see data/README)
1407     itemFetch = mStore->fetchItems(collection);
1408     QVERIFY(itemFetch->exec());
1409     QCOMPARE(itemFetch->error(), 0);
1410 
1411     items = itemFetch->items();
1412     QCOMPARE((int)items.count(), 4);
1413     for (const Item &item : std::as_const(items)) {
1414         const auto flags{item.flags()};
1415         for (const QByteArray &flag : flags) {
1416             ++flagCounts[flag];
1417         }
1418     }
1419 
1420     QCOMPARE(flagCounts["\\SEEN"], 2);
1421     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1422     QCOMPARE(flagCounts["$TODO"], 1);
1423     flagCounts.clear();
1424 
1425     // check for children cache path updates
1426     collection1.setParentCollection(target);
1427     collection1_1.setParentCollection(collection1);
1428     collection1_2.setParentCollection(collection1);
1429 
1430     itemFetch = mStore->fetchItems(collection1_1);
1431     QVERIFY(itemFetch->exec());
1432     QCOMPARE(itemFetch->error(), 0);
1433 
1434     items = itemFetch->items();
1435     QCOMPARE((int)items.count(), 4);
1436     for (const Item &item : std::as_const(items)) {
1437         const auto flags{item.flags()};
1438         for (const QByteArray &flag : flags) {
1439             ++flagCounts[flag];
1440         }
1441     }
1442 
1443     QCOMPARE(flagCounts["\\SEEN"], 2);
1444     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1445     QCOMPARE(flagCounts["$TODO"], 1);
1446     flagCounts.clear();
1447 
1448     itemFetch = mStore->fetchItems(collection1_2);
1449     QVERIFY(itemFetch->exec());
1450     QCOMPARE(itemFetch->error(), 0);
1451 
1452     items = itemFetch->items();
1453     QCOMPARE((int)items.count(), 4);
1454     for (const Item &item : std::as_const(items)) {
1455         const auto flags{item.flags()};
1456         for (const QByteArray &flag : flags) {
1457             ++flagCounts[flag];
1458         }
1459     }
1460 
1461     QCOMPARE(flagCounts["\\SEEN"], 2);
1462     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1463     QCOMPARE(flagCounts["$TODO"], 1);
1464     flagCounts.clear();
1465 
1466     // test move mbox with subtree into sibling
1467     Collection collection4;
1468     collection4.setName(QStringLiteral("collection4"));
1469     collection4.setRemoteId(QStringLiteral("collection4"));
1470     collection4.setParentCollection(mStore->topLevelCollection());
1471 
1472     // load sub collection index data to check for correct cache updates
1473     Collection collection4_1;
1474     collection4_1.setName(QStringLiteral("collection4_1"));
1475     collection4_1.setRemoteId(QStringLiteral("collection4_1"));
1476     collection4_1.setParentCollection(collection4);
1477     itemFetch = mStore->fetchItems(collection4_1);
1478     QVERIFY(itemFetch->exec());
1479 
1480     Collection collection4_2;
1481     collection4_2.setName(QStringLiteral("collection4_2"));
1482     collection4_2.setRemoteId(QStringLiteral("collection4_2"));
1483     collection4_2.setParentCollection(collection4);
1484     itemFetch = mStore->fetchItems(collection4_2);
1485     QVERIFY(itemFetch->exec());
1486 
1487     job = mStore->moveCollection(collection4, target);
1488 
1489     QVERIFY(job->exec());
1490     QCOMPARE(job->error(), 0);
1491 
1492     collection = job->collection();
1493     QCOMPARE(collection.remoteId(), collection4.remoteId());
1494     QCOMPARE(collection.parentCollection(), target);
1495 
1496     fileInfo4.refresh();
1497     QVERIFY(!fileInfo4.exists());
1498     fileInfo4 = QFileInfo(subDirTarget, collection.remoteId());
1499     QVERIFY(fileInfo4.exists());
1500     QVERIFY(!subDir4.exists());
1501     subDir4 = subDirTarget;
1502     QVERIFY(subDir4.cd(QStringLiteral(".collection4.directory")));
1503     QCOMPARE(subDir4.entryList(QStringList() << QStringLiteral("collection*")),
1504              QStringList() << QStringLiteral("collection4_1") << QStringLiteral("collection4_2"));
1505 
1506     // check for index preservation
1507     var = job->property("onDiskIndexInvalidated");
1508     QVERIFY(var.isValid());
1509     QCOMPARE(var.userType(), colListVar.userType());
1510 
1511     collections = var.value<Collection::List>();
1512     QCOMPARE((int)collections.count(), 1);
1513     QCOMPARE(collections.first(), collection);
1514 
1515     // get the items and check the flags (see data/README)
1516     itemFetch = mStore->fetchItems(collection);
1517     QVERIFY(itemFetch->exec());
1518     QCOMPARE(itemFetch->error(), 0);
1519 
1520     items = itemFetch->items();
1521     QCOMPARE((int)items.count(), 4);
1522     for (const Item &item : std::as_const(items)) {
1523         const auto flags{item.flags()};
1524         for (const QByteArray &flag : flags) {
1525             ++flagCounts[flag];
1526         }
1527     }
1528 
1529     QCOMPARE(flagCounts["\\SEEN"], 2);
1530     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1531     QCOMPARE(flagCounts["$TODO"], 1);
1532     flagCounts.clear();
1533 
1534     // check for children cache path updates
1535     collection4.setParentCollection(target);
1536     collection4_1.setParentCollection(collection4);
1537     collection4_2.setParentCollection(collection4);
1538 
1539     itemFetch = mStore->fetchItems(collection4_1);
1540     QVERIFY(itemFetch->exec());
1541     QCOMPARE(itemFetch->error(), 0);
1542 
1543     items = itemFetch->items();
1544     QCOMPARE((int)items.count(), 4);
1545     for (const Item &item : std::as_const(items)) {
1546         const auto flags{item.flags()};
1547         for (const QByteArray &flag : flags) {
1548             ++flagCounts[flag];
1549         }
1550     }
1551 
1552     QCOMPARE(flagCounts["\\SEEN"], 2);
1553     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1554     QCOMPARE(flagCounts["$TODO"], 1);
1555     flagCounts.clear();
1556 
1557     itemFetch = mStore->fetchItems(collection4_2);
1558     QVERIFY(itemFetch->exec());
1559     QCOMPARE(itemFetch->error(), 0);
1560 
1561     items = itemFetch->items();
1562     QCOMPARE((int)items.count(), 4);
1563     for (const Item &item : std::as_const(items)) {
1564         const auto flags{item.flags()};
1565         for (const QByteArray &flag : flags) {
1566             ++flagCounts[flag];
1567         }
1568     }
1569 
1570     QCOMPARE(flagCounts["\\SEEN"], 2);
1571     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1572     QCOMPARE(flagCounts["$TODO"], 1);
1573     flagCounts.clear();
1574 
1575     // move from parent maildir to parent's sibling
1576     collection3.setParentCollection(target);
1577 
1578     job = mStore->moveCollection(collection1_1, collection3);
1579 
1580     QVERIFY(job->exec());
1581     QCOMPARE(job->error(), 0);
1582 
1583     QDir subDir3 = subDirTarget;
1584     QVERIFY(subDir3.cd(QStringLiteral(".collection3.directory")));
1585 
1586     collection = job->collection();
1587     QCOMPARE(collection.remoteId(), collection1_1.remoteId());
1588     QCOMPARE(collection.parentCollection(), collection3);
1589 
1590     QFileInfo fileInfo1_1(subDir1, collection.remoteId());
1591     QVERIFY(!fileInfo1_1.exists());
1592     fileInfo1_1 = QFileInfo(subDir3, collection.remoteId());
1593     QVERIFY(fileInfo1_1.exists());
1594 
1595     // check for index preservation
1596     var = job->property("onDiskIndexInvalidated");
1597     QVERIFY(var.isValid());
1598     QCOMPARE(var.userType(), colListVar.userType());
1599 
1600     collections = var.value<Collection::List>();
1601     QCOMPARE((int)collections.count(), 1);
1602     QCOMPARE(collections.first(), collection);
1603 
1604     // get the items and check the flags (see data/README)
1605     itemFetch = mStore->fetchItems(collection);
1606     QVERIFY(itemFetch->exec());
1607     QCOMPARE(itemFetch->error(), 0);
1608 
1609     items = itemFetch->items();
1610     QCOMPARE((int)items.count(), 4);
1611     for (const Item &item : std::as_const(items)) {
1612         const auto flags{item.flags()};
1613         for (const QByteArray &flag : flags) {
1614             ++flagCounts[flag];
1615         }
1616     }
1617 
1618     QCOMPARE(flagCounts["\\SEEN"], 2);
1619     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1620     QCOMPARE(flagCounts["$TODO"], 1);
1621     flagCounts.clear();
1622 
1623     // move from parent maildir to parent's sibling
1624     job = mStore->moveCollection(collection1_2, collection3);
1625 
1626     QVERIFY(job->exec());
1627     QCOMPARE(job->error(), 0);
1628 
1629     collection = job->collection();
1630     QCOMPARE(collection.remoteId(), collection1_2.remoteId());
1631     QCOMPARE(collection.parentCollection(), collection3);
1632 
1633     QFileInfo fileInfo1_2(subDir1, collection.remoteId());
1634     QVERIFY(!fileInfo1_2.exists());
1635     fileInfo1_2 = QFileInfo(subDir3, collection.remoteId());
1636     QVERIFY(fileInfo1_2.exists());
1637 
1638     // check for index preservation
1639     var = job->property("onDiskIndexInvalidated");
1640     QVERIFY(var.isValid());
1641     QCOMPARE(var.userType(), colListVar.userType());
1642 
1643     collections = var.value<Collection::List>();
1644     QCOMPARE((int)collections.count(), 1);
1645     QCOMPARE(collections.first(), collection);
1646 
1647     // get the items and check the flags (see data/README)
1648     itemFetch = mStore->fetchItems(collection);
1649     QVERIFY(itemFetch->exec());
1650     QCOMPARE(itemFetch->error(), 0);
1651 
1652     items = itemFetch->items();
1653     QCOMPARE((int)items.count(), 4);
1654     for (const Item &item : std::as_const(items)) {
1655         const auto flags{item.flags()};
1656         for (const QByteArray &flag : flags) {
1657             ++flagCounts[flag];
1658         }
1659     }
1660 
1661     QCOMPARE(flagCounts["\\SEEN"], 2);
1662     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1663     QCOMPARE(flagCounts["$TODO"], 1);
1664     flagCounts.clear();
1665 
1666     // move from parent mbox to parent's sibling
1667     job = mStore->moveCollection(collection4_1, collection3);
1668 
1669     QVERIFY(job->exec());
1670     QCOMPARE(job->error(), 0);
1671 
1672     collection = job->collection();
1673     QCOMPARE(collection.remoteId(), collection4_1.remoteId());
1674     QCOMPARE(collection.parentCollection(), collection3);
1675 
1676     QFileInfo fileInfo4_1(subDir4, collection.remoteId());
1677     QVERIFY(!fileInfo4_1.exists());
1678     fileInfo4_1 = QFileInfo(subDir3, collection.remoteId());
1679     QVERIFY(fileInfo4_1.exists());
1680 
1681     // check for index preservation
1682     var = job->property("onDiskIndexInvalidated");
1683     QVERIFY(var.isValid());
1684     QCOMPARE(var.userType(), colListVar.userType());
1685 
1686     collections = var.value<Collection::List>();
1687     QCOMPARE((int)collections.count(), 1);
1688     QCOMPARE(collections.first(), collection);
1689 
1690     // get the items and check the flags (see data/README)
1691     itemFetch = mStore->fetchItems(collection);
1692     QVERIFY(itemFetch->exec());
1693     QCOMPARE(itemFetch->error(), 0);
1694 
1695     items = itemFetch->items();
1696     QCOMPARE((int)items.count(), 4);
1697     for (const Item &item : std::as_const(items)) {
1698         const auto flags{item.flags()};
1699         for (const QByteArray &flag : flags) {
1700             ++flagCounts[flag];
1701         }
1702     }
1703 
1704     QCOMPARE(flagCounts["\\SEEN"], 2);
1705     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1706     QCOMPARE(flagCounts["$TODO"], 1);
1707     flagCounts.clear();
1708 
1709     // move from parent mbox to parent's sibling
1710     job = mStore->moveCollection(collection4_2, collection3);
1711 
1712     QVERIFY(job->exec());
1713     QCOMPARE(job->error(), 0);
1714 
1715     collection = job->collection();
1716     QCOMPARE(collection.remoteId(), collection4_2.remoteId());
1717     QCOMPARE(collection.parentCollection(), collection3);
1718 
1719     QFileInfo fileInfo4_2(subDir4, collection.remoteId());
1720     QVERIFY(!fileInfo4_2.exists());
1721     fileInfo4_2 = QFileInfo(subDir3, collection.remoteId());
1722     QVERIFY(fileInfo4_2.exists());
1723 
1724     // check for index preservation
1725     var = job->property("onDiskIndexInvalidated");
1726     QVERIFY(var.isValid());
1727     QCOMPARE(var.userType(), colListVar.userType());
1728 
1729     collections = var.value<Collection::List>();
1730     QCOMPARE((int)collections.count(), 1);
1731     QCOMPARE(collections.first(), collection);
1732 
1733     // get the items and check the flags (see data/README)
1734     itemFetch = mStore->fetchItems(collection);
1735     QVERIFY(itemFetch->exec());
1736     QCOMPARE(itemFetch->error(), 0);
1737 
1738     items = itemFetch->items();
1739     QCOMPARE((int)items.count(), 4);
1740     for (const Item &item : std::as_const(items)) {
1741         const auto flags{item.flags()};
1742         for (const QByteArray &flag : flags) {
1743             ++flagCounts[flag];
1744         }
1745     }
1746 
1747     QCOMPARE(flagCounts["\\SEEN"], 2);
1748     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1749     QCOMPARE(flagCounts["$TODO"], 1);
1750     flagCounts.clear();
1751 
1752     // move from parent mbox to grandparent
1753     collection1_1.setParentCollection(collection3);
1754 
1755     job = mStore->moveCollection(collection1_1, target);
1756 
1757     QVERIFY(job->exec());
1758     QCOMPARE(job->error(), 0);
1759 
1760     collection = job->collection();
1761     QCOMPARE(collection.remoteId(), collection1_1.remoteId());
1762     QCOMPARE(collection.parentCollection(), target);
1763 
1764     fileInfo1_1.refresh();
1765     QVERIFY(!fileInfo1_1.exists());
1766     fileInfo1_1 = QFileInfo(subDirTarget, collection.remoteId());
1767     QVERIFY(fileInfo1_1.exists());
1768 
1769     // check for index preservation
1770     var = job->property("onDiskIndexInvalidated");
1771     QVERIFY(var.isValid());
1772     QCOMPARE(var.userType(), colListVar.userType());
1773 
1774     collections = var.value<Collection::List>();
1775     QCOMPARE((int)collections.count(), 1);
1776     QCOMPARE(collections.first(), collection);
1777 
1778     // get the items and check the flags (see data/README)
1779     itemFetch = mStore->fetchItems(collection);
1780     QVERIFY(itemFetch->exec());
1781     QCOMPARE(itemFetch->error(), 0);
1782 
1783     items = itemFetch->items();
1784     QCOMPARE((int)items.count(), 4);
1785     for (const Item &item : std::as_const(items)) {
1786         const auto flags{item.flags()};
1787         for (const QByteArray &flag : flags) {
1788             ++flagCounts[flag];
1789         }
1790     }
1791 
1792     QCOMPARE(flagCounts["\\SEEN"], 2);
1793     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1794     QCOMPARE(flagCounts["$TODO"], 1);
1795     flagCounts.clear();
1796 
1797     // move from parent mbox to grandparent
1798     collection1_2.setParentCollection(collection3);
1799     job = mStore->moveCollection(collection1_2, target);
1800 
1801     QVERIFY(job->exec());
1802     QCOMPARE(job->error(), 0);
1803 
1804     collection = job->collection();
1805     QCOMPARE(collection.remoteId(), collection1_2.remoteId());
1806     QCOMPARE(collection.parentCollection(), target);
1807 
1808     fileInfo1_2.refresh();
1809     QVERIFY(!fileInfo1_2.exists());
1810     fileInfo1_2 = QFileInfo(subDirTarget, collection.remoteId());
1811     QVERIFY(fileInfo1_2.exists());
1812 
1813     // check for index preservation
1814     var = job->property("onDiskIndexInvalidated");
1815     QVERIFY(var.isValid());
1816     QCOMPARE(var.userType(), colListVar.userType());
1817 
1818     collections = var.value<Collection::List>();
1819     QCOMPARE((int)collections.count(), 1);
1820     QCOMPARE(collections.first(), collection);
1821 
1822     // get the items and check the flags (see data/README)
1823     itemFetch = mStore->fetchItems(collection);
1824     QVERIFY(itemFetch->exec());
1825     QCOMPARE(itemFetch->error(), 0);
1826 
1827     items = itemFetch->items();
1828     QCOMPARE((int)items.count(), 4);
1829     for (const Item &item : std::as_const(items)) {
1830         const auto flags{item.flags()};
1831         for (const QByteArray &flag : flags) {
1832             ++flagCounts[flag];
1833         }
1834     }
1835 
1836     QCOMPARE(flagCounts["\\SEEN"], 2);
1837     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1838     QCOMPARE(flagCounts["$TODO"], 1);
1839     flagCounts.clear();
1840 
1841     // move from parent maildir to grandparent
1842     Collection collection1_3;
1843     collection1_3.setName(QStringLiteral("collection1_3"));
1844     collection1_3.setRemoteId(QStringLiteral("collection1_3"));
1845     collection1_3.setParentCollection(collection1);
1846 
1847     job = mStore->moveCollection(collection1_3, target);
1848 
1849     QVERIFY(job->exec());
1850     QCOMPARE(job->error(), 0);
1851 
1852     collection = job->collection();
1853     QCOMPARE(collection.remoteId(), collection1_3.remoteId());
1854     QCOMPARE(collection.parentCollection(), target);
1855 
1856     QFileInfo fileInfo1_3(subDir1, collection.remoteId());
1857     QVERIFY(!fileInfo1_3.exists());
1858     fileInfo1_3 = QFileInfo(subDirTarget, collection.remoteId());
1859     QVERIFY(fileInfo1_3.exists());
1860 
1861     // check for index preservation
1862     var = job->property("onDiskIndexInvalidated");
1863     QVERIFY(var.isValid());
1864     QCOMPARE(var.userType(), colListVar.userType());
1865 
1866     collections = var.value<Collection::List>();
1867     QCOMPARE((int)collections.count(), 1);
1868     QCOMPARE(collections.first(), collection);
1869 
1870     // get the items and check the flags (see data/README)
1871     itemFetch = mStore->fetchItems(collection);
1872     QVERIFY(itemFetch->exec());
1873     QCOMPARE(itemFetch->error(), 0);
1874 
1875     items = itemFetch->items();
1876     QCOMPARE((int)items.count(), 4);
1877     for (const Item &item : std::as_const(items)) {
1878         const auto flags{item.flags()};
1879         for (const QByteArray &flag : flags) {
1880             ++flagCounts[flag];
1881         }
1882     }
1883 
1884     QCOMPARE(flagCounts["\\SEEN"], 2);
1885     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1886     QCOMPARE(flagCounts["$TODO"], 1);
1887     flagCounts.clear();
1888 
1889     // move from parent maildir to grandparent
1890     Collection collection1_4;
1891     collection1_4.setName(QStringLiteral("collection1_4"));
1892     collection1_4.setRemoteId(QStringLiteral("collection1_4"));
1893     collection1_4.setParentCollection(collection1);
1894 
1895     job = mStore->moveCollection(collection1_4, target);
1896 
1897     QVERIFY(job->exec());
1898     QCOMPARE(job->error(), 0);
1899 
1900     collection = job->collection();
1901     QCOMPARE(collection.remoteId(), collection1_4.remoteId());
1902     QCOMPARE(collection.parentCollection(), target);
1903 
1904     QFileInfo fileInfo1_4(subDir1, collection.remoteId());
1905     QVERIFY(!fileInfo1_4.exists());
1906     fileInfo1_4 = QFileInfo(subDirTarget, collection.remoteId());
1907     QVERIFY(fileInfo1_4.exists());
1908 
1909     // check for index preservation
1910     var = job->property("onDiskIndexInvalidated");
1911     QVERIFY(var.isValid());
1912     QCOMPARE(var.userType(), colListVar.userType());
1913 
1914     collections = var.value<Collection::List>();
1915     QCOMPARE((int)collections.count(), 1);
1916     QCOMPARE(collections.first(), collection);
1917 
1918     // get the items and check the flags (see data/README)
1919     itemFetch = mStore->fetchItems(collection);
1920     QVERIFY(itemFetch->exec());
1921     QCOMPARE(itemFetch->error(), 0);
1922 
1923     items = itemFetch->items();
1924     QCOMPARE((int)items.count(), 4);
1925     for (const Item &item : std::as_const(items)) {
1926         const auto flags{item.flags()};
1927         for (const QByteArray &flag : flags) {
1928             ++flagCounts[flag];
1929         }
1930     }
1931 
1932     QCOMPARE(flagCounts["\\SEEN"], 2);
1933     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1934     QCOMPARE(flagCounts["$TODO"], 1);
1935     flagCounts.clear();
1936 
1937     // move from mbox to grandchild
1938     collection1_1.setParentCollection(target);
1939 
1940     job = mStore->moveCollection(collection1_1, collection3);
1941 
1942     QVERIFY(job->exec());
1943     QCOMPARE(job->error(), 0);
1944 
1945     collection = job->collection();
1946     QCOMPARE(collection.remoteId(), collection1_1.remoteId());
1947     QCOMPARE(collection.parentCollection(), collection3);
1948 
1949     fileInfo1_1.refresh();
1950     QVERIFY(!fileInfo1_1.exists());
1951     fileInfo1_1 = QFileInfo(subDir3, collection.remoteId());
1952     QVERIFY(fileInfo1_1.exists());
1953 
1954     // check for index preservation
1955     var = job->property("onDiskIndexInvalidated");
1956     QVERIFY(var.isValid());
1957     QCOMPARE(var.userType(), colListVar.userType());
1958 
1959     collections = var.value<Collection::List>();
1960     QCOMPARE((int)collections.count(), 1);
1961     QCOMPARE(collections.first(), collection);
1962 
1963     // get the items and check the flags (see data/README)
1964     itemFetch = mStore->fetchItems(collection);
1965     QVERIFY(itemFetch->exec());
1966     QCOMPARE(itemFetch->error(), 0);
1967 
1968     items = itemFetch->items();
1969     QCOMPARE((int)items.count(), 4);
1970     for (const Item &item : std::as_const(items)) {
1971         const auto flags{item.flags()};
1972         for (const QByteArray &flag : flags) {
1973             ++flagCounts[flag];
1974         }
1975     }
1976 
1977     QCOMPARE(flagCounts["\\SEEN"], 2);
1978     QCOMPARE(flagCounts["\\FLAGGED"], 1);
1979     QCOMPARE(flagCounts["$TODO"], 1);
1980     flagCounts.clear();
1981 
1982     // move from maildir to grandchild
1983     collection1_2.setParentCollection(target);
1984 
1985     job = mStore->moveCollection(collection1_2, collection3);
1986 
1987     QVERIFY(job->exec());
1988     QCOMPARE(job->error(), 0);
1989 
1990     collection = job->collection();
1991     QCOMPARE(collection.remoteId(), collection1_2.remoteId());
1992     QCOMPARE(collection.parentCollection(), collection3);
1993 
1994     fileInfo1_2.refresh();
1995     QVERIFY(!fileInfo1_2.exists());
1996     fileInfo1_2 = QFileInfo(subDir3, collection.remoteId());
1997     QVERIFY(fileInfo1_2.exists());
1998 
1999     // check for index preservation
2000     var = job->property("onDiskIndexInvalidated");
2001     QVERIFY(var.isValid());
2002     QCOMPARE(var.userType(), colListVar.userType());
2003 
2004     collections = var.value<Collection::List>();
2005     QCOMPARE((int)collections.count(), 1);
2006     QCOMPARE(collections.first(), collection);
2007 
2008     // get the items and check the flags (see data/README)
2009     itemFetch = mStore->fetchItems(collection);
2010     QVERIFY(itemFetch->exec());
2011     QCOMPARE(itemFetch->error(), 0);
2012 
2013     items = itemFetch->items();
2014     QCOMPARE((int)items.count(), 4);
2015     for (const Item &item : std::as_const(items)) {
2016         const auto flags{item.flags()};
2017         for (const QByteArray &flag : flags) {
2018             ++flagCounts[flag];
2019         }
2020     }
2021 
2022     QCOMPARE(flagCounts["\\SEEN"], 2);
2023     QCOMPARE(flagCounts["\\FLAGGED"], 1);
2024     QCOMPARE(flagCounts["$TODO"], 1);
2025     flagCounts.clear();
2026 }
2027 
2028 QTEST_MAIN(CollectionMoveTest)
2029 
2030 #include "collectionmovetest.moc"
2031