File indexing completed on 2025-01-05 04:55:02

0001 
0002 #include <QTest>
0003 #include <QDebug>
0004 #include <QStandardItemModel>
0005 #include <QSignalSpy>
0006 #include <sink/test.h>
0007 #include <sink/store.h>
0008 #include <sink/resourcecontrol.h>
0009 #include <KCalendarCore/Event>
0010 #include <KCalendarCore/ICalFormat>
0011 
0012 #include "eventoccurrencemodel.h"
0013 #include "multidayeventmodel.h"
0014 #include "perioddayeventmodel.h"
0015 
0016 class EventOccurrenceModelTest : public QObject
0017 {
0018     Q_OBJECT
0019 private slots:
0020 
0021     void initTestCase()
0022     {
0023         Sink::Test::initTest();
0024     }
0025 
0026     void testEventOccurrenceModel()
0027     {
0028         Sink::ApplicationDomain::DummyResource::create("account1");
0029 
0030         using namespace Sink::ApplicationDomain;
0031         auto account = ApplicationDomainType::createEntity<SinkAccount>();
0032         Sink::Store::create(account).exec().waitForFinished();
0033 
0034         auto resource = Sink::ApplicationDomain::DummyResource::create(account.identifier());
0035         Sink::Store::create(resource).exec().waitForFinished();
0036 
0037         auto calendar1 = ApplicationDomainType::createEntity<Calendar>(resource.identifier());
0038         Sink::Store::create(calendar1).exec().waitForFinished();
0039 
0040         const QDateTime start{{2018, 04, 17}, {6, 0, 0}};
0041         {
0042             auto event1 = ApplicationDomainType::createEntity<Event>(resource.identifier());
0043             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0044             calcoreEvent->setUid("event1");
0045             calcoreEvent->setSummary("summary1");
0046             calcoreEvent->setDescription("description");
0047             calcoreEvent->setDtStart(start);
0048             calcoreEvent->setDuration(3600);
0049             calcoreEvent->setAllDay(false);
0050             event1.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0051             event1.setCalendar(calendar1);
0052             Sink::Store::create(event1).exec().waitForFinished();
0053         }
0054         {
0055             //1st indent level
0056             auto event2 = ApplicationDomainType::createEntity<Event>(resource.identifier());
0057             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0058             calcoreEvent->setUid("event2");
0059             calcoreEvent->setSummary("summary2");
0060             calcoreEvent->setDescription("description");
0061             calcoreEvent->setDtStart(start.addDays(1));
0062             calcoreEvent->setDuration(3600);
0063             calcoreEvent->setAllDay(false);
0064             calcoreEvent->recurrence()->setDaily(1);
0065             event2.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0066             event2.setCalendar(calendar1);
0067             Sink::Store::create(event2).exec().waitForFinished();
0068         }
0069         {
0070             //2rd indent level
0071             auto event3 = ApplicationDomainType::createEntity<Event>(resource.identifier());
0072             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0073             calcoreEvent->setUid("event3");
0074             calcoreEvent->setSummary("summary3");
0075             calcoreEvent->setDtStart(start.addDays(1));
0076             calcoreEvent->setDuration(3600);
0077             calcoreEvent->setAllDay(false);
0078             event3.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0079             event3.setCalendar(calendar1);
0080             Sink::Store::create(event3).exec().waitForFinished();
0081         }
0082         {
0083             auto event4 = ApplicationDomainType::createEntity<Event>(resource.identifier());
0084             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0085             calcoreEvent->setUid("event4");
0086             calcoreEvent->setSummary("summary4");
0087             calcoreEvent->setDtStart(start.addDays(2));
0088             calcoreEvent->setAllDay(true);
0089             event4.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0090             event4.setCalendar(calendar1);
0091             Sink::Store::create(event4).exec().waitForFinished();
0092         }
0093         {
0094             //all day event 2 with duration of two days
0095             auto event4 = ApplicationDomainType::createEntity<Event>(resource.identifier());
0096             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0097             calcoreEvent->setUid("event4.1");
0098             calcoreEvent->setSummary("summary4.1");
0099             calcoreEvent->setDtStart(start.addDays(2));
0100             calcoreEvent->setDtEnd(start.addDays(3));
0101             calcoreEvent->setAllDay(true);
0102             event4.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0103             event4.setCalendar(calendar1);
0104             Sink::Store::create(event4).exec().waitForFinished();
0105         }
0106         {
0107             auto event1 = ApplicationDomainType::createEntity<Event>(resource.identifier());
0108             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0109             calcoreEvent->setUid("event5");
0110             calcoreEvent->setSummary("summary5");
0111             calcoreEvent->setDescription("description");
0112             calcoreEvent->setDtStart(start);
0113             calcoreEvent->setDuration(3600);
0114             calcoreEvent->setAllDay(false);
0115             event1.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0116             event1.setCalendar(calendar1);
0117             Sink::Store::create(event1).exec().waitForFinished();
0118         }
0119         {
0120             //3rd indent level
0121             auto event6 = ApplicationDomainType::createEntity<Event>(resource.identifier());
0122             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0123             calcoreEvent->setUid("event6");
0124             calcoreEvent->setSummary("summary6");
0125             calcoreEvent->setDtStart(start.addDays(1));
0126             calcoreEvent->setDuration(3600);
0127             calcoreEvent->setAllDay(false);
0128             event6.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0129             event6.setCalendar(calendar1);
0130             Sink::Store::create(event6).exec().waitForFinished();
0131         }
0132         {
0133             //Start matches end of previous event
0134             auto event7 = ApplicationDomainType::createEntity<Event>(resource.identifier());
0135             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0136             calcoreEvent->setUid("event7");
0137             calcoreEvent->setSummary("summary7");
0138             calcoreEvent->setDtStart(start.addDays(1).addSecs(3600));
0139             calcoreEvent->setDuration(3600);
0140             calcoreEvent->setAllDay(false);
0141             event7.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0142             event7.setCalendar(calendar1);
0143             Sink::Store::create(event7).exec().waitForFinished();
0144         }
0145 
0146         Sink::ResourceControl::flushMessageQueue(resource.identifier()).exec().waitForFinished();
0147 
0148         {
0149             const int expectedNumberOfOccurreces = 13;
0150             const int numberOfDays = 7;
0151             EventOccurrenceModel model;
0152 
0153             QSignalSpy initialItemsLoadedSpy(&model, &EventOccurrenceModel::initialItemsLoaded);
0154 
0155             model.setStart(start.date());
0156             model.setLength(numberOfDays);
0157             model.setCalendarFilter({calendar1.identifier()});
0158             QTRY_COMPARE(model.rowCount({}), expectedNumberOfOccurreces);
0159             QCOMPARE(initialItemsLoadedSpy.count(), 1);
0160 
0161             auto countEvents = [&] (const QVariantList &lines) {
0162                 int count = 0;
0163                 for (const auto &line : lines) {
0164                     count += line.toList().size();
0165                 }
0166                 return count;
0167             };
0168 
0169             //Check the multidayevent model
0170             {
0171                 MultiDayEventModel multiDayModel;
0172                 multiDayModel.setModel(&model);
0173                 QTRY_COMPARE(multiDayModel.rowCount({}), 1);
0174                 const auto lines = multiDayModel.index(0, 0, {}).data(multiDayModel.roleNames().key("events")).value<QVariantList>();
0175                 QTRY_COMPARE(countEvents(lines), expectedNumberOfOccurreces);
0176                 //We have 6 lines in the first week
0177                 QCOMPARE(lines.size(), 6);
0178                 QCOMPARE(lines[0].toList().size(), 1); //All day event
0179                 QCOMPARE(lines[0].toList()[0].toMap().value("duration").toInt(), 1);
0180                 QCOMPARE(lines[1].toList().size(), 1); //All day event
0181                 QCOMPARE(lines[1].toList()[0].toMap().value("duration").toInt(), 2);
0182                 QCOMPARE(lines[2].toList().size(), 7); //Recurring event summary2/summary3
0183                 QCOMPARE(lines[3].toList().size(), 2); //summary5/summary6
0184                 QCOMPARE(lines[4].toList().size(), 1); //summary2 FIXME why is it on a second row? because above summary3 moves in-between. Try to prefer existing events?
0185                 QCOMPARE(lines[4].toList()[0].toMap().value("text").toString(), "summary2");
0186                 QCOMPARE(lines[5].toList().size(), 1); //summary7
0187                 QCOMPARE(lines[5].toList()[0].toMap().value("text").toString(), "summary7");
0188             }
0189 
0190             {
0191                 PeriodDayEventModel multiDayModel;
0192                 multiDayModel.setModel(&model);
0193                 QTRY_COMPARE(multiDayModel.rowCount({}), numberOfDays);
0194                 {
0195                     const auto events = multiDayModel.index(0, 0, {}).data(multiDayModel.roleNames().key("events")).value<QVariantList>();
0196                     QCOMPARE(events.size(), 2);
0197                     QCOMPARE(events[0].toMap()["indentation"].toInt(), 0);
0198                     QCOMPARE(events[1].toMap()["indentation"].toInt(), 1);
0199                 }
0200                 {
0201                     const auto events = multiDayModel.index(1, 0, {}).data(multiDayModel.roleNames().key("events")).value<QVariantList>();
0202                     QCOMPARE(events.size(), 4);
0203                     QCOMPARE(events[0].toMap()["indentation"].toInt(), 0);
0204                     QCOMPARE(events[1].toMap()["indentation"].toInt(), 1);
0205                     QCOMPARE(events[2].toMap()["indentation"].toInt(), 2);
0206                     QCOMPARE(events[3].toMap()["indentation"].toInt(), 0);
0207                 }
0208                 {
0209                     const auto events = multiDayModel.index(2, 0, {}).data(multiDayModel.roleNames().key("events")).value<QVariantList>();
0210                     QCOMPARE(events.size(), 1);
0211                     QCOMPARE(events[0].toMap()["indentation"].toInt(), 0);
0212                 }
0213             }
0214 
0215             //Test modification
0216             {
0217                 const auto event = model.index(0, 0, {}).data(EventOccurrenceModel::Event).value<Sink::ApplicationDomain::Event::Ptr>();
0218                 auto calcoreEvent = KCalendarCore::ICalFormat().readIncidence(event->getIcal()).dynamicCast<KCalendarCore::Event>();
0219                 calcoreEvent->setSummary("modified");
0220 
0221                 event->setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0222 
0223                 QSignalSpy resetSpy(&model, &QAbstractItemModel::modelReset);
0224                 QSignalSpy rowsInsertedSpy(&model, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
0225                 QSignalSpy rowsRemovedSpy(&model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
0226                 QSignalSpy dataChangedSpy(&model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QVector<int> &)));
0227                 QSignalSpy initialItemsLoadedSpy(&model, &EventOccurrenceModel::initialItemsLoaded);
0228 
0229                 VERIFYEXEC(Sink::Store::modify(*event));
0230                 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue(resource.identifier()));
0231 
0232                 QTest::qWait(200);
0233                 QCOMPARE(resetSpy.count(), 0);
0234                 QCOMPARE(rowsRemovedSpy.count(), 0);
0235                 QCOMPARE(rowsInsertedSpy.count(), 0);
0236                 QCOMPARE(dataChangedSpy.count(), 1);
0237                 QCOMPARE(initialItemsLoadedSpy.count(), 0);
0238             }
0239 
0240             //Test an empty filter
0241             model.setCalendarFilter({});
0242             QTRY_COMPARE(model.rowCount({}), 0);
0243         }
0244     }
0245 
0246     void testMultiWeekEvents()
0247     {
0248         Sink::ApplicationDomain::DummyResource::create("account1");
0249 
0250         using namespace Sink::ApplicationDomain;
0251         auto account = ApplicationDomainType::createEntity<SinkAccount>();
0252         Sink::Store::create(account).exec().waitForFinished();
0253 
0254         auto resource = Sink::ApplicationDomain::DummyResource::create(account.identifier());
0255         Sink::Store::create(resource).exec().waitForFinished();
0256 
0257         auto calendar1 = ApplicationDomainType::createEntity<Calendar>(resource.identifier());
0258         Sink::Store::create(calendar1).exec().waitForFinished();
0259 
0260         const QDateTime start{{2018, 04, 17}, {6, 0, 0}};
0261         {
0262             //all day event 2 with duration of two days
0263             auto event4 = ApplicationDomainType::createEntity<Event>(resource.identifier());
0264             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0265             calcoreEvent->setUid("event1");
0266             calcoreEvent->setSummary("summary1");
0267             calcoreEvent->setDtStart(start.addDays(2));
0268             calcoreEvent->setDtEnd(start.addDays(9));
0269             calcoreEvent->setAllDay(true);
0270             event4.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0271             event4.setCalendar(calendar1);
0272             Sink::Store::create(event4).exec().waitForFinished();
0273         }
0274 
0275         Sink::ResourceControl::flushMessageQueue(resource.identifier()).exec().waitForFinished();
0276 
0277         {
0278             const int numberOfDays = 7 * 6;
0279             EventOccurrenceModel model;
0280 
0281             QSignalSpy initialItemsLoadedSpy(&model, &EventOccurrenceModel::initialItemsLoaded);
0282 
0283             model.setStart(start.date());
0284             model.setLength(numberOfDays);
0285             model.setCalendarFilter({calendar1.identifier()});
0286             //FIXME Reverse these tests?
0287             QTRY_COMPARE(model.rowCount({}), 1);
0288             QCOMPARE(initialItemsLoadedSpy.count(), 1);
0289 
0290             auto countEvents = [&] (const QVariantList &lines) {
0291                 int count = 0;
0292                 for (const auto &line : lines) {
0293                     count += line.toList().size();
0294                 }
0295                 return count;
0296             };
0297 
0298             //Check the multidayevent model
0299             {
0300                 MultiDayEventModel multiDayModel;
0301                 multiDayModel.setModel(&model);
0302                 QTRY_COMPARE(multiDayModel.rowCount({}), 6);
0303                 {
0304                     const auto lines = multiDayModel.index(0, 0, {}).data(multiDayModel.roleNames().key("events")).value<QVariantList>();
0305                     QCOMPARE(countEvents(lines), 1);
0306                     QCOMPARE(lines.size(), 1);
0307                     QCOMPARE(lines[0].toList().size(), 1); //All day event
0308                     QCOMPARE(lines[0].toList()[0].toMap().value("duration").toInt(), 5);
0309                 }
0310                 {
0311                     const auto lines = multiDayModel.index(1, 0, {}).data(multiDayModel.roleNames().key("events")).value<QVariantList>();
0312                     QCOMPARE(countEvents(lines), 1);
0313                     QCOMPARE(lines.size(), 1);
0314                     QCOMPARE(lines[0].toList().size(), 1); //All day event
0315                     QCOMPARE(lines[0].toList()[0].toMap().value("duration").toInt(), 3);
0316                 }
0317                 {
0318                     const auto lines = multiDayModel.index(2, 0, {}).data(multiDayModel.roleNames().key("events")).value<QVariantList>();
0319                     QCOMPARE(countEvents(lines), 0);
0320                     QCOMPARE(lines.size(), 0);
0321                 }
0322             }
0323         }
0324     }
0325 
0326     void testRecurrenceException()
0327     {
0328         Sink::ApplicationDomain::DummyResource::create("account1");
0329 
0330         using namespace Sink::ApplicationDomain;
0331         auto account = ApplicationDomainType::createEntity<SinkAccount>();
0332         Sink::Store::create(account).exec().waitForFinished();
0333 
0334         auto resource = Sink::ApplicationDomain::DummyResource::create(account.identifier());
0335         Sink::Store::create(resource).exec().waitForFinished();
0336 
0337         auto calendar1 = ApplicationDomainType::createEntity<Calendar>(resource.identifier());
0338         Sink::Store::create(calendar1).exec().waitForFinished();
0339 
0340         const QDateTime start{{2018, 04, 17}, {6, 0, 0}};
0341         {
0342             auto event = ApplicationDomainType::createEntity<Event>(resource.identifier());
0343             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0344             calcoreEvent->setUid("event");
0345             calcoreEvent->setSummary("summary2");
0346             calcoreEvent->setDescription("description");
0347             calcoreEvent->setDtStart(start.addDays(1));
0348             calcoreEvent->setDuration(3600);
0349             calcoreEvent->setAllDay(false);
0350             calcoreEvent->recurrence()->setDaily(1);
0351             event.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0352             event.setCalendar(calendar1);
0353             Sink::Store::create(event).exec().waitForFinished();
0354         }
0355 
0356         //Exception
0357         {
0358             auto event = ApplicationDomainType::createEntity<Event>(resource.identifier());
0359             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0360             calcoreEvent->setUid("event");
0361             calcoreEvent->setSummary("summary2");
0362             calcoreEvent->setDescription("description");
0363             calcoreEvent->setRecurrenceId(start.addDays(2));
0364             calcoreEvent->setDtStart(start.addDays(2).addSecs(3600));
0365             calcoreEvent->setDuration(7200);
0366             calcoreEvent->setAllDay(false);
0367             event.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0368             event.setCalendar(calendar1);
0369             Sink::Store::create(event).exec().waitForFinished();
0370         }
0371 
0372         Sink::ResourceControl::flushMessageQueue(resource.identifier()).exec().waitForFinished();
0373 
0374         {
0375             EventOccurrenceModel model;
0376             model.setStart(start.date());
0377             model.setLength(7);
0378             model.setCalendarFilter({calendar1.identifier()});
0379             QTRY_COMPARE(model.rowCount({}), 6);
0380 
0381             auto getOccurrence = [&] (int index) {
0382                 return model.index(index, 0, {}).data(EventOccurrenceModel::EventOccurrence).value<EventOccurrenceModel::Occurrence>();
0383             };
0384             QCOMPARE(getOccurrence(0).start, start.addDays(1));
0385             QCOMPARE(getOccurrence(1).start, start.addDays(2).addSecs(3600)); //The exception
0386             QCOMPARE(getOccurrence(2).start, start.addDays(3));
0387         }
0388     }
0389 
0390 
0391     //Original event on saturday, exception on monday next week
0392     void testRecurrenceException2()
0393     {
0394         Sink::ApplicationDomain::DummyResource::create("account1");
0395 
0396         using namespace Sink::ApplicationDomain;
0397         auto account = ApplicationDomainType::createEntity<SinkAccount>();
0398         Sink::Store::create(account).exec().waitForFinished();
0399 
0400         auto resource = Sink::ApplicationDomain::DummyResource::create(account.identifier());
0401         Sink::Store::create(resource).exec().waitForFinished();
0402 
0403         auto calendar1 = ApplicationDomainType::createEntity<Calendar>(resource.identifier());
0404         Sink::Store::create(calendar1).exec().waitForFinished();
0405 
0406         //Monthly recurrence
0407         {
0408             auto event = ApplicationDomainType::createEntity<Event>(resource.identifier());
0409             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0410             calcoreEvent->setUid("event");
0411             calcoreEvent->setSummary("summary2");
0412             calcoreEvent->setDescription("description");
0413             calcoreEvent->setDtStart(QDateTime{{2021, 3, 1}, {15, 0, 0}});
0414             calcoreEvent->setDuration(3600);
0415             calcoreEvent->setAllDay(false);
0416             calcoreEvent->recurrence()->setMonthly(1);
0417             event.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0418             event.setCalendar(calendar1);
0419             Sink::Store::create(event).exec().waitForFinished();
0420         }
0421 
0422         //Recurs on saturday, exception on the following monday
0423         {
0424             auto event = ApplicationDomainType::createEntity<Event>(resource.identifier());
0425             auto calcoreEvent = QSharedPointer<KCalendarCore::Event>::create();
0426             calcoreEvent->setUid("event");
0427             calcoreEvent->setSummary("summary2");
0428             calcoreEvent->setDescription("description");
0429             calcoreEvent->setRecurrenceId(QDateTime{{2021, 5, 1}, {15, 0, 0}});
0430             calcoreEvent->setDtStart(QDateTime{{2021, 5, 3}, {15, 0, 0}});
0431             calcoreEvent->setDuration(7200);
0432             calcoreEvent->setAllDay(false);
0433             event.setIcal(KCalendarCore::ICalFormat().toICalString(calcoreEvent).toUtf8());
0434             event.setCalendar(calendar1);
0435             Sink::Store::create(event).exec().waitForFinished();
0436         }
0437 
0438         Sink::ResourceControl::flushMessageQueue(resource.identifier()).exec().waitForFinished();
0439 
0440         //Old week (start on monday before)
0441         {
0442             EventOccurrenceModel model;
0443 
0444             model.setStart({2021, 4, 26});
0445             model.setLength(7);
0446             model.setCalendarFilter({calendar1.identifier()});
0447 
0448             //We don't have any signal when loading is done
0449             QTest::qWait(200);
0450 
0451             QCOMPARE(model.rowCount({}), 0);
0452         }
0453         //New week (start on monday after the occurrence)
0454         {
0455             EventOccurrenceModel model;
0456             model.setStart({2021, 5, 3});
0457             model.setLength(7);
0458             model.setCalendarFilter({calendar1.identifier()});
0459             QTRY_COMPARE(model.rowCount({}), 1);
0460 
0461             auto getOccurrence = [&] (int index) {
0462                 return model.index(index, 0, {}).data(EventOccurrenceModel::EventOccurrence).value<EventOccurrenceModel::Occurrence>();
0463             };
0464             QDateTime expected{{2021, 5, 3}, {15, 0, 0}};
0465             QCOMPARE(getOccurrence(0).start, expected);
0466         }
0467     }
0468 
0469 };
0470 
0471 QTEST_MAIN(EventOccurrenceModelTest)
0472 #include "eventoccurrencemodeltest.moc"