File indexing completed on 2024-05-12 05:10:39

0001 /*
0002     SPDX-FileCopyrightText: 2010-2011 Sérgio Martins <iamsergio@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "../src/incidencechanger.h"
0008 
0009 #include <Akonadi/Collection>
0010 #include <Akonadi/CollectionFetchJob>
0011 #include <Akonadi/CollectionFetchScope>
0012 #include <akonadi/qtest_akonadi.h>
0013 
0014 #include <Akonadi/Item>
0015 #include <Akonadi/ItemCreateJob>
0016 #include <Akonadi/ItemDeleteJob>
0017 #include <Akonadi/ItemFetchJob>
0018 #include <Akonadi/ItemFetchScope>
0019 
0020 #include <KCalendarCore/Event>
0021 
0022 #include <QBitArray>
0023 
0024 using namespace Akonadi;
0025 using namespace KCalendarCore;
0026 
0027 Q_DECLARE_METATYPE(QList<Akonadi::IncidenceChanger::ChangeType>)
0028 Q_DECLARE_METATYPE(QList<bool>)
0029 Q_DECLARE_METATYPE(QList<Akonadi::Collection::Right>)
0030 Q_DECLARE_METATYPE(QList<Akonadi::Collection::Rights>)
0031 Q_DECLARE_METATYPE(QList<Akonadi::IncidenceChanger::ResultCode>)
0032 Q_DECLARE_METATYPE(KCalendarCore::RecurrenceRule::PeriodType)
0033 QString s_ourEmail = QStringLiteral("unittests@dev.nul"); // change also in kdepimlibs/akonadi/calendar/tests/unittestenv/kdehome/share/config
0034 QString s_outEmail2 = QStringLiteral("identity2@kde.org");
0035 
0036 static Akonadi::Item item()
0037 {
0038     Item item;
0039     Incidence::Ptr incidence = Incidence::Ptr(new Event());
0040     incidence->setSummary(QStringLiteral("random summary"));
0041     item.setMimeType(incidence->mimeType());
0042     item.setPayload<KCalendarCore::Incidence::Ptr>(incidence);
0043     return item;
0044 }
0045 
0046 static Akonadi::Item createItem(const Akonadi::Collection &collection)
0047 {
0048     Item i = item();
0049     auto createJob = new ItemCreateJob(i, collection);
0050 
0051     createJob->exec();
0052     return createJob->item();
0053 }
0054 
0055 class IncidenceChangerTest : public QObject
0056 {
0057     Q_OBJECT
0058     Collection mCollection;
0059 
0060     QHash<int, IncidenceChanger::ResultCode> mExpectedResultByChangeId;
0061     IncidenceChanger *mChanger = nullptr;
0062 
0063     int mIncidencesToDelete;
0064     int mIncidencesToAdd;
0065     int mIncidencesToModify;
0066 
0067     QHash<int, Akonadi::Item::Id> mItemIdByChangeId;
0068     QHash<QString, Akonadi::Item::Id> mItemIdByUid;
0069     int mChangeToWaitFor;
0070     bool mPermissionsOrRollback;
0071     bool mDiscardedEqualsSuccess;
0072     Akonadi::Item mLastItemCreated;
0073 
0074 private Q_SLOTS:
0075     void initTestCase()
0076     {
0077         AkonadiTest::checkTestIsIsolated();
0078 
0079         mIncidencesToDelete = 0;
0080         mIncidencesToAdd = 0;
0081         mIncidencesToModify = 0;
0082         mPermissionsOrRollback = false;
0083         mDiscardedEqualsSuccess = false;
0084 
0085         mChangeToWaitFor = -1;
0086         // Control::start(); //TODO: uncomment when using testrunner
0087         qRegisterMetaType<Akonadi::Item>("Akonadi::Item");
0088         qRegisterMetaType<QList<Akonadi::IncidenceChanger::ChangeType>>("QList<Akonadi::IncidenceChanger::ChangeType>");
0089         auto job = new CollectionFetchJob(Collection::root(), CollectionFetchJob::Recursive, this);
0090         // Get list of collections
0091         job->fetchScope().setContentMimeTypes(QStringList() << QStringLiteral("application/x-vnd.akonadi.calendar.event"));
0092         AKVERIFYEXEC(job);
0093 
0094         // Find our collection
0095         Collection::List collections = job->collections();
0096         QVERIFY(!collections.isEmpty());
0097         mCollection = collections.first();
0098         mCollection.setRights(Collection::Rights(Collection::AllRights));
0099 
0100         QVERIFY(mCollection.isValid());
0101         QVERIFY((mCollection.rights() & Akonadi::Collection::CanCreateItem));
0102 
0103         mChanger = new IncidenceChanger(this);
0104         mChanger->setShowDialogsOnError(false);
0105 
0106         connect(mChanger, &IncidenceChanger::createFinished, this, &IncidenceChangerTest::createFinished);
0107 
0108         connect(mChanger, &IncidenceChanger::deleteFinished, this, &IncidenceChangerTest::deleteFinished);
0109 
0110         connect(mChanger, &IncidenceChanger::modifyFinished, this, &IncidenceChangerTest::modifyFinished);
0111     }
0112 
0113     void testCreating_data()
0114     {
0115         QTest::addColumn<bool>("sendInvalidIncidence");
0116         QTest::addColumn<QString>("uid");
0117         QTest::addColumn<QString>("summary");
0118         QTest::addColumn<Akonadi::Collection>("destinationCollection");
0119         QTest::addColumn<Akonadi::Collection>("defaultCollection");
0120         QTest::addColumn<bool>("respectsCollectionRights");
0121         QTest::addColumn<Akonadi::IncidenceChanger::DestinationPolicy>("destinationPolicy");
0122         QTest::addColumn<bool>("failureExpected");
0123         QTest::addColumn<Akonadi::IncidenceChanger::ResultCode>("expectedResultCode");
0124 
0125         QTest::newRow("Simple Creation1") << false << "SomeUid1"
0126                                           << "Summary1" << mCollection << Collection() << true << IncidenceChanger::DestinationPolicyNeverAsk << false
0127                                           << IncidenceChanger::ResultCodeSuccess;
0128 
0129         QTest::newRow("Simple Creation2") << false << "SomeUid2"
0130                                           << "Summary2" << mCollection << Collection() << true << IncidenceChanger::DestinationPolicyNeverAsk << false
0131                                           << IncidenceChanger::ResultCodeSuccess;
0132 
0133         QTest::newRow("Invalid incidence") << true << "SomeUid3"
0134                                            << "Summary3" << mCollection << Collection() << true << IncidenceChanger::DestinationPolicyNeverAsk << true;
0135 
0136         QTest::newRow("Invalid collection") << false << "SomeUid4"
0137                                             << "Summary4" << Collection() << Collection() << true << IncidenceChanger::DestinationPolicyNeverAsk << false
0138                                             << IncidenceChanger::ResultCodeInvalidDefaultCollection;
0139 
0140         QTest::newRow("Default collection") << false << "SomeUid5"
0141                                             << "Summary5" << Collection() << mCollection << true << IncidenceChanger::DestinationPolicyDefault << false
0142                                             << IncidenceChanger::ResultCodeSuccess;
0143 
0144         // In this case, the collection dialog shouldn't be shown, as we only have 1 collection
0145         QTest::newRow("Only one collection") << false << "SomeUid6"
0146                                              << "Summary6" << Collection() << Collection() << true << IncidenceChanger::DestinationPolicyAsk << false
0147                                              << IncidenceChanger::ResultCodeSuccess;
0148 
0149         Collection collectionWithoutRights = Collection(mCollection.id());
0150         collectionWithoutRights.setRights(Collection::Rights());
0151         Q_ASSERT((mCollection.rights() & Akonadi::Collection::CanCreateItem));
0152 
0153         QTest::newRow("No rights") << false << "SomeUid6"
0154                                    << "Summary6" << Collection() << collectionWithoutRights << true << IncidenceChanger::DestinationPolicyNeverAsk << false
0155                                    << IncidenceChanger::ResultCodePermissions;
0156 
0157         QTest::newRow("No rights but its ok") << false << "SomeUid7"
0158                                               << "Summary7" << Collection() << collectionWithoutRights << false << IncidenceChanger::DestinationPolicyNeverAsk
0159                                               << false << IncidenceChanger::ResultCodeSuccess;
0160     }
0161 
0162     void testCreating()
0163     {
0164         QFETCH(bool, sendInvalidIncidence);
0165         QFETCH(QString, uid);
0166         QFETCH(QString, summary);
0167         QFETCH(Akonadi::Collection, destinationCollection);
0168         QFETCH(Akonadi::Collection, defaultCollection);
0169         QFETCH(bool, respectsCollectionRights);
0170         QFETCH(Akonadi::IncidenceChanger::DestinationPolicy, destinationPolicy);
0171         QFETCH(bool, failureExpected);
0172 
0173         Incidence::Ptr incidence;
0174 
0175         if (!sendInvalidIncidence) {
0176             incidence = Incidence::Ptr(new Event());
0177             incidence->setUid(uid);
0178             incidence->setSummary(summary);
0179         }
0180         mChanger->setRespectsCollectionRights(respectsCollectionRights);
0181         mChanger->setDestinationPolicy(destinationPolicy);
0182         mChanger->setDefaultCollection(defaultCollection);
0183         const int changeId = mChanger->createIncidence(incidence, destinationCollection);
0184 
0185         QVERIFY(failureExpected ^ (changeId != -1));
0186 
0187         if (!failureExpected) {
0188             QFETCH(Akonadi::IncidenceChanger::ResultCode, expectedResultCode);
0189             mIncidencesToAdd = 1;
0190 
0191             mExpectedResultByChangeId.insert(changeId, expectedResultCode);
0192             waitForSignals();
0193 
0194             if (expectedResultCode == IncidenceChanger::ResultCodeSuccess && !failureExpected) {
0195                 Item item;
0196                 QVERIFY(mItemIdByChangeId.contains(changeId));
0197                 item.setId(mItemIdByChangeId.value(changeId));
0198                 auto fetchJob = new ItemFetchJob(item, this);
0199                 fetchJob->fetchScope().fetchFullPayload();
0200                 AKVERIFYEXEC(fetchJob);
0201                 QVERIFY(!fetchJob->items().isEmpty());
0202                 Item retrievedItem = fetchJob->items().first();
0203                 QVERIFY(retrievedItem.isValid());
0204                 QVERIFY(retrievedItem.hasPayload());
0205                 QVERIFY(retrievedItem.hasPayload<KCalendarCore::Event::Ptr>());
0206                 QVERIFY(retrievedItem.hasPayload<KCalendarCore::Incidence::Ptr>());
0207                 auto incidence = retrievedItem.payload<KCalendarCore::Incidence::Ptr>();
0208                 QCOMPARE(incidence->summary(), summary);
0209                 QCOMPARE(incidence->uid(), uid);
0210             }
0211         }
0212         mItemIdByChangeId.clear();
0213     }
0214 
0215     void testDeleting_data()
0216     {
0217         QTest::addColumn<Akonadi::Item::List>("items");
0218         QTest::addColumn<bool>("respectsCollectionRights");
0219         QTest::addColumn<bool>("failureExpected");
0220         QTest::addColumn<Akonadi::IncidenceChanger::ResultCode>("expectedResultCode");
0221 
0222         QTest::newRow("Delete empty list") << Item::List() << true << true;
0223         QTest::newRow("Delete invalid item") << (Item::List() << Item()) << true << true;
0224 
0225         auto fetchJob = new ItemFetchJob(mCollection);
0226         fetchJob->fetchScope().fetchFullPayload();
0227         AKVERIFYEXEC(fetchJob);
0228         Item::List items = fetchJob->items();
0229 
0230         // 5 Incidences were created in testCreating(). Keep this in sync.
0231         QCOMPARE(items.count(), 5);
0232         QTest::newRow("Simple delete") << (Item::List() << items.at(0)) << true << false << IncidenceChanger::ResultCodeSuccess;
0233 
0234         QTest::newRow("Delete already deleted") << (Item::List() << items.at(0)) << true << false << IncidenceChanger::ResultCodeAlreadyDeleted;
0235 
0236         QTest::newRow("Delete all others") << (Item::List() << items.at(1) << items.at(2)) << true << false << IncidenceChanger::ResultCodeSuccess;
0237 
0238         Collection collectionWithoutRights = Collection(mCollection.id());
0239         collectionWithoutRights.setRights(Collection::Rights());
0240         Item item = items.at(3);
0241         item.setParentCollection(collectionWithoutRights);
0242         QTest::newRow("Delete can't delete") << (Item::List() << item) << true << false << IncidenceChanger::ResultCodePermissions;
0243     }
0244 
0245     void testDeleting()
0246     {
0247         QFETCH(Akonadi::Item::List, items);
0248         QFETCH(bool, respectsCollectionRights);
0249         QFETCH(bool, failureExpected);
0250         mChanger->setRespectsCollectionRights(respectsCollectionRights);
0251         const int changeId = mChanger->deleteIncidences(items);
0252 
0253         QVERIFY(failureExpected ^ (changeId != -1));
0254 
0255         if (!failureExpected) {
0256             QFETCH(Akonadi::IncidenceChanger::ResultCode, expectedResultCode);
0257             mIncidencesToDelete = 1;
0258             mExpectedResultByChangeId.insert(changeId, expectedResultCode);
0259             waitForSignals();
0260 
0261             if (expectedResultCode == IncidenceChanger::ResultCodeSuccess) {
0262                 // Check that the incidence was really deleted
0263                 for (const Akonadi::Item &item : std::as_const(items)) {
0264                     auto fetchJob = new ItemFetchJob(item, this);
0265                     fetchJob->fetchScope().fetchFullPayload();
0266                     QVERIFY(!fetchJob->exec());
0267                     QVERIFY(fetchJob->items().isEmpty());
0268                     delete fetchJob;
0269                 }
0270             }
0271         }
0272     }
0273 
0274     void testModifying_data()
0275     {
0276         QTest::addColumn<Akonadi::Item>("item");
0277         QTest::addColumn<QString>("newSummary");
0278         QTest::addColumn<bool>("respectsCollectionRights");
0279         QTest::addColumn<int>("expectedRevision");
0280         QTest::addColumn<bool>("failureExpected");
0281         QTest::addColumn<Akonadi::IncidenceChanger::ResultCode>("expectedResultCode");
0282 
0283         QTest::newRow("Invalid item") << Item() << QString() << true << 0 << true;
0284         QTest::newRow("Valid item, invalid payload") << Item(1) << QString() << true << 0 << true;
0285 
0286         Item item;
0287         item.setMimeType(Event::eventMimeType());
0288         Incidence::Ptr incidence = Incidence::Ptr(new Event());
0289         incidence->setUid(QStringLiteral("test123uid"));
0290         incidence->setSummary(QStringLiteral("summary"));
0291         item.setPayload<KCalendarCore::Incidence::Ptr>(incidence);
0292         auto job = new ItemCreateJob(item, mCollection, this);
0293         AKVERIFYEXEC(job);
0294         item = job->item();
0295         incidence->setSummary(QStringLiteral("New Summary"));
0296         item.setPayload<KCalendarCore::Incidence::Ptr>(incidence);
0297 
0298         QTest::newRow("Change summary") << item << "New Summary" << true << 1 << false << IncidenceChanger::ResultCodeSuccess;
0299 
0300         Collection collectionWithoutRights = Collection(mCollection.id());
0301         collectionWithoutRights.setRights(Collection::Rights());
0302         item.setParentCollection(collectionWithoutRights);
0303 
0304         QTest::newRow("Can't change") << item << "New Summary" << true << 1 << false << IncidenceChanger::ResultCodePermissions;
0305     }
0306 
0307     void testModifying()
0308     {
0309         QFETCH(Akonadi::Item, item);
0310         QFETCH(QString, newSummary);
0311         QFETCH(bool, respectsCollectionRights);
0312         QFETCH(int, expectedRevision);
0313         QFETCH(bool, failureExpected);
0314 
0315         mChanger->setRespectsCollectionRights(respectsCollectionRights);
0316         const int changeId = mChanger->modifyIncidence(item);
0317         QVERIFY(failureExpected ^ (changeId != -1));
0318 
0319         if (!failureExpected) {
0320             QFETCH(Akonadi::IncidenceChanger::ResultCode, expectedResultCode);
0321 
0322             mIncidencesToModify = 1;
0323             mExpectedResultByChangeId.insert(changeId, expectedResultCode);
0324             waitForSignals();
0325             auto fetchJob = new ItemFetchJob(item, this);
0326             fetchJob->fetchScope().fetchFullPayload();
0327             AKVERIFYEXEC(fetchJob);
0328             QVERIFY(fetchJob->items().count() == 1);
0329             Item fetchedItem = fetchJob->items().constFirst();
0330             QVERIFY(fetchedItem.isValid());
0331             QVERIFY(fetchedItem.hasPayload<KCalendarCore::Incidence::Ptr>());
0332             auto incidence = fetchedItem.payload<KCalendarCore::Incidence::Ptr>();
0333             QCOMPARE(incidence->summary(), newSummary);
0334             QCOMPARE(incidence->revision(), expectedRevision);
0335             delete fetchJob;
0336         }
0337     }
0338 
0339     void testModifyingAlarmSettings()
0340     {
0341         // A user should be able to change alarm settings independently
0342         // do not trigger a revision increment
0343         // kolab #1386
0344         Item item;
0345         item.setMimeType(Event::eventMimeType());
0346         Incidence::Ptr incidence = Incidence::Ptr(new Event());
0347         incidence->setUid(QStringLiteral("test123uid"));
0348         incidence->setSummary(QStringLiteral("summary"));
0349         incidence->setOrganizer(Person(QStringLiteral("orga"), QStringLiteral("orga@dev.nul")));
0350         incidence->setDirtyFields(QSet<IncidenceBase::Field>());
0351         item.setPayload<KCalendarCore::Incidence::Ptr>(incidence);
0352         auto job = new ItemCreateJob(item, mCollection, this);
0353         AKVERIFYEXEC(job);
0354         item = job->item();
0355         Alarm::Ptr alarm = Alarm::Ptr(new Alarm(incidence.data()));
0356         alarm->setStartOffset(Duration(-15));
0357         alarm->setType(Alarm::Display);
0358         incidence->addAlarm(alarm);
0359         item.setPayload<KCalendarCore::Incidence::Ptr>(incidence);
0360 
0361         mChanger->setRespectsCollectionRights(true);
0362         const int changeId = mChanger->modifyIncidence(item);
0363         QVERIFY(changeId != -1);
0364 
0365         mIncidencesToModify = 1;
0366         mExpectedResultByChangeId.insert(changeId, IncidenceChanger::ResultCodeSuccess);
0367         waitForSignals();
0368         auto fetchJob = new ItemFetchJob(item, this);
0369         fetchJob->fetchScope().fetchFullPayload();
0370         AKVERIFYEXEC(fetchJob);
0371         QVERIFY(fetchJob->items().count() == 1);
0372         Item fetchedItem = fetchJob->items().constFirst();
0373         QVERIFY(fetchedItem.isValid());
0374         QVERIFY(fetchedItem.hasPayload<KCalendarCore::Incidence::Ptr>());
0375         auto incidence2 = fetchedItem.payload<KCalendarCore::Incidence::Ptr>();
0376         QCOMPARE(incidence2->alarms().count(), 1);
0377         QCOMPARE(incidence2->revision(), 0);
0378         delete fetchJob;
0379     }
0380 
0381     void testModifyRescedule_data()
0382     {
0383         // When a event is resceduled than all attendees part status should set to NEEDS-ACTION
0384         // kolab #4533
0385         QTest::addColumn<Akonadi::Item>("item");
0386         QTest::addColumn<Event::Ptr>("event");
0387         QTest::addColumn<bool>("expectReset");
0388 
0389         Attendee us(QString(), s_ourEmail);
0390         us.setStatus(Attendee::Accepted);
0391         Attendee mia(QStringLiteral("Mia Wallace"), QStringLiteral("mia@dev.nul"));
0392         mia.setStatus(Attendee::Declined);
0393         mia.setRSVP(false);
0394         Attendee vincent(QStringLiteral("Vincent"), QStringLiteral("vincent@dev.nul"));
0395         vincent.setStatus(Attendee::Delegated);
0396         Attendee jules(QStringLiteral("Jules"), QStringLiteral("jules@dev.nul"));
0397         jules.setStatus(Attendee::Accepted);
0398         jules.setRole(Attendee::NonParticipant);
0399 
0400         // we as organizator
0401         Item item;
0402         item.setMimeType(Event::eventMimeType());
0403         Event::Ptr incidence = Event::Ptr(new Event());
0404         incidence->setUid(QStringLiteral("test123uid"));
0405         incidence->setDtStart(QDateTime(QDate(2006, 1, 8), QTime(12, 0, 0), QTimeZone::utc()));
0406         incidence->setDtEnd(QDateTime(QDate(2006, 1, 8), QTime(14, 0, 0), QTimeZone::utc()));
0407         incidence->setAllDay(false);
0408         incidence->setLocation(QStringLiteral("location"));
0409         incidence->setOrganizer(Person(QString(), s_ourEmail));
0410         incidence->addAttendee(us);
0411         incidence->addAttendee(mia);
0412         incidence->addAttendee(vincent);
0413         incidence->addAttendee(jules);
0414         incidence->setDirtyFields(QSet<IncidenceBase::Field>());
0415         item.setPayload<KCalendarCore::Incidence::Ptr>(incidence);
0416 
0417         {
0418             Event::Ptr event = Event::Ptr(new Event(*incidence));
0419             event->setDtStart(QDateTime(QDate(2006, 1, 8), QTime(13, 0, 0), QTimeZone::utc()));
0420             QCOMPARE(event->dirtyFields().count(), 1);
0421             QTest::newRow("organizator:start Date") << item << event << true;
0422         }
0423 
0424         {
0425             Event::Ptr event = Event::Ptr(new Event(*incidence));
0426             event->setDtEnd(QDateTime(QDate(2006, 1, 8), QTime(13, 0, 0), QTimeZone::utc()));
0427             QCOMPARE(event->dirtyFields().count(), 1);
0428             QTest::newRow("organizator:end Date") << item << event << true;
0429         }
0430 
0431         {
0432             Event::Ptr event = Event::Ptr(new Event(*incidence));
0433             event->setAllDay(true);
0434             QCOMPARE(event->dirtyFields().count(), 2);
0435             QTest::newRow("organizator:allDay") << item << event << true;
0436         }
0437 
0438         {
0439             Event::Ptr event = Event::Ptr(new Event(*incidence));
0440             event->setLocation(QStringLiteral("location2"));
0441             QCOMPARE(event->dirtyFields().count(), 1);
0442             QTest::newRow("organizator:location") << item << event << true;
0443         }
0444 
0445         {
0446             Event::Ptr event = Event::Ptr(new Event(*incidence));
0447             event->setSummary(QStringLiteral("summary"));
0448             QCOMPARE(event->dirtyFields().count(), 1);
0449             QTest::newRow("organizator:summary") << item << event << false;
0450         }
0451 
0452         // we are normal attendee
0453         Item item2;
0454         item2.setMimeType(Event::eventMimeType());
0455         Event::Ptr incidence2 = Event::Ptr(new Event());
0456         incidence2->setUid(QStringLiteral("test123uid"));
0457         incidence2->setDtStart(QDateTime(QDate(2006, 1, 8), QTime(12, 0, 0), QTimeZone::utc()));
0458         incidence2->setDtEnd(QDateTime(QDate(2006, 1, 8), QTime(14, 0, 0), QTimeZone::utc()));
0459         incidence2->setAllDay(false);
0460         incidence2->setLocation(QStringLiteral("location"));
0461         incidence2->setOrganizer(Person(QStringLiteral("External organizator"), QStringLiteral("exorga@dev.nul")));
0462         incidence2->addAttendee(us);
0463         incidence2->addAttendee(mia);
0464         incidence2->addAttendee(vincent);
0465         incidence2->addAttendee(jules);
0466         incidence2->setDirtyFields(QSet<IncidenceBase::Field>());
0467         item2.setPayload<KCalendarCore::Incidence::Ptr>(incidence2);
0468 
0469         {
0470             Event::Ptr event = Event::Ptr(new Event(*incidence2));
0471             event->setDtStart(QDateTime(QDate(2006, 1, 8), QTime(13, 0, 0), QTimeZone::utc()));
0472             QTest::newRow("attendee:start Date") << item2 << event << false;
0473         }
0474 
0475         {
0476             Event::Ptr event = Event::Ptr(new Event(*incidence2));
0477             event->setDtEnd(QDateTime(QDate(2006, 1, 8), QTime(13, 0, 0), QTimeZone::utc()));
0478             QTest::newRow("attendee:end Date") << item2 << event << false;
0479         }
0480 
0481         {
0482             Event::Ptr event = Event::Ptr(new Event(*incidence2));
0483             event->setAllDay(false);
0484             QTest::newRow("attendee:allDay") << item2 << event << false;
0485         }
0486 
0487         {
0488             Event::Ptr event = Event::Ptr(new Event(*incidence2));
0489             event->setLocation(QStringLiteral("location2"));
0490             QTest::newRow("attendee:location") << item2 << event << false;
0491         }
0492 
0493         {
0494             Event::Ptr event = Event::Ptr(new Event(*incidence2));
0495             event->setSummary(QStringLiteral("summary"));
0496             QTest::newRow("attendee:summary") << item2 << event << false;
0497         }
0498     }
0499 
0500     void testModifyRescedule()
0501     {
0502         QFETCH(Akonadi::Item, item);
0503         QFETCH(Event::Ptr, event);
0504         QFETCH(bool, expectReset);
0505 
0506         item.setId(-1);
0507         auto job = new ItemCreateJob(item, mCollection, this);
0508         AKVERIFYEXEC(job);
0509         item = job->item();
0510         item.setPayload<KCalendarCore::Incidence::Ptr>(event);
0511 
0512         int revision = event->revision();
0513 
0514         mChanger->setRespectsCollectionRights(true);
0515         const int changeId = mChanger->modifyIncidence(item);
0516         QVERIFY(changeId != -1);
0517 
0518         mIncidencesToModify = 1;
0519         mExpectedResultByChangeId.insert(changeId, IncidenceChanger::ResultCodeSuccess);
0520         waitForSignals();
0521         auto fetchJob = new ItemFetchJob(item, this);
0522         fetchJob->fetchScope().fetchFullPayload();
0523         AKVERIFYEXEC(fetchJob);
0524         QVERIFY(fetchJob->items().count() == 1);
0525         Item fetchedItem = fetchJob->items().constFirst();
0526 
0527         QVERIFY(fetchedItem.isValid());
0528         QVERIFY(fetchedItem.hasPayload<KCalendarCore::Event::Ptr>());
0529         auto incidence = fetchedItem.payload<KCalendarCore::Event::Ptr>();
0530 
0531         QCOMPARE(incidence->revision(), revision + 1);
0532 
0533         if (expectReset) {
0534             if (incidence->organizer().email() == s_ourEmail) {
0535                 QCOMPARE(incidence->attendeeByMail(s_ourEmail).status(), Attendee::Accepted);
0536             } else {
0537                 QCOMPARE(incidence->attendeeByMail(s_ourEmail).status(), Attendee::NeedsAction);
0538             }
0539             QCOMPARE(incidence->attendeeByMail(QStringLiteral("mia@dev.nul")).status(), Attendee::NeedsAction);
0540             QCOMPARE(incidence->attendeeByMail(QStringLiteral("mia@dev.nul")).RSVP(), true);
0541             QCOMPARE(incidence->attendeeByMail(QStringLiteral("vincent@dev.nul")).status(), Attendee::Delegated);
0542             QCOMPARE(incidence->attendeeByMail(QStringLiteral("vincent@dev.nul")).RSVP(), false);
0543             QCOMPARE(incidence->attendeeByMail(QStringLiteral("jules@dev.nul")).status(), Attendee::Accepted);
0544             QCOMPARE(incidence->attendeeByMail(QStringLiteral("jules@dev.nul")).RSVP(), false);
0545         } else {
0546             QCOMPARE(incidence->attendeeByMail(s_ourEmail).status(), Attendee::Accepted);
0547             QCOMPARE(incidence->attendeeByMail(QStringLiteral("mia@dev.nul")).status(), Attendee::Declined);
0548             QCOMPARE(incidence->attendeeByMail(QStringLiteral("vincent@dev.nul")).status(), Attendee::Delegated);
0549             QCOMPARE(incidence->attendeeByMail(QStringLiteral("jules@dev.nul")).status(), Attendee::Accepted);
0550         }
0551         delete fetchJob;
0552     }
0553 
0554     void testMassModifyForConflicts_data()
0555     {
0556         QTest::addColumn<Akonadi::Item>("item");
0557         QTest::addColumn<bool>("waitForPreviousJob");
0558         QTest::addColumn<int>("numberOfModifications");
0559 
0560         // Create an incidence
0561         Item item;
0562         item.setMimeType(Event::eventMimeType());
0563         Incidence::Ptr incidence = Incidence::Ptr(new Event());
0564         incidence->setUid(QStringLiteral("test123uid"));
0565         incidence->setSummary(QStringLiteral("summary"));
0566         item.setPayload<KCalendarCore::Incidence::Ptr>(incidence);
0567         auto job = new ItemCreateJob(item, mCollection, this);
0568         AKVERIFYEXEC(job);
0569         item = job->item();
0570 
0571         QTest::newRow("15 modifications in sequence") << item << true << 15;
0572         QTest::newRow("15 modifications in parallel") << item << false << 15;
0573     }
0574 
0575     void testMassModifyForConflicts()
0576     {
0577         QFETCH(Akonadi::Item, item);
0578         QFETCH(bool, waitForPreviousJob);
0579         QFETCH(int, numberOfModifications);
0580         mDiscardedEqualsSuccess = true;
0581 
0582         Q_ASSERT(numberOfModifications > 0);
0583 
0584         if (!waitForPreviousJob) {
0585             mIncidencesToModify = numberOfModifications;
0586         }
0587 
0588         int changeId = -1;
0589         for (int i = 0; i < numberOfModifications; ++i) {
0590             auto incidence = item.payload<KCalendarCore::Incidence::Ptr>();
0591             Q_ASSERT(incidence);
0592             incidence->setSummary(QString::number(i));
0593             changeId = mChanger->modifyIncidence(item);
0594             QVERIFY(changeId != -1);
0595 
0596             if (waitForPreviousJob) {
0597                 mIncidencesToModify = 1;
0598                 mExpectedResultByChangeId.insert(changeId, IncidenceChanger::ResultCodeSuccess);
0599                 waitForSignals();
0600                 auto fetchJob = new ItemFetchJob(item, this);
0601                 fetchJob->fetchScope().fetchFullPayload();
0602                 AKVERIFYEXEC(fetchJob);
0603                 QVERIFY(fetchJob->items().count() == 1);
0604                 QCOMPARE(fetchJob->items().first().payload<KCalendarCore::Incidence::Ptr>()->summary(), QString::number(i));
0605             }
0606         }
0607 
0608         if (!waitForPreviousJob) {
0609             mIncidencesToModify = numberOfModifications;
0610             // Wait for the last one
0611             mExpectedResultByChangeId.insert(changeId, IncidenceChanger::ResultCodeSuccess);
0612             waitForChange(changeId);
0613             auto fetchJob = new ItemFetchJob(item, this);
0614             fetchJob->fetchScope().fetchFullPayload();
0615             AKVERIFYEXEC(fetchJob);
0616             QVERIFY(fetchJob->items().count() == 1);
0617             QCOMPARE(fetchJob->items().first().payload<KCalendarCore::Incidence::Ptr>()->summary(), QString::number(numberOfModifications - 1));
0618             if (mIncidencesToModify > 0) {
0619                 waitForSignals();
0620             }
0621         }
0622 
0623         mDiscardedEqualsSuccess = false;
0624     }
0625 
0626     void testAtomicOperations_data()
0627     {
0628         QTest::addColumn<Akonadi::Item::List>("items");
0629         QTest::addColumn<QList<Akonadi::IncidenceChanger::ChangeType>>("changeTypes");
0630         QTest::addColumn<QList<bool>>("failureExpectedList");
0631         QTest::addColumn<QList<Akonadi::IncidenceChanger::ResultCode>>("expectedResults");
0632         QTest::addColumn<QList<Akonadi::Collection::Rights>>("rights");
0633         QTest::addColumn<bool>("permissionsOrRollback");
0634 
0635         Akonadi::Item::List items;
0636         QList<Akonadi::IncidenceChanger::ChangeType> changeTypes;
0637         QList<bool> failureExpectedList;
0638         QList<Akonadi::IncidenceChanger::ResultCode> expectedResults;
0639         QList<Akonadi::Collection::Rights> rights;
0640         const Collection::Rights allRights = Collection::AllRights;
0641         const Collection::Rights noRights = Collection::Rights();
0642         //------------------------------------------------------------------------------------------
0643         // Create two incidences, should succeed
0644         items << item() << item();
0645         changeTypes << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeCreate;
0646         failureExpectedList << false << false;
0647         expectedResults << IncidenceChanger::ResultCodeSuccess << IncidenceChanger::ResultCodeSuccess;
0648         rights << allRights << allRights;
0649 
0650         QTest::newRow("create two - success ") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0651         //------------------------------------------------------------------------------------------
0652         changeTypes.clear();
0653         changeTypes << IncidenceChanger::ChangeTypeModify << IncidenceChanger::ChangeTypeModify;
0654         items.clear();
0655         items << createItem(mCollection) << createItem(mCollection);
0656 
0657         QTest::newRow("modify two - success ") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0658         //------------------------------------------------------------------------------------------
0659         changeTypes.clear();
0660         changeTypes << IncidenceChanger::ChangeTypeDelete << IncidenceChanger::ChangeTypeDelete;
0661         QTest::newRow("delete two - success ") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0662         //------------------------------------------------------------------------------------------
0663         // Creation succeeds but deletion doesn't ( invalid item case )
0664         items.clear();
0665         items << item() << Item(); // Invalid item on purpose
0666         changeTypes.clear();
0667         changeTypes << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeDelete;
0668         failureExpectedList.clear();
0669         failureExpectedList << false << true;
0670         expectedResults.clear();
0671         expectedResults << IncidenceChanger::ResultCodeRolledback << IncidenceChanger::ResultCodeRolledback;
0672         rights.clear();
0673         rights << allRights << allRights;
0674 
0675         QTest::newRow("create,try delete") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0676         //------------------------------------------------------------------------------------------
0677         // deletion doesn't succeed, but creation does ( invalid item case )
0678         items.clear();
0679         items << Item() << item(); // Invalid item on purpose
0680         changeTypes.clear();
0681         changeTypes << IncidenceChanger::ChangeTypeDelete << IncidenceChanger::ChangeTypeCreate;
0682         failureExpectedList.clear();
0683         failureExpectedList << true << false;
0684         expectedResults.clear();
0685         expectedResults << IncidenceChanger::ResultCodeRolledback << IncidenceChanger::ResultCodeRolledback;
0686         rights.clear();
0687         rights << allRights << allRights;
0688 
0689         QTest::newRow("try delete,create") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0690         //------------------------------------------------------------------------------------------
0691         // Creation succeeds but deletion doesn't ( valid, inexistent item case )
0692         items.clear();
0693         items << item() << Item(10101010);
0694         changeTypes.clear();
0695         changeTypes << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeDelete;
0696         failureExpectedList.clear();
0697         failureExpectedList << false << false;
0698         expectedResults.clear();
0699         expectedResults << IncidenceChanger::ResultCodeRolledback << IncidenceChanger::ResultCodeJobError;
0700         rights.clear();
0701         rights << allRights << allRights;
0702 
0703         QTest::newRow("create,try delete v2") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0704         //------------------------------------------------------------------------------------------
0705         // deletion doesn't succeed, but creation does ( valid, inexistent item case )
0706         items.clear();
0707         items << Item(10101010) << item();
0708         changeTypes.clear();
0709         changeTypes << IncidenceChanger::ChangeTypeDelete << IncidenceChanger::ChangeTypeCreate;
0710         failureExpectedList.clear();
0711         failureExpectedList << false << false;
0712         expectedResults.clear();
0713         expectedResults << IncidenceChanger::ResultCodeJobError << IncidenceChanger::ResultCodeRolledback;
0714         rights.clear();
0715         rights << allRights << allRights;
0716 
0717         QTest::newRow("try delete,create v2") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0718         //------------------------------------------------------------------------------------------
0719         // deletion doesn't succeed, but creation does ( NO ACL case )
0720         items.clear();
0721         items << createItem(mCollection) << item();
0722         changeTypes.clear();
0723         changeTypes << IncidenceChanger::ChangeTypeDelete << IncidenceChanger::ChangeTypeCreate;
0724         failureExpectedList.clear();
0725         failureExpectedList << false << false;
0726         expectedResults.clear();
0727         expectedResults << IncidenceChanger::ResultCodePermissions << IncidenceChanger::ResultCodeRolledback;
0728         rights.clear();
0729         rights << noRights << allRights;
0730 
0731         QTest::newRow("try delete(ACL),create") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0732         //------------------------------------------------------------------------------------------
0733         // Creation succeeds but deletion doesn't ( NO ACL case )
0734         items.clear();
0735         items << item() << createItem(mCollection);
0736         changeTypes.clear();
0737         changeTypes << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeDelete;
0738         failureExpectedList.clear();
0739         failureExpectedList << false << false;
0740         expectedResults.clear();
0741         expectedResults << IncidenceChanger::ResultCodeRolledback << IncidenceChanger::ResultCodePermissions;
0742         rights.clear();
0743         rights << allRights << noRights;
0744 
0745         QTest::newRow("create,try delete(ACL)") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0746         //------------------------------------------------------------------------------------------
0747         // 1 successful modification, 1 failed creation
0748         changeTypes.clear();
0749         changeTypes << IncidenceChanger::ChangeTypeModify << IncidenceChanger::ChangeTypeCreate;
0750         items.clear();
0751         items << createItem(mCollection) << Item();
0752         failureExpectedList.clear();
0753         failureExpectedList << false << true;
0754         expectedResults.clear();
0755         expectedResults << IncidenceChanger::ResultCodeRolledback << IncidenceChanger::ResultCodeRolledback;
0756         rights.clear();
0757         rights << allRights << allRights;
0758 
0759         QTest::newRow("modify,try create") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0760         //------------------------------------------------------------------------------------------
0761         // 1 successful modification, 1 failed creation
0762         changeTypes.clear();
0763         changeTypes << IncidenceChanger::ChangeTypeModify << IncidenceChanger::ChangeTypeCreate;
0764         items.clear();
0765         items << createItem(mCollection) << item();
0766         failureExpectedList.clear();
0767         failureExpectedList << false << false;
0768         expectedResults.clear();
0769         expectedResults << IncidenceChanger::ResultCodeRolledback << IncidenceChanger::ResultCodePermissions;
0770         rights.clear();
0771         rights << allRights << noRights;
0772 
0773         QTest::newRow("modify,try create v2") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0774         //------------------------------------------------------------------------------------------
0775         // 1 failed creation, 1 successful modification
0776         changeTypes.clear();
0777         changeTypes << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeModify;
0778         items.clear();
0779         items << Item() << createItem(mCollection);
0780         failureExpectedList.clear();
0781         failureExpectedList << true << false;
0782         expectedResults.clear();
0783         expectedResults << IncidenceChanger::ResultCodeRolledback << IncidenceChanger::ResultCodeRolledback;
0784         rights.clear();
0785         rights << allRights << allRights;
0786 
0787         QTest::newRow("try create,modify") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0788         //------------------------------------------------------------------------------------------
0789         // 1 failed creation, 1 successful modification
0790         changeTypes.clear();
0791         changeTypes << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeModify;
0792         items.clear();
0793         items << item() << createItem(mCollection);
0794         failureExpectedList.clear();
0795         failureExpectedList << false << false;
0796         expectedResults.clear();
0797         expectedResults << IncidenceChanger::ResultCodePermissions << IncidenceChanger::ResultCodeRolledback;
0798         rights.clear();
0799         rights << noRights << allRights;
0800 
0801         QTest::newRow("try create,modify v2") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0802         //------------------------------------------------------------------------------------------
0803         // 4 creations, last one fails
0804         changeTypes.clear();
0805         changeTypes << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeCreate
0806                     << IncidenceChanger::ChangeTypeCreate;
0807         items.clear();
0808         items << item() << item() << item() << item();
0809         failureExpectedList.clear();
0810         failureExpectedList << false << false << false << false;
0811         expectedResults.clear();
0812         expectedResults << IncidenceChanger::ResultCodeRolledback << IncidenceChanger::ResultCodeRolledback << IncidenceChanger::ResultCodeRolledback
0813                         << IncidenceChanger::ResultCodePermissions;
0814         rights.clear();
0815         rights << allRights << allRights << allRights << noRights;
0816 
0817         QTest::newRow("create 4, last fails") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0818         //------------------------------------------------------------------------------------------
0819         // 4 creations, first one fails
0820         changeTypes.clear();
0821         changeTypes << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeCreate
0822                     << IncidenceChanger::ChangeTypeCreate;
0823         items.clear();
0824         items << item() << item() << item() << item();
0825         failureExpectedList.clear();
0826         failureExpectedList << false << false << false << false;
0827         expectedResults.clear();
0828         expectedResults << IncidenceChanger::ResultCodePermissions << IncidenceChanger::ResultCodeRolledback << IncidenceChanger::ResultCodeRolledback
0829                         << IncidenceChanger::ResultCodeRolledback;
0830         rights.clear();
0831         rights << noRights << allRights << allRights << allRights;
0832 
0833         QTest::newRow("create 4, first fails") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0834         //------------------------------------------------------------------------------------------
0835         // 4 creations, second one fails
0836         changeTypes.clear();
0837         changeTypes << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeCreate
0838                     << IncidenceChanger::ChangeTypeCreate;
0839         items.clear();
0840         items << item() << item() << item() << item();
0841         failureExpectedList.clear();
0842         failureExpectedList << false << false << false << false;
0843         expectedResults.clear();
0844         expectedResults << IncidenceChanger::ResultCodeRolledback << IncidenceChanger::ResultCodePermissions << IncidenceChanger::ResultCodeRolledback
0845                         << IncidenceChanger::ResultCodeRolledback;
0846         rights.clear();
0847         rights << allRights << noRights << allRights << allRights;
0848 
0849         QTest::newRow("create 4, second fails") << items << changeTypes << failureExpectedList << expectedResults << rights << false;
0850         //------------------------------------------------------------------------------------------
0851         // 4 fails
0852         changeTypes.clear();
0853         changeTypes << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeCreate << IncidenceChanger::ChangeTypeCreate
0854                     << IncidenceChanger::ChangeTypeCreate;
0855         items.clear();
0856         items << item() << item() << item() << item();
0857         failureExpectedList.clear();
0858         failureExpectedList << false << false << false << false;
0859         expectedResults.clear();
0860         expectedResults << IncidenceChanger::ResultCodePermissions << IncidenceChanger::ResultCodePermissions << IncidenceChanger::ResultCodePermissions
0861                         << IncidenceChanger::ResultCodePermissions;
0862         rights.clear();
0863         rights << noRights << noRights << noRights << noRights;
0864 
0865         QTest::newRow("create 4, all fail") << items << changeTypes << failureExpectedList << expectedResults << rights << true;
0866         //------------------------------------------------------------------------------------------
0867         //------------------------------------------------------------------------------------------
0868         //------------------------------------------------------------------------------------------
0869         //------------------------------------------------------------------------------------------
0870     }
0871 
0872     void testAtomicOperations()
0873     {
0874         QFETCH(Akonadi::Item::List, items);
0875         QFETCH(QList<Akonadi::IncidenceChanger::ChangeType>, changeTypes);
0876         QFETCH(QList<bool>, failureExpectedList);
0877         QFETCH(QList<Akonadi::IncidenceChanger::ResultCode>, expectedResults);
0878         QFETCH(QList<Akonadi::Collection::Rights>, rights);
0879         QFETCH(bool, permissionsOrRollback);
0880 
0881         QCOMPARE(items.count(), changeTypes.count());
0882         QCOMPARE(items.count(), failureExpectedList.count());
0883         QCOMPARE(items.count(), expectedResults.count());
0884         QCOMPARE(items.count(), rights.count());
0885 
0886         mPermissionsOrRollback = permissionsOrRollback;
0887         mChanger->setDefaultCollection(mCollection);
0888         mChanger->setRespectsCollectionRights(true);
0889         mChanger->setDestinationPolicy(IncidenceChanger::DestinationPolicyNeverAsk);
0890         mChanger->startAtomicOperation();
0891         mIncidencesToAdd = 0;
0892         mIncidencesToModify = 0;
0893         mIncidencesToDelete = 0;
0894         for (int i = 0; i < items.count(); ++i) {
0895             mCollection.setRights(rights[i]);
0896             mChanger->setDefaultCollection(mCollection);
0897             const Akonadi::Item item = items[i];
0898             int changeId = -1;
0899             switch (changeTypes[i]) {
0900             case IncidenceChanger::ChangeTypeCreate:
0901                 changeId = mChanger->createIncidence(item.hasPayload() ? item.payload<KCalendarCore::Incidence::Ptr>() : Incidence::Ptr());
0902                 if (changeId != -1) {
0903                     ++mIncidencesToAdd;
0904                 }
0905                 break;
0906             case IncidenceChanger::ChangeTypeDelete:
0907                 changeId = mChanger->deleteIncidence(item);
0908                 if (changeId != -1) {
0909                     ++mIncidencesToDelete;
0910                 }
0911                 break;
0912             case IncidenceChanger::ChangeTypeModify:
0913                 QVERIFY(item.isValid());
0914                 QVERIFY(item.hasPayload<KCalendarCore::Incidence::Ptr>());
0915                 item.payload<KCalendarCore::Incidence::Ptr>()->setSummary(QStringLiteral("Changed"));
0916                 changeId = mChanger->modifyIncidence(item);
0917                 if (changeId != -1) {
0918                     ++mIncidencesToModify;
0919                 }
0920                 break;
0921             default:
0922                 QVERIFY(false);
0923             }
0924             QVERIFY(!((changeId == -1) ^ failureExpectedList[i]));
0925             if (changeId != -1) {
0926                 mExpectedResultByChangeId.insert(changeId, expectedResults[i]);
0927             }
0928         }
0929         mChanger->endAtomicOperation();
0930 
0931         waitForSignals();
0932 
0933         // Validate:
0934         for (int i = 0; i < items.count(); ++i) {
0935             const bool expectedSuccess = (expectedResults[i] == IncidenceChanger::ResultCodeSuccess);
0936             mCollection.setRights(rights[i]);
0937 
0938             Akonadi::Item item = items[i];
0939 
0940             switch (changeTypes[i]) {
0941             case IncidenceChanger::ChangeTypeCreate:
0942                 if (expectedSuccess) {
0943                     QVERIFY(item.hasPayload<KCalendarCore::Incidence::Ptr>());
0944                     auto incidence = item.payload<KCalendarCore::Incidence::Ptr>();
0945                     QVERIFY(incidence);
0946                     QVERIFY(!incidence->uid().isEmpty());
0947                     QVERIFY(mItemIdByUid.contains(incidence->uid()));
0948                     auto fJob = new ItemFetchJob(Item(mItemIdByUid.value(incidence->uid())));
0949                     fJob->fetchScope().fetchFullPayload();
0950                     AKVERIFYEXEC(fJob);
0951                     QCOMPARE(fJob->items().count(), 1);
0952                     QVERIFY(fJob->items().constFirst().isValid());
0953                     QVERIFY(fJob->items().constFirst().hasPayload());
0954                     QVERIFY(fJob->items().constFirst().hasPayload<KCalendarCore::Incidence::Ptr>());
0955                     QCOMPARE(item.payload<KCalendarCore::Incidence::Ptr>()->uid(), fJob->items().constFirst().payload<KCalendarCore::Incidence::Ptr>()->uid());
0956                 }
0957                 break;
0958             case IncidenceChanger::ChangeTypeDelete:
0959                 if (expectedSuccess) {
0960                     auto fJob = new ItemFetchJob(Item(item.id()));
0961                     QVERIFY(!fJob->exec());
0962                 }
0963                 break;
0964             case IncidenceChanger::ChangeTypeModify:
0965                 if (expectedSuccess) {
0966                     auto fJob = new ItemFetchJob(Item(item.id()));
0967                     fJob->fetchScope().fetchFullPayload();
0968                     AKVERIFYEXEC(fJob);
0969                     QCOMPARE(item.payload<KCalendarCore::Incidence::Ptr>()->summary(),
0970                              fJob->items().first().payload<KCalendarCore::Incidence::Ptr>()->summary());
0971                 }
0972                 break;
0973             default:
0974                 QVERIFY(false);
0975             }
0976         }
0977         mPermissionsOrRollback = false;
0978     }
0979 
0980     void testAdjustRecurrence_data()
0981     {
0982         QTest::addColumn<bool>("allDay");
0983         QTest::addColumn<QDateTime>("dtStart");
0984         QTest::addColumn<QDateTime>("dtEnd");
0985         QTest::addColumn<int>("offsetToMove");
0986         QTest::addColumn<int>("frequency");
0987         QTest::addColumn<KCalendarCore::RecurrenceRule::PeriodType>("recurrenceType");
0988 
0989         // For weekly recurrences
0990         QTest::addColumn<QBitArray>("weekDays");
0991         QTest::addColumn<QBitArray>("expectedWeekDays");
0992 
0993         // Recurrence end
0994         QTest::addColumn<QDate>("recurrenceEnd");
0995         QTest::addColumn<QDate>("expectedRecurrenceEnd");
0996 
0997         const QDateTime dtStart = QDateTime(QDate::currentDate(), QTime(8, 0));
0998         const QDateTime dtEnd = dtStart.addSecs(3600);
0999         const int one_day = 3600 * 24;
1000         const int one_hour = 3600;
1001         QBitArray days(7);
1002         QBitArray expectedDays(7);
1003 
1004         //-------------------------------------------------------------------------
1005         days.setBit(dtStart.date().dayOfWeek() - 1);
1006         expectedDays.setBit(dtStart.addSecs(one_day).date().dayOfWeek() - 1);
1007 
1008         QTest::newRow("weekly") << false << dtStart << dtEnd << one_day << 1 << KCalendarCore::RecurrenceRule::rWeekly << days << expectedDays << QDate()
1009                                 << QDate();
1010         //-------------------------------------------------------------------------
1011         days.fill(false);
1012         days.setBit(dtStart.date().dayOfWeek() - 1);
1013         expectedDays.setBit(dtStart.addSecs(one_day).date().dayOfWeek() - 1);
1014         QTest::newRow("weekly allday") << true << QDateTime(dtStart.date().startOfDay()) << QDateTime(dtEnd.date().startOfDay()) << one_day << 1
1015                                        << KCalendarCore::RecurrenceRule::rWeekly << days << expectedDays << QDate() << QDate();
1016         //-------------------------------------------------------------------------
1017         // Here nothing should change
1018         days.fill(false);
1019         days.setBit(dtStart.date().dayOfWeek() - 1);
1020 
1021         QTest::newRow("weekly nop") << false << dtStart << dtEnd << one_hour << 1 << KCalendarCore::RecurrenceRule::rWeekly << days << days << QDate()
1022                                     << QDate();
1023         //-------------------------------------------------------------------------
1024         // Test with multiple week days. Only the weekday from the old DTSTART should be unset.
1025         days.fill(true);
1026         expectedDays = days;
1027         expectedDays.clearBit(dtStart.date().dayOfWeek() - 1);
1028         QTest::newRow("weekly multiple") << false << dtStart << dtEnd << one_day << 1 << KCalendarCore::RecurrenceRule::rWeekly << days << expectedDays
1029                                          << QDate() << QDate();
1030         //-------------------------------------------------------------------------
1031         // Testing moving an event such that DTSTART > recurrence end, which would
1032         // result in the event disappearing from all views.
1033         QTest::newRow("recur end") << false << dtStart << dtEnd << one_day * 7 << 1 << KCalendarCore::RecurrenceRule::rDaily << QBitArray() << QBitArray()
1034                                    << dtStart.date().addDays(3) << QDate();
1035         //-------------------------------------------------------------------------
1036         mCollection.setRights(Collection::Rights(Collection::AllRights));
1037     }
1038 
1039     void testAdjustRecurrence()
1040     {
1041         QFETCH(bool, allDay);
1042         QFETCH(QDateTime, dtStart);
1043         QFETCH(QDateTime, dtEnd);
1044         QFETCH(int, offsetToMove);
1045         QFETCH(int, frequency);
1046         QFETCH(KCalendarCore::RecurrenceRule::PeriodType, recurrenceType);
1047         QFETCH(QBitArray, weekDays);
1048         QFETCH(QBitArray, expectedWeekDays);
1049         QFETCH(QDate, recurrenceEnd);
1050         QFETCH(QDate, expectedRecurrenceEnd);
1051 
1052         Event::Ptr incidence = Event::Ptr(new Event());
1053         incidence->setSummary(QStringLiteral("random summary"));
1054         incidence->setDtStart(dtStart);
1055         incidence->setDtEnd(dtEnd);
1056         incidence->setAllDay(allDay);
1057 
1058         Recurrence *recurrence = incidence->recurrence();
1059 
1060         switch (recurrenceType) {
1061         case KCalendarCore::RecurrenceRule::rDaily:
1062             recurrence->setDaily(frequency);
1063             break;
1064         case KCalendarCore::RecurrenceRule::rWeekly:
1065             recurrence->setWeekly(frequency, weekDays);
1066             break;
1067         default:
1068             // Not tested yet
1069             Q_ASSERT(false);
1070             QVERIFY(false);
1071         }
1072 
1073         if (recurrenceEnd.isValid()) {
1074             recurrence->setEndDate(recurrenceEnd);
1075         }
1076 
1077         // Create the recurring incidence
1078         int changeId = mChanger->createIncidence(incidence, mCollection);
1079         QVERIFY(changeId != -1);
1080         mIncidencesToAdd = 1;
1081         mExpectedResultByChangeId.insert(changeId, IncidenceChanger::ResultCodeSuccess);
1082         waitForSignals();
1083 
1084         // Now lets move it
1085         Incidence::Ptr originalIncidence = Incidence::Ptr(incidence->clone());
1086         incidence->setDtStart(dtStart.addSecs(offsetToMove));
1087 
1088         incidence->setDtEnd(dtEnd.addSecs(offsetToMove));
1089         mLastItemCreated.setPayload(incidence);
1090         changeId = mChanger->modifyIncidence(mLastItemCreated, originalIncidence);
1091         QVERIFY(changeId != -1);
1092         mIncidencesToModify = 1;
1093         mExpectedResultByChangeId.insert(changeId, IncidenceChanger::ResultCodeSuccess);
1094         waitForSignals();
1095 
1096         // Now check the results
1097         switch (recurrenceType) {
1098         case KCalendarCore::RecurrenceRule::rDaily:
1099             break;
1100         case KCalendarCore::RecurrenceRule::rWeekly:
1101             QCOMPARE(incidence->recurrence()->days(), expectedWeekDays);
1102             if (weekDays != expectedWeekDays) {
1103                 QVERIFY(incidence->dirtyFields().contains(IncidenceBase::FieldRecurrence));
1104             }
1105             break;
1106         default:
1107             // Not tested yet
1108             Q_ASSERT(false);
1109             QVERIFY(false);
1110         }
1111 
1112         if (recurrenceEnd.isValid() && !expectedRecurrenceEnd.isValid()) {
1113             QVERIFY(incidence->recurrence()->endDate() >= incidence->dtStart().date());
1114             QVERIFY(incidence->dirtyFields().contains(IncidenceBase::FieldRecurrence));
1115         }
1116     }
1117 
1118 public Q_SLOTS:
1119 
1120     void waitForSignals()
1121     {
1122         QTestEventLoop::instance().enterLoop(10);
1123 
1124         if (QTestEventLoop::instance().timeout()) {
1125             qDebug() << "Remaining: " << mIncidencesToAdd << mIncidencesToDelete << mIncidencesToModify;
1126         }
1127         QVERIFY(!QTestEventLoop::instance().timeout());
1128     }
1129 
1130     // Waits for a specific change
1131     void waitForChange(int changeId)
1132     {
1133         mChangeToWaitFor = changeId;
1134 
1135         int i = 0;
1136         while (mChangeToWaitFor != -1 && i++ < 10) { // wait 10 seconds max.
1137             QTest::qWait(100);
1138         }
1139 
1140         QVERIFY(mChangeToWaitFor == -1);
1141     }
1142 
1143     void deleteFinished(int changeId, const QList<Akonadi::Item::Id> &deletedIds, Akonadi::IncidenceChanger::ResultCode resultCode, const QString &errorMessage)
1144     {
1145         QVERIFY(changeId != -1);
1146         mChangeToWaitFor = -1;
1147         --mIncidencesToDelete;
1148 
1149         if (resultCode != IncidenceChanger::ResultCodeSuccess) {
1150             qDebug() << "Error string is " << errorMessage;
1151         } else {
1152             QVERIFY(!deletedIds.isEmpty());
1153             for (Akonadi::Item::Id id : std::as_const(deletedIds)) {
1154                 QVERIFY(id != -1);
1155             }
1156         }
1157 
1158         compareExpectedResult(resultCode, mExpectedResultByChangeId[changeId], QStringLiteral("createFinished"));
1159 
1160         maybeQuitEventLoop();
1161     }
1162 
1163     void createFinished(int changeId, const Akonadi::Item &item, Akonadi::IncidenceChanger::ResultCode resultCode, const QString &errorString)
1164     {
1165         QVERIFY(changeId != -1);
1166         mChangeToWaitFor = -1;
1167         --mIncidencesToAdd;
1168 
1169         if (resultCode == IncidenceChanger::ResultCodeSuccess) {
1170             QVERIFY(item.isValid());
1171             QVERIFY(item.parentCollection().isValid());
1172             mItemIdByChangeId.insert(changeId, item.id());
1173             QVERIFY(item.hasPayload());
1174             auto incidence = item.payload<KCalendarCore::Incidence::Ptr>();
1175             mItemIdByUid.insert(incidence->uid(), item.id());
1176             mLastItemCreated = item;
1177         } else {
1178             qDebug() << "Error string is " << errorString;
1179         }
1180 
1181         compareExpectedResult(resultCode, mExpectedResultByChangeId[changeId], QStringLiteral("createFinished"));
1182 
1183         qDebug() << "Createfinished " << mIncidencesToAdd;
1184         maybeQuitEventLoop();
1185     }
1186 
1187     void modifyFinished(int changeId, const Akonadi::Item &item, Akonadi::IncidenceChanger::ResultCode resultCode, const QString &errorString)
1188     {
1189         mChangeToWaitFor = -1;
1190         --mIncidencesToModify;
1191         QVERIFY(changeId != -1);
1192 
1193         if (resultCode == IncidenceChanger::ResultCodeSuccess) {
1194             QVERIFY(item.isValid());
1195         } else {
1196             qDebug() << "Error string is " << errorString;
1197         }
1198 
1199         compareExpectedResult(resultCode, mExpectedResultByChangeId[changeId], QStringLiteral("modifyFinished"));
1200 
1201         maybeQuitEventLoop();
1202     }
1203 
1204     void maybeQuitEventLoop()
1205     {
1206         if (mIncidencesToDelete == 0 && mIncidencesToAdd == 0 && mIncidencesToModify == 0) {
1207             QTestEventLoop::instance().exitLoop();
1208         }
1209     }
1210 
1211     void testDefaultCollection()
1212     {
1213         const Collection newCollection(42);
1214         IncidenceChanger changer;
1215         QCOMPARE(changer.defaultCollection(), Collection());
1216         changer.setDefaultCollection(newCollection);
1217         QCOMPARE(changer.defaultCollection(), newCollection);
1218     }
1219 
1220     void testDestinationPolicy()
1221     {
1222         IncidenceChanger changer;
1223         QCOMPARE(changer.destinationPolicy(), IncidenceChanger::DestinationPolicyDefault);
1224         changer.setDestinationPolicy(IncidenceChanger::DestinationPolicyNeverAsk);
1225         QCOMPARE(changer.destinationPolicy(), IncidenceChanger::DestinationPolicyNeverAsk);
1226     }
1227 
1228     void testDialogsOnError()
1229     {
1230         IncidenceChanger changer;
1231         QCOMPARE(changer.showDialogsOnError(), true);
1232         changer.setShowDialogsOnError(false);
1233         QCOMPARE(changer.showDialogsOnError(), false);
1234     }
1235 
1236     void testRespectsCollectionRights()
1237     {
1238         IncidenceChanger changer;
1239         QCOMPARE(changer.respectsCollectionRights(), true);
1240         changer.setRespectsCollectionRights(false);
1241         QCOMPARE(changer.respectsCollectionRights(), false);
1242     }
1243 
1244     void compareExpectedResult(IncidenceChanger::ResultCode result, IncidenceChanger::ResultCode expected, const QString &str)
1245     {
1246         if (mPermissionsOrRollback) {
1247             if (expected == IncidenceChanger::ResultCodePermissions) {
1248                 expected = IncidenceChanger::ResultCodeRolledback;
1249             }
1250 
1251             if (result == IncidenceChanger::ResultCodePermissions) {
1252                 result = IncidenceChanger::ResultCodeRolledback;
1253             }
1254         }
1255 
1256         if (mDiscardedEqualsSuccess) {
1257             if (expected == IncidenceChanger::ResultCodeModificationDiscarded) {
1258                 expected = IncidenceChanger::ResultCodeSuccess;
1259             }
1260 
1261             if (result == IncidenceChanger::ResultCodeModificationDiscarded) {
1262                 result = IncidenceChanger::ResultCodeSuccess;
1263             }
1264         }
1265 
1266         if (result != expected) {
1267             qDebug() << str << "Expected " << expected << " got " << result;
1268         }
1269 
1270         QCOMPARE(result, expected);
1271     }
1272 };
1273 
1274 QTEST_AKONADIMAIN(IncidenceChangerTest)
1275 
1276 #include "incidencechangertest.moc"