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

0001 /*
0002   This file is part of the kcalcore library.
0003 
0004   SPDX-FileCopyrightText: 2011 Sérgio Martins <iamsergio@gmail.com>
0005 
0006   SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 #include "testrecurtodo.h"
0009 #include "todo.h"
0010 
0011 #include <QDebug>
0012 #include <QTest>
0013 
0014 QTEST_MAIN(RecurTodoTest)
0015 
0016 using namespace KCalendarCore;
0017 
0018 void RecurTodoTest::setTimeZone(const char *zonename)
0019 {
0020     QVERIFY(QTimeZone(zonename).isValid());
0021     qputenv("TZ", zonename);
0022     const QDateTime currentDateTime = QDateTime::currentDateTime();
0023     QVERIFY(currentDateTime.timeZone().isValid());
0024     QCOMPARE(currentDateTime.timeZoneAbbreviation(), QString::fromLatin1(zonename));
0025 }
0026 
0027 void RecurTodoTest::testAllDay()
0028 {
0029     setTimeZone("UTC");
0030     const QDate currentDate = QDate::currentDate();
0031     const QDateTime currentUtcDateTime = QDateTime::currentDateTimeUtc();
0032 
0033     const QDate dueDate(QDate::currentDate());
0034     QCOMPARE(currentDate, dueDate);
0035     QCOMPARE(currentDate, currentUtcDateTime.date());
0036 
0037     Todo todo;
0038     todo.setDtStart(QDateTime(dueDate.addDays(-1), {}));
0039     todo.setDtDue(QDateTime(dueDate, {}));
0040     todo.setSummary(QStringLiteral("All day event"));
0041     todo.setAllDay(true);
0042 
0043     QCOMPARE(todo.dtStart().daysTo(todo.dtDue()), 1);
0044     QVERIFY(!todo.recurs());
0045 
0046     Recurrence *recurrence = todo.recurrence();
0047     recurrence->unsetRecurs();
0048     recurrence->setDaily(1);
0049     QCOMPARE(todo.dtDue(), QDateTime(dueDate, {}));
0050     QCOMPARE(todo.percentComplete(), 0);
0051     QVERIFY(todo.recurs()); // Previously it did not recur
0052     todo.setCompleted(currentUtcDateTime);
0053     QVERIFY(todo.recurs());
0054     QCOMPARE(todo.percentComplete(), 0); // It is still not done
0055     const QDate newStartDate = todo.dtStart().date();
0056     const QDate newDueDate = todo.dtDue().date();
0057     QCOMPARE(newStartDate, currentDate);
0058     QCOMPARE(newStartDate.daysTo(newDueDate), 1);
0059 
0060     todo.setCompleted(currentUtcDateTime);
0061 
0062     QCOMPARE(newDueDate, currentDate.addDays(1));
0063     QCOMPARE(todo.dtDue(true /*first occurrence*/).date(), dueDate);
0064 }
0065 
0066 void RecurTodoTest::testRecurrenceStart()
0067 {
0068     setTimeZone("UTC");
0069     const QDateTime currentDateTime = QDateTime::currentDateTime();
0070     const QDate currentDate = currentDateTime.date();
0071     const QTime currentTimeWithMS = currentDateTime.time();
0072 
0073     const QDate fourDaysAgo(currentDate.addDays(-4));
0074     const QDate treeDaysAgo(currentDate.addDays(-3));
0075     const QTime currentTime(currentTimeWithMS.hour(), currentTimeWithMS.minute(), currentTimeWithMS.second());
0076 
0077     Todo todo;
0078     Recurrence *recurrence = todo.recurrence();
0079     recurrence->unsetRecurs();
0080     recurrence->setDaily(1);
0081     todo.setDtStart(QDateTime(fourDaysAgo, currentTime));
0082     const QDateTime originalDtDue(treeDaysAgo, currentTime);
0083     todo.setDtDue(originalDtDue);
0084     todo.setSummary(QStringLiteral("Not an all day event"));
0085     QVERIFY(!todo.allDay());
0086     QVERIFY(recurrence->startDateTime().isValid());
0087 }
0088 
0089 void RecurTodoTest::testNonAllDay()
0090 {
0091     setTimeZone("UTC");
0092     const QDateTime currentDateTime = QDateTime::currentDateTime();
0093     const QDate currentDate = currentDateTime.date();
0094     const QTime currentTimeWithMS = currentDateTime.time();
0095 
0096     const QDate fourDaysAgo(currentDate.addDays(-4));
0097     const QDate treeDaysAgo(currentDate.addDays(-3));
0098     const QTime currentTime(currentTimeWithMS.hour(), currentTimeWithMS.minute(), currentTimeWithMS.second());
0099 
0100     Todo todo;
0101     todo.setDtStart(QDateTime(fourDaysAgo, currentTime));
0102     const QDateTime originalDtDue(treeDaysAgo, currentTime);
0103     todo.setDtDue(originalDtDue);
0104     todo.setSummary(QStringLiteral("Not an all day event"));
0105     QVERIFY(!todo.allDay());
0106     Recurrence *recurrence = todo.recurrence();
0107     recurrence->unsetRecurs();
0108     recurrence->setDaily(1);
0109     QVERIFY(recurrence->startDateTime().isValid());
0110     QCOMPARE(todo.dtDue(), originalDtDue);
0111     todo.setCompleted(QDateTime::currentDateTimeUtc());
0112     QVERIFY(todo.recurs());
0113     QVERIFY(todo.percentComplete() == 0);
0114 
0115     const bool equal = todo.dtStart() == QDateTime(currentDate, currentTime, todo.dtStart().timeZone()).addDays(1);
0116     if (!equal) {
0117         qDebug() << "Test Failed. dtDue = " << todo.dtDue().toString() << "OriginalDtDue:" << originalDtDue.toString()
0118                  << "QDateTime:" << QDateTime(currentDate, currentTime, todo.dtDue().timeZone()).addDays(1).toString();
0119     }
0120 
0121     QVERIFY(equal);
0122 
0123     todo.setCompleted(QDateTime::currentDateTimeUtc());
0124     QCOMPARE(todo.dtStart(), QDateTime(currentDate, currentTime, todo.dtStart().timeZone()).addDays(2));
0125     QCOMPARE(todo.dtDue(true /*first occurrence*/), QDateTime(treeDaysAgo, currentTime));
0126 }
0127 
0128 void RecurTodoTest::testIsAllDay()
0129 {
0130     KCalendarCore::Todo::Ptr todo(new KCalendarCore::Todo());
0131     todo->setUid(QStringLiteral("todo"));
0132     todo->setDtStart(QDateTime(QDate(2013, 03, 10), QTime(10, 0, 0), QTimeZone::UTC));
0133     todo->setDtDue(QDateTime(QDate(2013, 03, 10), QTime(10, 0, 0), QTimeZone::UTC));
0134     todo->recurrence()->setDaily(1);
0135     todo->recurrence()->setDuration(2);
0136     QCOMPARE(todo->allDay(), false);
0137     QCOMPARE(todo->recurrence()->allDay(), false);
0138 
0139     KCalendarCore::Todo::Ptr allDay(new KCalendarCore::Todo());
0140     allDay->setUid(QStringLiteral("todo"));
0141     allDay->setDtStart(QDateTime(QDate(2013, 03, 10), {}, QTimeZone::UTC));
0142     allDay->setDtDue(QDateTime(QDate(2013, 03, 10), {}, QTimeZone::UTC));
0143     allDay->setAllDay(true);
0144     allDay->recurrence()->setDaily(1);
0145     allDay->recurrence()->setDuration(2);
0146     QCOMPARE(allDay->allDay(), true);
0147     QCOMPARE(allDay->recurrence()->allDay(), true);
0148 }
0149 
0150 void RecurTodoTest::testHasDueDate()
0151 {
0152     KCalendarCore::Todo::Ptr todo(new KCalendarCore::Todo());
0153     todo->setUid(QStringLiteral("todo"));
0154     todo->setDtStart(QDateTime(QDate(2013, 03, 10), QTime(10, 0, 0), QTimeZone::UTC));
0155     todo->recurrence()->setDaily(1);
0156     todo->recurrence()->setDuration(2);
0157     QVERIFY(!todo->hasDueDate());
0158 }
0159 
0160 void RecurTodoTest::testRecurTodo_data()
0161 {
0162     QTest::addColumn<QDateTime>("dtstart");
0163     QTest::addColumn<QDateTime>("dtdue");
0164 
0165     // Can't use QDateTime::currentDateTimeUtc() due to milliseconds mismatching
0166     const QDateTime today = QDateTime::fromSecsSinceEpoch(QDateTime::currentSecsSinceEpoch(), QTimeZone::UTC);
0167     const QDateTime tomorrow = today.addDays(1);
0168     const QDateTime invalid;
0169 
0170     QTest::newRow("valid dtstart") << today << invalid;
0171     QTest::newRow("valid dtstart and dtdue") << today << tomorrow;
0172     QTest::newRow("valid dtdue") << invalid << today;
0173 }
0174 
0175 void RecurTodoTest::testRecurTodo()
0176 {
0177     QFETCH(QDateTime, dtstart);
0178     QFETCH(QDateTime, dtdue);
0179 
0180     KCalendarCore::Todo::Ptr todo(new KCalendarCore::Todo());
0181     todo->setUid(QStringLiteral("todo"));
0182     todo->setDtStart(dtstart);
0183     todo->setDtDue(dtdue);
0184     todo->recurrence()->setDaily(1);
0185 
0186     const bool legacyMode = !dtstart.isValid();
0187     QCOMPARE(todo->percentComplete(), 0);
0188 
0189     // Recur it
0190     todo->setCompleted(QDateTime::currentDateTimeUtc());
0191     QCOMPARE(todo->percentComplete(), 0);
0192 
0193     if (legacyMode) {
0194         QVERIFY(todo->dtDue().isValid());
0195         QVERIFY(!todo->dtStart().isValid());
0196         QCOMPARE(todo->dtDue(), dtdue.addDays(1));
0197 
0198         QCOMPARE(todo->dtDue(/**first=*/true), dtdue);
0199     } else {
0200         QVERIFY(todo->dtStart().isValid());
0201         QVERIFY(!(todo->dtDue().isValid() ^ dtdue.isValid()));
0202         QCOMPARE(todo->dtStart(), dtstart.addDays(1));
0203 
0204         if (dtdue.isValid()) {
0205             const int delta = dtstart.daysTo(dtdue);
0206             QCOMPARE(todo->dtStart().daysTo(todo->dtDue()), delta);
0207         }
0208 
0209         QCOMPARE(todo->dtStart(/**first=*/true), dtstart);
0210     }
0211 }
0212 
0213 void RecurTodoTest::testDtStart()
0214 {
0215     QDateTime start(QDate(2013, 03, 10), QTime(10, 0, 0), QTimeZone::UTC);
0216     KCalendarCore::Todo::Ptr todo(new KCalendarCore::Todo());
0217     todo->setUid(QStringLiteral("todo"));
0218     todo->setDtStart(start);
0219     todo->recurrence()->setDaily(1);
0220     todo->recurrence()->setDuration(2);
0221     QCOMPARE(todo->dtStart(), start);
0222 
0223     KCalendarCore::Todo::Ptr todoWithDue(new KCalendarCore::Todo());
0224     todoWithDue->setUid(QStringLiteral("todoWithDue"));
0225     todoWithDue->setDtStart(start);
0226     todoWithDue->setDtDue(QDateTime(start).addSecs(60));
0227     todoWithDue->recurrence()->setDaily(1);
0228     todoWithDue->recurrence()->setDuration(2);
0229     QCOMPARE(todoWithDue->dtStart(), start);
0230 }
0231 
0232 void RecurTodoTest::testRecurrenceBasedOnDtStart()
0233 {
0234     const QDateTime dtstart(QDate(2013, 03, 10), QTime(10, 0, 0), QTimeZone::UTC);
0235     const QDateTime dtdue(QDate(2013, 03, 10), QTime(11, 0, 0), QTimeZone::UTC);
0236 
0237     KCalendarCore::Todo::Ptr todo(new KCalendarCore::Todo());
0238     todo->setUid(QStringLiteral("todo"));
0239     todo->setDtStart(dtstart);
0240     todo->setDtDue(dtdue);
0241     todo->recurrence()->setDaily(1);
0242     todo->recurrence()->setDuration(3);
0243 
0244     QCOMPARE(todo->recurrence()->getNextDateTime(dtstart.addMSecs(-1)), dtstart);
0245     QCOMPARE(todo->recurrence()->getNextDateTime(dtstart), dtstart.addDays(1));
0246     QCOMPARE(todo->recurrence()->getNextDateTime(dtstart.addDays(1)), dtstart.addDays(2));
0247     QCOMPARE(todo->recurrence()->getNextDateTime(dtstart.addDays(2)), QDateTime());
0248 }
0249 
0250 // For backwards compatibility only
0251 void RecurTodoTest::testRecurrenceBasedOnDue()
0252 {
0253     const QDateTime dtdue(QDate(2013, 03, 10), QTime(11, 0, 0), QTimeZone::UTC);
0254 
0255     KCalendarCore::Todo::Ptr todo(new KCalendarCore::Todo());
0256     todo->setUid(QStringLiteral("todo"));
0257     todo->setDtDue(dtdue);
0258     todo->recurrence()->setDaily(1);
0259     todo->recurrence()->setDuration(3);
0260 
0261     QCOMPARE(todo->recurrence()->getNextDateTime(dtdue.addMSecs(-1)), dtdue);
0262     QCOMPARE(todo->recurrence()->getNextDateTime(dtdue), dtdue.addDays(1));
0263     QCOMPARE(todo->recurrence()->getNextDateTime(dtdue.addDays(1)), dtdue.addDays(2));
0264     QCOMPARE(todo->recurrence()->getNextDateTime(dtdue.addDays(2)), QDateTime());
0265 }
0266 
0267 /** Test that occurrences specified by a recurrence rule are eliminated by
0268  * exception dates.
0269  */
0270 void RecurTodoTest::testRecurrenceExdates()
0271 {
0272     const QDateTime dtstart(QDate(2013, 03, 10), QTime(10, 0, 0), QTimeZone::UTC);
0273     const QDateTime dtdue(QDate(2013, 03, 10), QTime(11, 0, 0), QTimeZone::UTC);
0274 
0275     KCalendarCore::Todo::Ptr todo(new KCalendarCore::Todo());
0276     todo->setUid(QStringLiteral("todo"));
0277     todo->setDtStart(dtstart);
0278     todo->setDtDue(dtdue);
0279     todo->recurrence()->setDaily(1);
0280     todo->recurrence()->setDuration(3);
0281 
0282     // Test for boundary errors.
0283     todo->recurrence()->addExDateTime(dtstart);
0284     todo->recurrence()->addExDateTime(dtstart.addDays(2));
0285 
0286     QCOMPARE(todo->recurrence()->getNextDateTime(dtstart.addMSecs(-1)), dtstart.addDays(1));
0287     QCOMPARE(todo->recurrence()->getNextDateTime(dtstart.addDays(1)), QDateTime());
0288 }
0289 
0290 #include "moc_testrecurtodo.cpp"