File indexing completed on 2024-05-05 12:10:32
0001 /* 0002 This file is part of the kcalcore library. 0003 0004 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net> 0005 SPDX-FileContributor: Sergio Martins <sergio.martins@kdab.com> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "testicalformat.h" 0011 #include "event.h" 0012 #include "icalformat.h" 0013 #include "memorycalendar.h" 0014 #include "occurrenceiterator.h" 0015 0016 #include <QTest> 0017 #include <QTimeZone> 0018 0019 QTEST_MAIN(ICalFormatTest) 0020 0021 using namespace KCalendarCore; 0022 0023 void ICalFormatTest::testDeserializeSerialize() 0024 { 0025 ICalFormat format; 0026 0027 const QString serializedCalendar = QLatin1String( 0028 "BEGIN:VCALENDAR\n" 0029 "PRODID:-//IDN nextcloud.com//Calendar app 2.0.4//EN\n" 0030 "VERSION:2.0\n" 0031 "BEGIN:VEVENT\n" 0032 "CREATED:20201103T161248Z\n" 0033 "DTSTAMP:20201103T161340Z\n" 0034 "LAST-MODIFIED:20201103T161340Z\n" 0035 "SEQUENCE:2\n" 0036 "UID:bd1d299d-3b03-4514-be69-e680ad2ff884\n" 0037 "DTSTART;TZID=Europe/Paris:20201103T100000\n" 0038 "DTEND;TZID=Europe/Paris:20201103T110000\n" 0039 "SUMMARY:test recur\n" 0040 "RRULE:FREQ=DAILY;COUNT=4\n" 0041 "END:VEVENT\n" 0042 "BEGIN:VEVENT\n" 0043 "CREATED:20201103T161823Z\n" 0044 "DTSTAMP:20201103T161823Z\n" 0045 "LAST-MODIFIED:20201103T161823Z\n" 0046 "SEQUENCE:1\n" 0047 "UID:bd1d299d-3b03-4514-be69-e680ad2ff884\n" 0048 "DTSTART;TZID=Europe/Paris:20201104T111500\n" 0049 "DTEND;TZID=Europe/Paris:20201104T121500\n" 0050 "SUMMARY:test recur\n" 0051 "COLOR:khaki\n" 0052 "RECURRENCE-ID;TZID=Europe/Paris:20201104T100000\n" 0053 "END:VEVENT\n" 0054 "END:VCALENDAR"); 0055 MemoryCalendar::Ptr calendar = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc())); 0056 QVERIFY(format.fromString(calendar, serializedCalendar)); 0057 const QString uid = QString::fromLatin1("bd1d299d-3b03-4514-be69-e680ad2ff884"); 0058 Incidence::Ptr parent = calendar->incidence(uid); 0059 QVERIFY(parent); 0060 const QDateTime start(QDate(2020, 11, 3), QTime(9, 0), QTimeZone::utc()); 0061 QCOMPARE(parent->dtStart(), start); 0062 QCOMPARE(parent.staticCast<Event>()->dtEnd(), start.addSecs(3600)); 0063 QCOMPARE(parent->summary(), QString::fromLatin1("test recur")); 0064 QCOMPARE(parent->revision(), 2); 0065 Recurrence *recur = parent->recurrence(); 0066 QVERIFY(recur->recurs()); 0067 QCOMPARE(recur->duration(), 4); 0068 QCOMPARE(recur->recurrenceType(), static_cast<ushort>(Recurrence::rDaily)); 0069 0070 Incidence::Ptr occurrence = calendar->incidence(uid, start.addDays(1)); 0071 QVERIFY(occurrence); 0072 const QDateTime startOcc(QDate(2020, 11, 4), QTime(10, 15), QTimeZone::utc()); 0073 QCOMPARE(occurrence->dtStart(), startOcc); 0074 QCOMPARE(occurrence.staticCast<Event>()->dtEnd(), startOcc.addSecs(3600)); 0075 QCOMPARE(occurrence->color(), QString::fromLatin1("khaki")); 0076 QCOMPARE(occurrence->summary(), QString::fromLatin1("test recur")); 0077 QCOMPARE(occurrence->revision(), 1); 0078 QVERIFY(occurrence->hasRecurrenceId()); 0079 QCOMPARE(occurrence->recurrenceId(), start.addDays(1)); 0080 0081 const QString serialization = format.toString(calendar, QString()); 0082 QVERIFY(!serialization.isEmpty()); 0083 MemoryCalendar::Ptr check = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc())); 0084 QVERIFY(format.fromString(check, serialization)); 0085 Incidence::Ptr reparent = check->incidence(uid); 0086 QVERIFY(reparent); 0087 QCOMPARE(*parent, *reparent); 0088 Incidence::Ptr reoccurence = check->incidence(uid, start.addDays(1)); 0089 QVERIFY(reoccurence); 0090 QCOMPARE(*occurrence, *reoccurence); 0091 } 0092 0093 void ICalFormatTest::testCharsets() 0094 { 0095 ICalFormat format; 0096 const QDate currentDate = QDate::currentDate(); 0097 Event::Ptr event = Event::Ptr(new Event()); 0098 event->setUid(QStringLiteral("12345")); 0099 event->setDtStart(QDateTime(currentDate, {})); 0100 event->setDtEnd(QDateTime(currentDate.addDays(1), {})); 0101 event->setAllDay(true); 0102 0103 // ü 0104 const QChar latin1_umlaut[] = {(QChar)0xFC, QLatin1Char('\0')}; 0105 event->setSummary(QString(latin1_umlaut)); 0106 0107 // Test if toString( Incidence ) didn't mess charsets 0108 const QString serialized = format.toString(event.staticCast<Incidence>()); 0109 const QChar utf_umlaut[] = {(QChar)0xC3, (QChar)0XBC, QLatin1Char('\0')}; 0110 QVERIFY(serialized.toUtf8().contains(QString(utf_umlaut).toLatin1().constData())); 0111 QVERIFY(!serialized.toUtf8().contains(QString(latin1_umlaut).toLatin1().constData())); 0112 QVERIFY(serialized.toLatin1().contains(QString(latin1_umlaut).toLatin1().constData())); 0113 QVERIFY(!serialized.toLatin1().contains(QString(utf_umlaut).toLatin1().constData())); 0114 0115 // test fromString( QString ) 0116 const QString serializedCalendar = QLatin1String("BEGIN:VCALENDAR\nPRODID:-//K Desktop Environment//NONSGML libkcal 3.2//EN\nVERSION:2.0\n") + serialized 0117 + QLatin1String("\nEND:VCALENDAR"); 0118 0119 Incidence::Ptr event2 = format.fromString(serializedCalendar); 0120 QVERIFY(event->summary() == event2->summary()); 0121 QVERIFY(event2->summary().toUtf8() == QByteArray(QString(utf_umlaut).toLatin1().constData())); 0122 0123 // test save() 0124 MemoryCalendar::Ptr calendar(new MemoryCalendar(QTimeZone::utc())); 0125 calendar->addIncidence(event); 0126 QVERIFY(format.save(calendar, QLatin1String("hommer.ics"))); 0127 0128 // Make sure hommer.ics is in UTF-8 0129 QFile file(QStringLiteral("hommer.ics")); 0130 QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); 0131 0132 const QByteArray bytesFromFile = file.readAll(); 0133 QVERIFY(bytesFromFile.contains(QString(utf_umlaut).toLatin1().constData())); 0134 QVERIFY(!bytesFromFile.contains(QString(latin1_umlaut).toLatin1().constData())); 0135 file.close(); 0136 0137 // Test load: 0138 MemoryCalendar::Ptr calendar2(new MemoryCalendar(QTimeZone::utc())); 0139 QVERIFY(format.load(calendar2, QLatin1String("hommer.ics"))); 0140 QVERIFY(calendar2->incidences().count() == 1); 0141 0142 Event::Ptr loadedEvent = calendar2->incidences().at(0).staticCast<Event>(); 0143 QVERIFY(loadedEvent->summary().toUtf8() == QByteArray(QString(utf_umlaut).toLatin1().constData())); 0144 QVERIFY(*loadedEvent == *event); 0145 0146 // Test fromRawString() 0147 MemoryCalendar::Ptr calendar3(new MemoryCalendar(QTimeZone::utc())); 0148 QVERIFY(format.fromRawString(calendar3, bytesFromFile)); 0149 QVERIFY(calendar3->incidences().count() == 1); 0150 QVERIFY(*calendar3->incidences().at(0) == *event); 0151 0152 QFile::remove(QStringLiteral("hommer.ics")); 0153 } 0154 0155 void ICalFormatTest::testVolatileProperties() 0156 { 0157 // Volatile properties are not written to the serialized data 0158 ICalFormat format; 0159 const QDate currentDate = QDate::currentDate(); 0160 Event::Ptr event = Event::Ptr(new Event()); 0161 event->setUid(QStringLiteral("12345")); 0162 event->setDtStart(QDateTime(currentDate, {})); 0163 event->setDtEnd(QDateTime(currentDate.addDays(1), {})); 0164 event->setAllDay(true); 0165 event->setCustomProperty("VOLATILE", "FOO", QStringLiteral("BAR")); 0166 QString string = format.toICalString(event); 0167 Incidence::Ptr incidence = format.fromString(string); 0168 0169 QCOMPARE(incidence->uid(), QStringLiteral("12345")); 0170 QVERIFY(incidence->customProperties().isEmpty()); 0171 } 0172 0173 void ICalFormatTest::testCuType() 0174 { 0175 ICalFormat format; 0176 const QDate currentDate = QDate::currentDate(); 0177 Event::Ptr event(new Event()); 0178 event->setUid(QStringLiteral("12345")); 0179 event->setDtStart(QDateTime(currentDate, {})); 0180 event->setDtEnd(QDateTime(currentDate.addDays(1), {})); 0181 event->setAllDay(true); 0182 0183 Attendee attendee(QStringLiteral("fred"), QStringLiteral("fred@flintstone.com")); 0184 attendee.setCuType(Attendee::Resource); 0185 0186 event->addAttendee(attendee); 0187 0188 const QString serialized = format.toString(event.staticCast<Incidence>()); 0189 0190 // test fromString(QString) 0191 const QString serializedCalendar = QLatin1String("BEGIN:VCALENDAR\nPRODID:-//K Desktop Environment//NONSGML libkcal 3.2//EN\nVERSION:2.0\n") + serialized 0192 + QLatin1String("\nEND:VCALENDAR"); 0193 0194 Incidence::Ptr event2 = format.fromString(serializedCalendar); 0195 QVERIFY(event2->attendeeCount() == 1); 0196 Attendee attendee2 = event2->attendees()[0]; 0197 QVERIFY(attendee2.cuType() == attendee.cuType()); 0198 QVERIFY(attendee2.name() == attendee.name()); 0199 QVERIFY(attendee2.email() == attendee.email()); 0200 } 0201 0202 void ICalFormatTest::testAlarm() 0203 { 0204 ICalFormat format; 0205 0206 Event::Ptr event(new Event); 0207 event->setDtStart(QDate(2017, 03, 24).startOfDay()); 0208 Alarm::Ptr alarm = event->newAlarm(); 0209 alarm->setType(Alarm::Display); 0210 alarm->setStartOffset(Duration(0)); 0211 0212 const QString serialized = QLatin1String("BEGIN:VCALENDAR\nPRODID:-//K Desktop Environment//NONSGML libkcal 3.2//EN\nVERSION:2.0\n") 0213 + format.toString(event.staticCast<Incidence>()) + QLatin1String("\nEND:VCALENDAR"); 0214 0215 Incidence::Ptr event2 = format.fromString(serialized); 0216 Alarm::Ptr alarm2 = event2->alarms()[0]; 0217 QCOMPARE(*alarm, *alarm2); 0218 } 0219 0220 void ICalFormatTest::testDateTimeSerialization_data() 0221 { 0222 QTest::addColumn<QDateTime>("dtStart"); 0223 QTest::addColumn<QByteArray>("dtStartData"); 0224 0225 QTest::newRow("UTC time spec") 0226 << QDateTime(QDate(2021, 4, 9), QTime(12, 00), Qt::UTC) 0227 << QByteArray("DTSTART:20210409T120000Z"); 0228 QTest::newRow("UTC time zone") 0229 << QDateTime(QDate(2021, 4, 9), QTime(12, 00), QTimeZone::utc()) 0230 << QByteArray("DTSTART:20210409T120000Z"); 0231 QTest::newRow("named time zone") 0232 << QDateTime(QDate(2021, 4, 9), QTime(14, 00), QTimeZone("Europe/Paris")) 0233 << QByteArray("DTSTART;TZID=Europe/Paris:20210409T140000"); 0234 } 0235 0236 void ICalFormatTest::testDateTimeSerialization() 0237 { 0238 QFETCH(QDateTime, dtStart); 0239 QFETCH(QByteArray, dtStartData); 0240 0241 Incidence::Ptr event(new Event); 0242 QVERIFY(event); 0243 event->setDtStart(dtStart); 0244 QCOMPARE(event->dtStart(), dtStart); 0245 0246 ICalFormat format; 0247 const QByteArray output = format.toRawString(event); 0248 const QList<QByteArray> lines = output.split('\n'); 0249 for (const QByteArray &line: lines) { 0250 if (line.startsWith(QByteArray("DTSTART"))) { 0251 QCOMPARE(line.chopped(1), dtStartData); 0252 break; 0253 } 0254 } 0255 } 0256 0257 void ICalFormatTest::testRDate() 0258 { 0259 ICalFormat format; 0260 0261 const QString serializedCalendar = QLatin1String( 0262 "BEGIN:VCALENDAR\n" 0263 "VERSION:2.0\n" 0264 "PRODID:-//Lotus Development Corporation//NONSGML Notes 9.0.1//EN_C\n" 0265 "METHOD:PUBLISH\n" 0266 "BEGIN:VEVENT\n" 0267 "DTSTART:20210630T100000Z\n" 0268 "DTEND:20210630T110000Z\n" 0269 "TRANSP:OPAQUE\n" 0270 "RDATE;VALUE=PERIOD:20210630T100000Z/20210630T110000Z\n" 0271 " ,20210825T100000Z/20210825T110000Z,20211027T100000Z/20211027T110000Z\n" 0272 " ,20211215T110000Z/PT2H\n" 0273 "LAST-MODIFIED:20210601T094627Z\n" 0274 "DTSTAMP:20210601T092939Z\n" 0275 "UID:5FC21473F5CC80CCC12586E70033ED9C-Lotus_Notes_Generated\n" 0276 "END:VEVENT\n" 0277 "END:VCALENDAR\n"); 0278 MemoryCalendar::Ptr calendar(new MemoryCalendar(QTimeZone::utc())); 0279 QVERIFY(format.fromString(calendar, serializedCalendar)); 0280 const QString uid = QString::fromLatin1("5FC21473F5CC80CCC12586E70033ED9C-Lotus_Notes_Generated"); 0281 Incidence::Ptr event = calendar->incidence(uid); 0282 QVERIFY(event); 0283 QVERIFY(event->recurs()); 0284 0285 const QDateTime ev1(QDate(2021, 6, 30), QTime(10, 0), Qt::UTC); 0286 const QDateTime ev2(QDate(2021, 8, 25), QTime(10, 0), Qt::UTC); 0287 const QDateTime ev3(QDate(2021, 10, 27), QTime(10, 0), Qt::UTC); 0288 const QDateTime ev4(QDate(2021, 12, 15), QTime(11, 0), Qt::UTC); 0289 QCOMPARE(event->recurrence()->rDateTimes(), 0290 QList<QDateTime>() << ev1 << ev2 << ev3 << ev4); 0291 0292 OccurrenceIterator it(*calendar, QDateTime(QDate(2021, 6, 1), QTime(0, 0)), 0293 QDateTime(QDate(2021, 12, 31), QTime(0, 0))); 0294 QVERIFY(it.hasNext()); 0295 it.next(); 0296 QCOMPARE(it.occurrenceStartDate(), ev1); 0297 QCOMPARE(it.occurrenceEndDate(), ev1.addSecs(3600)); 0298 QVERIFY(it.hasNext()); 0299 it.next(); 0300 QCOMPARE(it.occurrenceStartDate(), ev2); 0301 QCOMPARE(it.occurrenceEndDate(), ev2.addSecs(3600)); 0302 QVERIFY(it.hasNext()); 0303 it.next(); 0304 QCOMPARE(it.occurrenceStartDate(), ev3); 0305 QCOMPARE(it.occurrenceEndDate(), ev3.addSecs(3600)); 0306 QVERIFY(it.hasNext()); 0307 it.next(); 0308 QCOMPARE(it.occurrenceStartDate(), ev4); 0309 QCOMPARE(it.occurrenceEndDate(), ev4.addSecs(7200)); 0310 0311 const QStringList output = format.toString(calendar, QString()).split(QString::fromLatin1("\r\n")); 0312 QVERIFY(output.contains(QString::fromLatin1("RDATE;VALUE=PERIOD:20210630T100000Z/20210630T110000Z"))); 0313 QVERIFY(output.contains(QString::fromLatin1("RDATE;VALUE=PERIOD:20210825T100000Z/20210825T110000Z"))); 0314 QVERIFY(output.contains(QString::fromLatin1("RDATE;VALUE=PERIOD:20211027T100000Z/20211027T110000Z"))); 0315 QVERIFY(output.contains(QString::fromLatin1("RDATE;VALUE=PERIOD:20211215T110000Z/PT2H"))); 0316 } 0317 0318 void ICalFormatTest::testDateTime_data() 0319 { 0320 QTest::addColumn<QByteArray>("dtStartData"); 0321 QTest::addColumn<QDateTime>("dtStart"); 0322 0323 QTest::newRow("clock time") 0324 << QByteArray("DTSTART:20191113T130000") 0325 << QDateTime(QDate(2019, 11, 13), QTime(13, 00), Qt::LocalTime); 0326 QTest::newRow("date") 0327 << QByteArray("DTSTART;VALUE=DATE:20191113") 0328 << QDate(2019, 11, 13).startOfDay(); 0329 QTest::newRow("UTC time") 0330 << QByteArray("DTSTART:20191113T130000Z") 0331 << QDateTime(QDate(2019, 11, 13), QTime(13, 00), Qt::UTC); 0332 QTest::newRow("time zone time") 0333 << QByteArray("DTSTART;TZID=Europe/Paris:20191113T130000") 0334 << QDateTime(QDate(2019, 11, 13), QTime(13, 00), QTimeZone("Europe/Paris")); 0335 } 0336 0337 void ICalFormatTest::testDateTime() 0338 { 0339 QFETCH(QByteArray, dtStartData); 0340 QFETCH(QDateTime, dtStart); 0341 0342 // test fromString(QString) 0343 const QByteArray serializedCalendar 0344 = "BEGIN:VCALENDAR\nPRODID:-//K Desktop Environment//NONSGML libkcal 3.2//EN\nVERSION:2.0\nBEGIN:VEVENT\nUID:12345\n" 0345 + dtStartData 0346 + "\nEND:VEVENT\nEND:VCALENDAR"; 0347 0348 ICalFormat format; 0349 Incidence::Ptr event = format.fromString(QString::fromUtf8(serializedCalendar)); 0350 QVERIFY(event); 0351 QCOMPARE(dtStart, event->dtStart()); 0352 } 0353 0354 void ICalFormatTest::testNotebook() 0355 { 0356 Event::Ptr event(new Event); 0357 event->setDtStart(QDateTime(QDate(2022, 3, 21), QTime(8, 49), Qt::UTC)); 0358 Todo::Ptr todo(new Todo); 0359 todo->setDtStart(QDateTime(QDate(2022, 3, 21), QTime(8, 49), Qt::UTC)); 0360 Journal::Ptr journal(new Journal); 0361 journal->setDtStart(QDateTime(QDate(2022, 3, 21), QTime(8, 49), Qt::UTC)); 0362 MemoryCalendar::Ptr calendar(new MemoryCalendar(QTimeZone::utc())); 0363 QVERIFY(calendar->addEvent(event)); 0364 QVERIFY(calendar->addTodo(todo)); 0365 QVERIFY(calendar->addJournal(journal)); 0366 0367 ICalFormat format; 0368 const QString data = format.toString(calendar, QString()); 0369 QVERIFY(!format.exception()); 0370 0371 calendar->close(); 0372 QVERIFY(!calendar->event(event->uid(), event->recurrenceId())); 0373 QVERIFY(!calendar->todo(todo->uid(), todo->recurrenceId())); 0374 QVERIFY(!calendar->journal(journal->uid(), journal->recurrenceId())); 0375 0376 const QString notebook(QString::fromLatin1("my-imported-notebook")); 0377 QVERIFY(calendar->addNotebook(notebook, true)); 0378 QVERIFY(format.fromString(calendar, data, notebook)); 0379 0380 Event::Ptr reloadedEvent = calendar->event(event->uid(), event->recurrenceId()); 0381 QVERIFY(reloadedEvent); 0382 Todo::Ptr reloadedTodo = calendar->todo(todo->uid(), todo->recurrenceId()); 0383 QVERIFY(reloadedTodo); 0384 Journal::Ptr reloadedJournal = calendar->journal(journal->uid(), journal->recurrenceId()); 0385 QVERIFY(reloadedJournal); 0386 0387 QCOMPARE(calendar->incidences(notebook).length(), 3); 0388 QCOMPARE(calendar->notebook(reloadedEvent), notebook); 0389 QCOMPARE(calendar->notebook(reloadedTodo), notebook); 0390 QCOMPARE(calendar->notebook(reloadedJournal), notebook); 0391 } 0392 0393 /** 0394 * If an instance does not have a UID, one will be created for it. 0395 */ 0396 void ICalFormatTest::testUidGeneration() 0397 { 0398 const QString serializedCalendar = QLatin1String( 0399 "BEGIN:VCALENDAR\n" 0400 "VERSION:2.0\n" 0401 "BEGIN:VEVENT\n" 0402 "DTSTAMP:20201103T161340Z\n" 0403 "SUMMARY:test\n" 0404 "END:VEVENT\n" 0405 "END:VCALENDAR"); 0406 auto calendar = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc())); 0407 ICalFormat format; 0408 QVERIFY(format.fromString(calendar, serializedCalendar)); 0409 const auto events = calendar->events(); 0410 QCOMPARE(events.count(), 1); 0411 const auto event = events[0]; 0412 QVERIFY( ! event->uid().isEmpty()); 0413 } 0414 0415 /** 0416 * Generated UIDs do not depend on the order of properties. 0417 */ 0418 void ICalFormatTest::testUidGenerationStability() 0419 { 0420 ICalFormat format; 0421 0422 const QString serializedCalendar1 = QLatin1String( 0423 "BEGIN:VCALENDAR\n" 0424 "VERSION:2.0\n" 0425 "BEGIN:VEVENT\n" 0426 "DTSTAMP:20201103T161340Z\n" 0427 "SUMMARY:test\n" 0428 "END:VEVENT\n" 0429 "END:VCALENDAR"); 0430 auto calendar1 = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc())); 0431 QVERIFY(format.fromString(calendar1, serializedCalendar1)); 0432 const auto events1 = calendar1->events(); 0433 QCOMPARE(events1.count(), 1); 0434 0435 const QString serializedCalendar2 = QLatin1String( 0436 "BEGIN:VCALENDAR\n" 0437 "VERSION:2.0\n" 0438 "BEGIN:VEVENT\n" 0439 "SUMMARY:test\n" 0440 "DTSTAMP:20201103T161340Z\n" // Reordered. 0441 "END:VEVENT\n" 0442 "END:VCALENDAR"); 0443 auto calendar2 = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc())); 0444 QVERIFY(format.fromString(calendar2, serializedCalendar2)); 0445 const auto events2 = calendar2->events(); 0446 QCOMPARE(events2.count(), 1); 0447 0448 const auto event1 = events1[0]; 0449 const auto event2 = events2[0]; 0450 QCOMPARE(event1->uid(), event2->uid()); 0451 } 0452 0453 /** 0454 * Generated UIDs depend on property names and values. 0455 */ 0456 void ICalFormatTest::testUidGenerationUniqueness() 0457 { 0458 ICalFormat format; 0459 0460 const QString serializedCalendar1 = QLatin1String( 0461 "BEGIN:VCALENDAR\n" 0462 "VERSION:2.0\n" 0463 "BEGIN:VEVENT\n" 0464 "DTSTAMP:20201103T161340Z\n" 0465 "SUMMARY:test\n" 0466 "END:VEVENT\n" 0467 "END:VCALENDAR"); 0468 auto calendar1 = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc())); 0469 QVERIFY(format.fromString(calendar1, serializedCalendar1)); 0470 const auto events1 = calendar1->events(); 0471 QCOMPARE(events1.count(), 1); 0472 0473 const QString serializedCalendar2 = QLatin1String( 0474 "BEGIN:VCALENDAR\n" 0475 "VERSION:2.0\n" 0476 "BEGIN:VEVENT\n" 0477 "DTSTART:20201103T161340Z\n" // Property name change. 0478 "SUMMARY:test\n" 0479 "END:VEVENT\n" 0480 "END:VCALENDAR"); 0481 auto calendar2 = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc())); 0482 QVERIFY(format.fromString(calendar2, serializedCalendar2)); 0483 const auto events2 = calendar2->events(); 0484 QCOMPARE(events2.count(), 1); 0485 0486 const QString serializedCalendar3 = QLatin1String( 0487 "BEGIN:VCALENDAR\n" 0488 "VERSION:2.0\n" 0489 "BEGIN:VEVENT\n" 0490 "DTSTAMP:20201103T161341Z\n" // Property value changed. 0491 "SUMMARY:test\n" 0492 "END:VEVENT\n" 0493 "END:VCALENDAR"); 0494 auto calendar3 = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc())); 0495 QVERIFY(format.fromString(calendar3, serializedCalendar3)); 0496 const auto events3 = calendar3->events(); 0497 QCOMPARE(events3.count(), 1); 0498 0499 const auto event1 = events1[0]; 0500 const auto event2 = events2[0]; 0501 const auto event3 = events3[0]; 0502 QVERIFY(event1->uid() != event2->uid()); 0503 QVERIFY(event1->uid() != event3->uid()); 0504 QVERIFY(event2->uid() != event3->uid()); 0505 } 0506 0507 void ICalFormatTest::testIcalFormat() 0508 { 0509 ICalFormat format; 0510 auto duration = format.durationFromString(QStringLiteral("PT2H")); 0511 QVERIFY(!duration.isNull()); 0512 QCOMPARE(duration.asSeconds(), 7200); 0513 QCOMPARE(format.toString(duration), QLatin1String("PT2H")); 0514 } 0515 0516 void ICalFormatTest::testNonTextCustomProperties() 0517 { 0518 const auto input = QLatin1String( 0519 "BEGIN:VCALENDAR\n" 0520 "VERSION:2.0\n" 0521 "BEGIN:VEVENT\n" 0522 "X-APPLE-TRAVEL-START;ROUTING=CAR;VALUE=URI;X-ADDRESS=Bingerdenallee 1\\n\n" 0523 " 6921 JN Duiven\\nNederland;X-TITLE=Home:\n" 0524 "X-APPLE-TRAVEL-DURATION;VALUE=DURATION:PT45M\n" 0525 "X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-ADDRESS=Olympus 1\\n3524 WB Utre\n" 0526 " cht\\nThe Netherlands;X-APPLE-RADIUS=49.91307222863458;X-TITLE=Olympus 1\n" 0527 " :geo:52.063921,5.128511\n" 0528 "BEGIN:VALARM\n" 0529 "TRIGGER;X-APPLE-RELATED-TRAVEL=-PT30M:-PT1H15M\n" 0530 "END:VALARM\n" 0531 "END:VEVENT\n" 0532 "END:VCALENDAR\n"); 0533 ICalFormat format; 0534 MemoryCalendar::Ptr cal(new MemoryCalendar(QTimeZone::utc())); 0535 QVERIFY(format.fromString(cal, input)); 0536 const auto events = cal->events(); 0537 QCOMPARE(events.size(), 1); 0538 0539 const auto event = events[0]; 0540 QCOMPARE(event->nonKDECustomProperty("X-APPLE-TRAVEL-DURATION"), QLatin1String("PT45M")); 0541 QCOMPARE(event->nonKDECustomProperty("X-APPLE-TRAVEL-START"), QString()); 0542 QCOMPARE(event->nonKDECustomProperty("X-APPLE-STRUCTURED-LOCATION"), QLatin1String("geo:52.063921,5.128511")); 0543 } 0544 0545 void ICalFormatTest::testAllDaySchedulingMessage() 0546 { 0547 auto event = KCalendarCore::Event::Ptr::create(); 0548 event->setSummary(QStringLiteral("All Day Event")); 0549 event->setDtStart(QDateTime(QDate(2023, 10, 13), QTime(0, 0, 0), QTimeZone("Europe/Prague"))); 0550 event->setDtEnd(QDateTime(QDate(2023, 10, 15), QTime(0, 0, 0), QTimeZone("Europe/Prague"))); 0551 event->setOrganizer(Person(QStringLiteral("Dan"), QStringLiteral("dvratil@example.com"))); 0552 event->addAttendee(Attendee(QStringLiteral("Konqi"), QStringLiteral("konqi@example.com"))); 0553 event->setAllDay(true); 0554 0555 ICalFormat format; 0556 auto calendar = MemoryCalendar::Ptr::create(QTimeZone::utc()); 0557 const auto itipString = format.createScheduleMessage(event, KCalendarCore::iTIPRequest); 0558 QVERIFY(!itipString.isEmpty()); 0559 0560 auto scheduleMsg = format.parseScheduleMessage(calendar, itipString); 0561 QVERIFY(scheduleMsg->error().isEmpty()); 0562 0563 auto parsedEvent = scheduleMsg->event().staticCast<KCalendarCore::Event>(); 0564 QVERIFY(parsedEvent); 0565 QCOMPARE(parsedEvent->dtStart().date(), event->dtStart().date()); 0566 QCOMPARE(parsedEvent->dtEnd().date(), event->dtEnd().date()); 0567 } 0568 0569 #include "moc_testicalformat.cpp"