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"