diff --git a/autotests/testdateserialization.cpp b/autotests/testdateserialization.cpp index f691d8b0e..3ceea1a8d 100644 --- a/autotests/testdateserialization.cpp +++ b/autotests/testdateserialization.cpp @@ -1,83 +1,91 @@ /* * SPDX-FileCopyrightText: 2020 Glen Ditchfield * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "testdateserialization.h" #include "icalformat.h" #include "memorycalendar.h" #include #include QTEST_MAIN(TestDateSerialization) using namespace KCalendarCore; // Check that serialization and deserialization of a minimal recurring todo // preserves the start and due dates of the todo and its first occurrence. // See bug 345498. void TestDateSerialization::testNewRecurringTodo() { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QDateTime startDate = QDate(2015, 3, 24).startOfDay(); +#else QDateTime startDate { QDate(2015, 3, 24) }; +#endif QDateTime dueDate { startDate.addDays(1) }; Todo::Ptr todo(new Todo); todo->setDtStart(startDate); todo->setDtDue(dueDate, true); todo->setAllDay(true); todo->recurrence()->setMonthly(1); MemoryCalendar::Ptr cal { new MemoryCalendar(QTimeZone::utc()) }; cal->addIncidence(todo); ICalFormat format; const QString result = format.toString(cal, QString()); Incidence::Ptr i = format.fromString(result); QVERIFY(i); QVERIFY(i->type() == IncidenceBase::IncidenceType::TypeTodo); Todo::Ptr newTodo = i.staticCast(); QCOMPARE(newTodo->dtStart(true), startDate); QCOMPARE(newTodo->dtStart(false), startDate); QCOMPARE(newTodo->dtDue(true), dueDate); QCOMPARE(newTodo->dtDue(false), dueDate); } // Check that serialization and deserialization of a minimal recurring todo // that has been completed once preserves the start and due dates of the todo // and correctly calculates the start and due dates of the next occurrence. // See bug 345565. void TestDateSerialization::testTodoCompletedOnce() { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QDateTime startDate = QDate::currentDate().startOfDay(); +#else QDateTime startDate { QDate::currentDate() }; +#endif QDateTime dueDate { startDate.addDays(1) }; Todo::Ptr todo(new Todo); todo->setDtStart(startDate); todo->setDtDue(dueDate, true); todo->setAllDay(true); todo->recurrence()->setMonthly(1); MemoryCalendar::Ptr cal { new MemoryCalendar(QTimeZone::utc()) }; cal->addIncidence(todo); ICalFormat format; QString result = format.toString(cal, QString()); Incidence::Ptr i = format.fromString(result); QVERIFY(i); QVERIFY(i->type() == IncidenceBase::IncidenceType::TypeTodo); todo = i.staticCast(); todo->setCompleted(dueDate); cal = MemoryCalendar::Ptr {new MemoryCalendar(QTimeZone::utc()) }; cal->addIncidence(todo); result = format.toString(cal, QString()); QCOMPARE(todo->dtStart(true), startDate); QCOMPARE(todo->dtStart(false), startDate.addMonths(1)); QCOMPARE(todo->dtDue(true), dueDate); QCOMPARE(todo->dtDue(false), dueDate.addMonths(1)); } diff --git a/autotests/testfreebusyperiod.cpp b/autotests/testfreebusyperiod.cpp index e12802e9f..b686efb9c 100644 --- a/autotests/testfreebusyperiod.cpp +++ b/autotests/testfreebusyperiod.cpp @@ -1,104 +1,108 @@ /* This file is part of the kcalcore library. SPDX-FileCopyrightText: 2010 Casey Link SPDX-FileCopyrightText: 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company SPDX-License-Identifier: LGPL-2.0-or-later */ #include "testfreebusyperiod.h" #include "freebusyperiod.h" #include QTEST_MAIN(FreeBusyPeriodTest) using namespace KCalendarCore; void FreeBusyPeriodTest::testValidity() { const QDateTime p1DateTime(QDate(2006, 8, 30), QTime(7, 0, 0), Qt::UTC); FreeBusyPeriod p1(p1DateTime, Duration(60)); QString summary = QStringLiteral("I can haz summary?"); QString location = QStringLiteral("The Moon"); p1.setSummary(summary); p1.setLocation(location); QVERIFY(p1.hasDuration()); QCOMPARE(p1.duration().asSeconds(), 60); QVERIFY(p1.start() == QDateTime(QDate(2006, 8, 30), QTime(7, 0, 0), Qt::UTC)); QCOMPARE(p1.summary(), summary); QCOMPARE(p1.location(), location); } void FreeBusyPeriodTest::testAssign() { const QDateTime p1DateTime(QDate(2006, 8, 30), QTime(7, 0, 0), Qt::UTC); FreeBusyPeriod p1(p1DateTime, Duration(60)); FreeBusyPeriod p2; QString summary = QStringLiteral("I can haz summary?"); QString location = QStringLiteral("The Moon"); p1.setSummary(summary); p1.setLocation(location); p2 = p1; QVERIFY(p2.hasDuration()); QVERIFY(p2.duration().asSeconds() == 60); QVERIFY(p2.start() == QDateTime(QDate(2006, 8, 30), QTime(7, 0, 0), Qt::UTC)); QCOMPARE(p1.summary(), summary); QCOMPARE(p1.location(), location); } void FreeBusyPeriodTest::testDataStreamOut() { const QDateTime p1DateTime(QDate(2006, 8, 30), QTime(7, 0, 0), Qt::UTC); FreeBusyPeriod p1(p1DateTime, Duration(60)); p1.setSummary(QStringLiteral("I can haz summary?")); p1.setLocation(QStringLiteral("The Moon")); QByteArray byteArray; QDataStream out_stream(&byteArray, QIODevice::WriteOnly); out_stream << p1; QDataStream in_stream(&byteArray, QIODevice::ReadOnly); Period p2; Period periodParent = static_cast(p1); in_stream >> p2; QVERIFY(periodParent == p2); QString summary; in_stream >> summary; QCOMPARE(summary, p1.summary()); QString location; in_stream >> location; QCOMPARE(location, p1.location()); } void FreeBusyPeriodTest::testDataStreamIn() { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + const QDateTime p1DateTime = QDate(2006, 8, 30).startOfDay(); +#else const QDateTime p1DateTime(QDate(2006, 8, 30)); +#endif const Duration duration(24 * 60 * 60); FreeBusyPeriod p1(p1DateTime, duration); p1.setSummary(QStringLiteral("I can haz summary?")); p1.setLocation(QStringLiteral("The Moon")); QByteArray byteArray; QDataStream out_stream(&byteArray, QIODevice::WriteOnly); out_stream << p1; QDataStream in_stream(&byteArray, QIODevice::ReadOnly); FreeBusyPeriod p2; in_stream >> p2; QCOMPARE(p2, p1); } diff --git a/autotests/testicalformat.cpp b/autotests/testicalformat.cpp index d2912f433..740ee4eee 100644 --- a/autotests/testicalformat.cpp +++ b/autotests/testicalformat.cpp @@ -1,159 +1,163 @@ /* This file is part of the kcalcore library. SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company SPDX-FileContributor: Sergio Martins SPDX-License-Identifier: LGPL-2.0-or-later */ #include "testicalformat.h" #include "event.h" #include "icalformat.h" #include "memorycalendar.h" #include #include #include QTEST_MAIN(ICalFormatTest) using namespace KCalendarCore; void ICalFormatTest::testCharsets() { ICalFormat format; const QDate currentDate = QDate::currentDate(); Event::Ptr event = Event::Ptr(new Event()); event->setUid(QStringLiteral("12345")); event->setDtStart(QDateTime(currentDate, {})); event->setDtEnd(QDateTime(currentDate.addDays(1), {})); event->setAllDay(true); // ü const QChar latin1_umlaut[] = { 0xFC, QLatin1Char('\0') }; event->setSummary(QString(latin1_umlaut)); // Test if toString( Incidence ) didn't mess charsets const QString serialized = format.toString(event.staticCast()); const QChar utf_umlaut[] = { 0xC3, 0XBC, QLatin1Char('\0') }; QVERIFY(serialized.toUtf8().contains(QString(utf_umlaut).toLatin1().constData())); QVERIFY(!serialized.toUtf8().contains(QString(latin1_umlaut).toLatin1().constData())); QVERIFY(serialized.toLatin1().contains(QString(latin1_umlaut).toLatin1().constData())); QVERIFY(!serialized.toLatin1().contains(QString(utf_umlaut).toLatin1().constData())); // test fromString( QString ) const QString serializedCalendar = QLatin1String("BEGIN:VCALENDAR\nPRODID:-//K Desktop Environment//NONSGML libkcal 3.2//EN\nVERSION:2.0\n") +serialized +QLatin1String("\nEND:VCALENDAR"); Incidence::Ptr event2 = format.fromString(serializedCalendar); QVERIFY(event->summary() == event2->summary()); QVERIFY(event2->summary().toUtf8() == QByteArray(QString(utf_umlaut).toLatin1().constData())); // test save() MemoryCalendar::Ptr calendar(new MemoryCalendar(QTimeZone::utc())); calendar->addIncidence(event); QVERIFY(format.save(calendar, QLatin1String("hommer.ics"))); // Make sure hommer.ics is in UTF-8 QFile file(QStringLiteral("hommer.ics")); QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); const QByteArray bytesFromFile = file.readAll(); QVERIFY(bytesFromFile.contains(QString(utf_umlaut).toLatin1().constData())); QVERIFY(!bytesFromFile.contains(QString(latin1_umlaut).toLatin1().constData())); file.close(); // Test load: MemoryCalendar::Ptr calendar2(new MemoryCalendar(QTimeZone::utc())); QVERIFY(format.load(calendar2, QLatin1String("hommer.ics"))); QVERIFY(calendar2->incidences().count() == 1); // qDebug() << format.toString( event.staticCast() ); // qDebug() << format.toString( calendar2->incidences().at(0) ); Event::Ptr loadedEvent = calendar2->incidences().at(0).staticCast(); QVERIFY(loadedEvent->summary().toUtf8() == QByteArray(QString(utf_umlaut).toLatin1().constData())); QVERIFY(*loadedEvent == *event); // Test fromRawString() MemoryCalendar::Ptr calendar3(new MemoryCalendar(QTimeZone::utc())); QVERIFY(format.fromRawString(calendar3, bytesFromFile)); QVERIFY(calendar3->incidences().count() == 1); QVERIFY(*calendar3->incidences().at(0) == *event); QFile::remove(QStringLiteral("hommer.ics")); } void ICalFormatTest::testVolatileProperties() { // Volatile properties are not written to the serialized data ICalFormat format; const QDate currentDate = QDate::currentDate(); Event::Ptr event = Event::Ptr(new Event()); event->setUid(QStringLiteral("12345")); event->setDtStart(QDateTime(currentDate, {})); event->setDtEnd(QDateTime(currentDate.addDays(1), {})); event->setAllDay(true); event->setCustomProperty("VOLATILE", "FOO", QStringLiteral("BAR")); QString string = format.toICalString(event); Incidence::Ptr incidence = format.fromString(string); QCOMPARE(incidence->uid(), QStringLiteral("12345")); QVERIFY(incidence->customProperties().isEmpty()); } void ICalFormatTest::testCuType() { ICalFormat format; const QDate currentDate = QDate::currentDate(); Event::Ptr event(new Event()); event->setUid(QStringLiteral("12345")); event->setDtStart(QDateTime(currentDate, {})); event->setDtEnd(QDateTime(currentDate.addDays(1), {})); event->setAllDay(true); Attendee attendee(QStringLiteral("fred"), QStringLiteral("fred@flintstone.com")); attendee.setCuType(Attendee::Resource); event->addAttendee(attendee); const QString serialized = format.toString(event.staticCast()); // test fromString(QString) const QString serializedCalendar = QLatin1String("BEGIN:VCALENDAR\nPRODID:-//K Desktop Environment//NONSGML libkcal 3.2//EN\nVERSION:2.0\n") +serialized +QLatin1String("\nEND:VCALENDAR"); Incidence::Ptr event2 = format.fromString(serializedCalendar); QVERIFY(event2->attendeeCount() == 1); Attendee attendee2 = event2->attendees()[0]; QVERIFY(attendee2.cuType() == attendee.cuType()); QVERIFY(attendee2.name() == attendee.name()); QVERIFY(attendee2.email() == attendee.email()); } void ICalFormatTest::testAlarm() { ICalFormat format; Event::Ptr event(new Event); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + event->setDtStart(QDate(2017, 03, 24).startOfDay()); +#else event->setDtStart(QDateTime(QDate(2017, 03, 24))); +#endif Alarm::Ptr alarm = event->newAlarm(); alarm->setType(Alarm::Display); alarm->setStartOffset(Duration(0)); const QString serialized = QLatin1String("BEGIN:VCALENDAR\nPRODID:-//K Desktop Environment//NONSGML libkcal 3.2//EN\nVERSION:2.0\n") + format.toString(event.staticCast()) + QLatin1String("\nEND:VCALENDAR"); Incidence::Ptr event2 = format.fromString(serialized); Alarm::Ptr alarm2 = event2->alarms()[0]; QCOMPARE(*alarm, *alarm2); } diff --git a/autotests/testtimesininterval.cpp b/autotests/testtimesininterval.cpp index acb558a35..ba0acfb76 100644 --- a/autotests/testtimesininterval.cpp +++ b/autotests/testtimesininterval.cpp @@ -1,244 +1,256 @@ /* This file is part of the kcalcore library. SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company SPDX-FileContributor: Sergio Martins SPDX-License-Identifier: LGPL-2.0-or-later */ #include "testtimesininterval.h" #include "event.h" #include #include QTEST_MAIN(TimesInIntervalTest) using namespace KCalendarCore; void TimesInIntervalTest::test() { const QDateTime currentDate(QDate::currentDate(), {}); Event *event = new Event(); event->setDtStart(currentDate); event->setDtEnd(currentDate.addDays(1)); event->setAllDay(true); event->setSummary(QStringLiteral("Event1 Summary")); event->recurrence()->setDaily(1); //------------------------------------------------------------------------------------------------ // Just to warm up QVERIFY(event->recurs()); QVERIFY(event->recursAt(currentDate)); //------------------------------------------------------------------------------------------------ // Daily recurrence that never stops. // Should return numDaysInInterval+1 occurrences const int numDaysInInterval = 7; QDateTime start(currentDate); QDateTime end(start.addDays(numDaysInInterval)); start.setTime(QTime(0, 0, 0)); end.setTime(QTime(23, 59, 59)); auto dateList = event->recurrence()->timesInInterval(start, end); QVERIFY(dateList.count() == numDaysInInterval + 1); //------------------------------------------------------------------------------------------------ // start == end == first day of the recurrence, should only return 1 occurrence end = start; end.setTime(QTime(23, 59, 59)); dateList = event->recurrence()->timesInInterval(start, end); QVERIFY(dateList.count() == 1); //------------------------------------------------------------------------------------------------ // Test daily recurrence that only lasts X days const int recurrenceDuration = 3; event->recurrence()->setDuration(recurrenceDuration); end = start.addDays(100); dateList = event->recurrence()->timesInInterval(start, end); QVERIFY(dateList.count() == recurrenceDuration); //------------------------------------------------------------------------------------------------ // Test daily recurrence that only lasts X days, and give start == end == last day of // recurrence. Previous versions of kcal had a bug and didn't return an occurrence start = start.addDays(recurrenceDuration - 1); end = start; start.setTime(QTime(0, 0, 0)); end.setTime(QTime(23, 59, 59)); dateList = event->recurrence()->timesInInterval(start, end); QVERIFY(dateList.count() == 1); //------------------------------------------------------------------------------------------------ } //Test that interval start and end are inclusive void TimesInIntervalTest::testSubDailyRecurrenceIntervalInclusive() { const QDateTime start(QDate(2013, 03, 10), QTime(10, 0, 0), Qt::UTC); const QDateTime end(QDate(2013, 03, 10), QTime(11, 0, 0), Qt::UTC); KCalendarCore::Event::Ptr event(new KCalendarCore::Event()); event->setUid(QStringLiteral("event")); event->setDtStart(start); event->recurrence()->setHourly(1); event->recurrence()->setDuration(2); QList expectedEventOccurrences; expectedEventOccurrences << start << start.addSecs(60 * 60); const auto timesInInterval = event->recurrence()->timesInInterval(start, end); // qDebug() << "timesInInterval " << timesInInterval; for (const auto &dt : timesInInterval) { // qDebug() << dt; QCOMPARE(expectedEventOccurrences.removeAll(dt), 1); } QCOMPARE(expectedEventOccurrences.size(), 0); } //Test that the recurrence dtStart is used for calculation and not the interval start date void TimesInIntervalTest::testSubDailyRecurrence2() { const QDateTime start(QDate(2013, 03, 10), QTime(10, 2, 3), Qt::UTC); const QDateTime end(QDate(2013, 03, 10), QTime(13, 4, 5), Qt::UTC); KCalendarCore::Event::Ptr event(new KCalendarCore::Event()); event->setUid(QStringLiteral("event")); event->setDtStart(start); event->recurrence()->setHourly(1); event->recurrence()->setDuration(2); QList expectedEventOccurrences; expectedEventOccurrences << start << start.addSecs(60 * 60); const auto timesInInterval = event->recurrence()->timesInInterval(start.addSecs(-20), end.addSecs(20)); // qDebug() << "timesInInterval " << timesInInterval; for (const auto &dt : timesInInterval) { // qDebug() << dt; QCOMPARE(expectedEventOccurrences.removeAll(dt), 1); } QCOMPARE(expectedEventOccurrences.size(), 0); } void TimesInIntervalTest::testSubDailyRecurrenceIntervalLimits() { const QDateTime start(QDate(2013, 03, 10), QTime(10, 2, 3), Qt::UTC); const QDateTime end(QDate(2013, 03, 10), QTime(12, 2, 3), Qt::UTC); KCalendarCore::Event::Ptr event(new KCalendarCore::Event()); event->setUid(QStringLiteral("event")); event->setDtStart(start); event->recurrence()->setHourly(1); event->recurrence()->setDuration(3); QList expectedEventOccurrences; expectedEventOccurrences << start.addSecs(60 * 60); const auto timesInInterval = event->recurrence()->timesInInterval(start.addSecs(1), end.addSecs(-1)); // qDebug() << "timesInInterval " << timesInInterval; for (const auto &dt : timesInInterval) { // qDebug() << dt; QCOMPARE(expectedEventOccurrences.removeAll(dt), 1); } QCOMPARE(expectedEventOccurrences.size(), 0); } void TimesInIntervalTest::testLocalTimeHandlingNonAllDay() { // Create an event which occurs every weekday of every week, // starting from Friday the 11th of October, from 12 pm until 1 pm, clock time, // and lasts for two weeks, with three exception datetimes, // (only two of which will apply). QTimeZone anotherZone(QTimeZone::systemTimeZoneId().contains("Toronto") ? QTimeZone(QByteArray("Pacific/Midway")) : QTimeZone(QByteArray("America/Toronto"))); Event event; event.setAllDay(false); event.setDtStart(QDateTime(QDate(2019, 10, 11), QTime(12, 0), Qt::LocalTime)); RecurrenceRule * const rule = new RecurrenceRule(); rule->setRecurrenceType(RecurrenceRule::rDaily); rule->setStartDt(event.dtStart()); rule->setFrequency(1); rule->setDuration(14); rule->setByDays(QList() << RecurrenceRule::WDayPos(0, 1) // Monday << RecurrenceRule::WDayPos(0, 2) // Tuesday << RecurrenceRule::WDayPos(0, 3) // Wednesday << RecurrenceRule::WDayPos(0, 4) // Thursday << RecurrenceRule::WDayPos(0, 5)); // Friday Recurrence *recurrence = event.recurrence(); recurrence->addRRule(rule); // 12 o'clock in local time, will apply. recurrence->addExDateTime(QDateTime(QDate(2019, 10, 15), QTime(12, 0), Qt::LocalTime)); // 12 o'clock in another time zone, will not apply. recurrence->addExDateTime(QDateTime(QDate(2019, 10, 17), QTime(12, 0), anotherZone)); // The time in another time zone, corresponding to 12 o'clock in the system time zone, will apply. recurrence->addExDateTime(QDateTime(QDate(2019, 10, 24), QTime(12, 00), QTimeZone::systemTimeZone()).toTimeZone(anotherZone)); // Expand the events and within a wide interval const DateTimeList timesInInterval = recurrence->timesInInterval(QDateTime(QDate(2019, 10, 05), QTime(0, 0)), QDateTime(QDate(2019, 10, 25), QTime(23, 59))); // ensure that the expansion does not include weekend days, // nor either of the exception date times. const QList expectedDays { 11, 14, 16, 17, 18, 21, 22, 23, 25 }; for (int day : expectedDays) { QVERIFY(timesInInterval.contains(QDateTime(QDate(2019, 10, day), QTime(12, 0), Qt::LocalTime))); } QCOMPARE(timesInInterval.size(), expectedDays.size()); } void TimesInIntervalTest::testLocalTimeHandlingAllDay() { // Create an event which occurs every weekday of every week, // starting from Friday the 11th of October, and lasts for two weeks, // with four exception datetimes (only three of which will apply). QTimeZone anotherZone(QTimeZone::systemTimeZoneId().contains("Toronto") ? QTimeZone(QByteArray("Pacific/Midway")) : QTimeZone(QByteArray("America/Toronto"))); Event event; event.setAllDay(true); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + event.setDtStart(QDate(2019, 10, 11).startOfDay()); +#else event.setDtStart(QDateTime(QDate(2019, 10, 11))); +#endif RecurrenceRule * const rule = new RecurrenceRule(); rule->setRecurrenceType(RecurrenceRule::rDaily); rule->setStartDt(event.dtStart()); rule->setFrequency(1); rule->setDuration(14); rule->setByDays(QList() << RecurrenceRule::WDayPos(0, 1) // Monday << RecurrenceRule::WDayPos(0, 2) // Tuesday << RecurrenceRule::WDayPos(0, 3) // Wednesday << RecurrenceRule::WDayPos(0, 4) // Thursday << RecurrenceRule::WDayPos(0, 5)); // Friday Recurrence *recurrence = event.recurrence(); recurrence->addRRule(rule); // A simple date, will apply. recurrence->addExDate(QDate(2019, 10, 14)); // A date only local time, will apply. +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + recurrence->addExDateTime(QDate(2019, 10, 15).startOfDay()); +#else recurrence->addExDateTime(QDateTime(QDate(2019, 10, 15))); +#endif // A date time starting at 00:00 in another zone, will not apply. recurrence->addExDateTime(QDateTime(QDate(2019, 10, 17), QTime(), anotherZone)); // A date time starting at 00:00 in the system time zone, will apply. recurrence->addExDateTime(QDateTime(QDate(2019, 10, 24), QTime(), QTimeZone::systemTimeZone())); // Expand the events and within a wide interval const DateTimeList timesInInterval = recurrence->timesInInterval(QDateTime(QDate(2019, 10, 05), QTime(0, 0)), QDateTime(QDate(2019, 10, 25), QTime(23, 59))); // ensure that the expansion does not include weekend days, // nor either of the exception date times. const QList expectedDays { 11, 16, 17, 18, 21, 22, 23, 25 }; for (int day : expectedDays) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QVERIFY(timesInInterval.contains(QDate(2019, 10, day).startOfDay())); +#else QVERIFY(timesInInterval.contains(QDateTime(QDate(2019, 10, day)))); +#endif } QCOMPARE(timesInInterval.size(), expectedDays.size()); }