File indexing completed on 2024-04-21 03:52:40

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);
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") << QDateTime(QDate(2021, 4, 9), QTime(12, 00), QTimeZone::UTC) << QByteArray("DTSTART:20210409T120000Z");
0226     QTest::newRow("UTC time zone")
0227         << QDateTime(QDate(2021, 4, 9), QTime(12, 00), QTimeZone::utc())
0228         << QByteArray("DTSTART:20210409T120000Z");
0229     QTest::newRow("named time zone")
0230         << QDateTime(QDate(2021, 4, 9), QTime(14, 00), QTimeZone("Europe/Paris"))
0231         << QByteArray("DTSTART;TZID=Europe/Paris:20210409T140000");
0232 }
0233 
0234 void ICalFormatTest::testDateTimeSerialization()
0235 {
0236     QFETCH(QDateTime, dtStart);
0237     QFETCH(QByteArray, dtStartData);
0238 
0239     Incidence::Ptr event(new Event);
0240     QVERIFY(event);
0241     event->setDtStart(dtStart);
0242     QCOMPARE(event->dtStart(), dtStart);
0243 
0244     ICalFormat format;
0245     const QByteArray output = format.toRawString(event);
0246     const QList<QByteArray> lines = output.split('\n');
0247     for (const QByteArray &line: lines) {
0248         if (line.startsWith(QByteArray("DTSTART"))) {
0249             QCOMPARE(line.chopped(1), dtStartData);
0250             break;
0251         }
0252     }
0253 }
0254 
0255 void ICalFormatTest::testRDate()
0256 {
0257     ICalFormat format;
0258 
0259     const QString serializedCalendar = QLatin1String(
0260         "BEGIN:VCALENDAR\n"
0261         "VERSION:2.0\n"
0262         "PRODID:-//Lotus Development Corporation//NONSGML Notes 9.0.1//EN_C\n"
0263         "METHOD:PUBLISH\n"
0264         "BEGIN:VEVENT\n"
0265         "DTSTART:20210630T100000Z\n"
0266         "DTEND:20210630T110000Z\n"
0267         "TRANSP:OPAQUE\n"
0268         "RDATE;VALUE=PERIOD:20210630T100000Z/20210630T110000Z\n"
0269         " ,20210825T100000Z/20210825T110000Z,20211027T100000Z/20211027T110000Z\n"
0270         " ,20211215T110000Z/PT2H\n"
0271         "LAST-MODIFIED:20210601T094627Z\n"
0272         "DTSTAMP:20210601T092939Z\n"
0273         "UID:5FC21473F5CC80CCC12586E70033ED9C-Lotus_Notes_Generated\n"
0274         "END:VEVENT\n"
0275         "END:VCALENDAR\n");
0276     MemoryCalendar::Ptr calendar(new MemoryCalendar(QTimeZone::utc()));
0277     QVERIFY(format.fromString(calendar, serializedCalendar));
0278     const QString uid = QString::fromLatin1("5FC21473F5CC80CCC12586E70033ED9C-Lotus_Notes_Generated");
0279     Incidence::Ptr event = calendar->incidence(uid);
0280     QVERIFY(event);
0281     QVERIFY(event->recurs());
0282 
0283     const QDateTime ev1(QDate(2021, 6, 30), QTime(10, 0), QTimeZone::UTC);
0284     const QDateTime ev2(QDate(2021, 8, 25), QTime(10, 0), QTimeZone::UTC);
0285     const QDateTime ev3(QDate(2021, 10, 27), QTime(10, 0), QTimeZone::UTC);
0286     const QDateTime ev4(QDate(2021, 12, 15), QTime(11, 0), QTimeZone::UTC);
0287     QCOMPARE(event->recurrence()->rDateTimes(),
0288              QList<QDateTime>() << ev1 << ev2 << ev3 << ev4);
0289 
0290     OccurrenceIterator it(*calendar, QDateTime(QDate(2021, 6, 1), QTime(0, 0)),
0291                           QDateTime(QDate(2021, 12, 31), QTime(0, 0)));
0292     QVERIFY(it.hasNext());
0293     it.next();
0294     QCOMPARE(it.occurrenceStartDate(), ev1);
0295     QCOMPARE(it.occurrenceEndDate(), ev1.addSecs(3600));
0296     QVERIFY(it.hasNext());
0297     it.next();
0298     QCOMPARE(it.occurrenceStartDate(), ev2);
0299     QCOMPARE(it.occurrenceEndDate(), ev2.addSecs(3600));
0300     QVERIFY(it.hasNext());
0301     it.next();
0302     QCOMPARE(it.occurrenceStartDate(), ev3);
0303     QCOMPARE(it.occurrenceEndDate(), ev3.addSecs(3600));
0304     QVERIFY(it.hasNext());
0305     it.next();
0306     QCOMPARE(it.occurrenceStartDate(), ev4);
0307     QCOMPARE(it.occurrenceEndDate(), ev4.addSecs(7200));
0308 
0309     const QStringList output = format.toString(calendar).split(QString::fromLatin1("\r\n"));
0310     QVERIFY(output.contains(QString::fromLatin1("RDATE;VALUE=PERIOD:20210630T100000Z/20210630T110000Z")));
0311     QVERIFY(output.contains(QString::fromLatin1("RDATE;VALUE=PERIOD:20210825T100000Z/20210825T110000Z")));
0312     QVERIFY(output.contains(QString::fromLatin1("RDATE;VALUE=PERIOD:20211027T100000Z/20211027T110000Z")));
0313     QVERIFY(output.contains(QString::fromLatin1("RDATE;VALUE=PERIOD:20211215T110000Z/PT2H")));
0314 }
0315 
0316 void ICalFormatTest::testDateTime_data()
0317 {
0318     QTest::addColumn<QByteArray>("dtStartData");
0319     QTest::addColumn<QDateTime>("dtStart");
0320 
0321     QTest::newRow("clock time") << QByteArray("DTSTART:20191113T130000") << QDateTime(QDate(2019, 11, 13), QTime(13, 00), QTimeZone::LocalTime);
0322     QTest::newRow("date")
0323         << QByteArray("DTSTART;VALUE=DATE:20191113")
0324         << QDate(2019, 11, 13).startOfDay();
0325     QTest::newRow("UTC time") << QByteArray("DTSTART:20191113T130000Z") << QDateTime(QDate(2019, 11, 13), QTime(13, 00), QTimeZone::UTC);
0326     QTest::newRow("time zone time")
0327         << QByteArray("DTSTART;TZID=Europe/Paris:20191113T130000")
0328         << QDateTime(QDate(2019, 11, 13), QTime(13, 00), QTimeZone("Europe/Paris"));
0329 }
0330 
0331 void ICalFormatTest::testDateTime()
0332 {
0333     QFETCH(QByteArray, dtStartData);
0334     QFETCH(QDateTime, dtStart);
0335 
0336     // test fromString(QString)
0337     const QByteArray serializedCalendar
0338         = "BEGIN:VCALENDAR\nPRODID:-//K Desktop Environment//NONSGML libkcal 3.2//EN\nVERSION:2.0\nBEGIN:VEVENT\nUID:12345\n"
0339           + dtStartData
0340           + "\nEND:VEVENT\nEND:VCALENDAR";
0341 
0342     ICalFormat format;
0343     Incidence::Ptr event = format.fromString(QString::fromUtf8(serializedCalendar));
0344     QVERIFY(event);
0345     QCOMPARE(dtStart, event->dtStart());
0346 }
0347 
0348 /**
0349  * If an instance does not have a UID, one will be created for it.
0350  */
0351 void ICalFormatTest::testUidGeneration()
0352 {
0353     const QString serializedCalendar = QLatin1String(
0354         "BEGIN:VCALENDAR\n"
0355         "VERSION:2.0\n"
0356         "BEGIN:VEVENT\n"
0357         "DTSTAMP:20201103T161340Z\n"
0358         "SUMMARY:test\n"
0359         "END:VEVENT\n"
0360         "END:VCALENDAR");
0361     auto calendar = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc()));
0362     ICalFormat format;
0363     QVERIFY(format.fromString(calendar, serializedCalendar));
0364     const auto events = calendar->events();
0365     QCOMPARE(events.count(), 1);
0366     const auto event = events[0];
0367     QVERIFY( ! event->uid().isEmpty());
0368 }
0369 
0370 /**
0371  * Generated UIDs do not depend on the order of properties.
0372  */
0373 void ICalFormatTest::testUidGenerationStability()
0374 {
0375     ICalFormat format;
0376 
0377     const QString serializedCalendar1 = QLatin1String(
0378         "BEGIN:VCALENDAR\n"
0379         "VERSION:2.0\n"
0380         "BEGIN:VEVENT\n"
0381         "DTSTAMP:20201103T161340Z\n"
0382         "SUMMARY:test\n"
0383         "END:VEVENT\n"
0384         "END:VCALENDAR");
0385     auto calendar1 = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc()));
0386     QVERIFY(format.fromString(calendar1, serializedCalendar1));
0387     const auto events1 = calendar1->events();
0388     QCOMPARE(events1.count(), 1);
0389 
0390     const QString serializedCalendar2 = QLatin1String(
0391         "BEGIN:VCALENDAR\n"
0392         "VERSION:2.0\n"
0393         "BEGIN:VEVENT\n"
0394         "SUMMARY:test\n"
0395         "DTSTAMP:20201103T161340Z\n"    // Reordered.
0396         "END:VEVENT\n"
0397         "END:VCALENDAR");
0398     auto calendar2 = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc()));
0399     QVERIFY(format.fromString(calendar2, serializedCalendar2));
0400     const auto events2 = calendar2->events();
0401     QCOMPARE(events2.count(), 1);
0402 
0403     const auto event1 = events1[0];
0404     const auto event2 = events2[0];
0405     QCOMPARE(event1->uid(), event2->uid());
0406 }
0407 
0408 /**
0409  * Generated UIDs depend on property names and values.
0410  */
0411 void ICalFormatTest::testUidGenerationUniqueness()
0412 {
0413     ICalFormat format;
0414 
0415     const QString serializedCalendar1 = QLatin1String(
0416         "BEGIN:VCALENDAR\n"
0417         "VERSION:2.0\n"
0418         "BEGIN:VEVENT\n"
0419         "DTSTAMP:20201103T161340Z\n"
0420         "SUMMARY:test\n"
0421         "END:VEVENT\n"
0422         "END:VCALENDAR");
0423     auto calendar1 = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc()));
0424     QVERIFY(format.fromString(calendar1, serializedCalendar1));
0425     const auto events1 = calendar1->events();
0426     QCOMPARE(events1.count(), 1);
0427 
0428     const QString serializedCalendar2 = QLatin1String(
0429         "BEGIN:VCALENDAR\n"
0430         "VERSION:2.0\n"
0431         "BEGIN:VEVENT\n"
0432         "DTSTART:20201103T161340Z\n"    // Property name change.
0433         "SUMMARY:test\n"
0434         "END:VEVENT\n"
0435         "END:VCALENDAR");
0436     auto calendar2 = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc()));
0437     QVERIFY(format.fromString(calendar2, serializedCalendar2));
0438     const auto events2 = calendar2->events();
0439     QCOMPARE(events2.count(), 1);
0440 
0441     const QString serializedCalendar3 = QLatin1String(
0442         "BEGIN:VCALENDAR\n"
0443         "VERSION:2.0\n"
0444         "BEGIN:VEVENT\n"
0445         "DTSTAMP:20201103T161341Z\n"    // Property value changed.
0446         "SUMMARY:test\n"
0447         "END:VEVENT\n"
0448         "END:VCALENDAR");
0449     auto calendar3 = MemoryCalendar::Ptr(new MemoryCalendar(QTimeZone::utc()));
0450     QVERIFY(format.fromString(calendar3, serializedCalendar3));
0451     const auto events3 = calendar3->events();
0452     QCOMPARE(events3.count(), 1);
0453 
0454     const auto event1 = events1[0];
0455     const auto event2 = events2[0];
0456     const auto event3 = events3[0];
0457     QVERIFY(event1->uid() != event2->uid());
0458     QVERIFY(event1->uid() != event3->uid());
0459     QVERIFY(event2->uid() != event3->uid());
0460 }
0461 
0462 void ICalFormatTest::testIcalFormat()
0463 {
0464     ICalFormat format;
0465     auto duration = format.durationFromString(QStringLiteral("PT2H"));
0466     QVERIFY(!duration.isNull());
0467     QCOMPARE(duration.asSeconds(), 7200);
0468     QCOMPARE(format.toString(duration), QLatin1String("PT2H"));
0469 }
0470 
0471 void ICalFormatTest::testNonTextCustomProperties()
0472 {
0473     const auto input = QLatin1String(
0474         "BEGIN:VCALENDAR\n"
0475         "VERSION:2.0\n"
0476         "BEGIN:VEVENT\n"
0477         "X-APPLE-TRAVEL-START;ROUTING=CAR;VALUE=URI;X-ADDRESS=Bingerdenallee 1\\n\n"
0478         " 6921 JN Duiven\\nNederland;X-TITLE=Home:\n"
0479         "X-APPLE-TRAVEL-DURATION;VALUE=DURATION:PT45M\n"
0480         "X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-ADDRESS=Olympus 1\\n3524 WB Utre\n"
0481         " cht\\nThe Netherlands;X-APPLE-RADIUS=49.91307222863458;X-TITLE=Olympus 1\n"
0482         " :geo:52.063921,5.128511\n"
0483         "BEGIN:VALARM\n"
0484         "TRIGGER;X-APPLE-RELATED-TRAVEL=-PT30M:-PT1H15M\n"
0485         "END:VALARM\n"
0486         "END:VEVENT\n"
0487         "END:VCALENDAR\n");
0488     ICalFormat format;
0489     MemoryCalendar::Ptr cal(new MemoryCalendar(QTimeZone::utc()));
0490     QVERIFY(format.fromString(cal, input));
0491     const auto events = cal->events();
0492     QCOMPARE(events.size(), 1);
0493 
0494     const auto event = events[0];
0495     QCOMPARE(event->nonKDECustomProperty("X-APPLE-TRAVEL-DURATION"), QLatin1String("PT45M"));
0496     QCOMPARE(event->nonKDECustomProperty("X-APPLE-TRAVEL-START"), QString());
0497     QCOMPARE(event->nonKDECustomProperty("X-APPLE-STRUCTURED-LOCATION"), QLatin1String("geo:52.063921,5.128511"));
0498 }
0499 
0500 void ICalFormatTest::testAllDaySchedulingMessage()
0501 {
0502     auto event = KCalendarCore::Event::Ptr::create();
0503     event->setSummary(QStringLiteral("All Day Event"));
0504     event->setDtStart(QDateTime(QDate(2023, 10, 13), QTime(0, 0, 0), QTimeZone("Europe/Prague")));
0505     event->setDtEnd(QDateTime(QDate(2023, 10, 15), QTime(0, 0, 0), QTimeZone("Europe/Prague")));
0506     event->setOrganizer(Person(QStringLiteral("Dan"), QStringLiteral("dvratil@example.com")));
0507     event->addAttendee(Attendee(QStringLiteral("Konqi"), QStringLiteral("konqi@example.com")));
0508     event->setAllDay(true);
0509 
0510     ICalFormat format;
0511     auto calendar = MemoryCalendar::Ptr::create(QTimeZone::utc());
0512     const auto itipString = format.createScheduleMessage(event, KCalendarCore::iTIPRequest);
0513     QVERIFY(!itipString.isEmpty());
0514 
0515     auto scheduleMsg = format.parseScheduleMessage(calendar, itipString);
0516     QVERIFY(scheduleMsg->error().isEmpty());
0517 
0518     auto parsedEvent = scheduleMsg->event().staticCast<KCalendarCore::Event>();
0519     QVERIFY(parsedEvent);
0520     QCOMPARE(parsedEvent->dtStart().date(), event->dtStart().date());
0521     QCOMPARE(parsedEvent->dtEnd().date(), event->dtEnd().date());
0522 }
0523 
0524 #include "moc_testicalformat.cpp"