1 /*
2   This file is part of the kcalcore library.
3 
4   SPDX-FileCopyrightText: 2006, 2008 Allen Winter <winter@kde.org>
5 
6   SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 #include "testtodo.h"
9 #include "attachment.h"
10 #include "event.h"
11 #include "sorting.h"
12 #include "todo.h"
13 
14 #include <QTest>
15 QTEST_MAIN(TodoTest)
16 
17 using namespace KCalendarCore;
18 
initTestCase()19 void TodoTest::initTestCase()
20 {
21     qputenv("TZ", "UTC");
22 }
23 
testValidity()24 void TodoTest::testValidity()
25 {
26     QDate dt = QDate::currentDate();
27     Todo todo;
28     todo.setDtStart(QDateTime(dt, {}));
29     todo.setDtDue(QDateTime(dt, {}).addDays(1));
30     todo.setSummary(QStringLiteral("To-do1 Summary"));
31     todo.setDescription(QStringLiteral("This is a description of the first to-do"));
32     todo.setLocation(QStringLiteral("the place"));
33     todo.setPercentComplete(5);
34 
35     QCOMPARE(todo.summary(), QStringLiteral("To-do1 Summary"));
36     QCOMPARE(todo.location(), QStringLiteral("the place"));
37     QCOMPARE(todo.percentComplete(), 5);
38 }
39 
testCompare()40 void TodoTest::testCompare()
41 {
42     QDate dt = QDate::currentDate();
43     Todo todo1;
44     todo1.setDtStart(QDateTime(dt, {}));
45     todo1.setDtDue(QDateTime(dt, {}).addDays(1));
46     todo1.setSummary(QStringLiteral("To-do1 Summary"));
47     todo1.setDescription(QStringLiteral("This is a description of the first to-do"));
48     todo1.setLocation(QStringLiteral("the place"));
49     todo1.setCompleted(true);
50 
51     Todo todo2;
52     todo2.setDtStart(QDateTime(dt, {}).addDays(1));
53     todo2.setDtDue(QDateTime(dt, {}).addDays(2));
54     todo2.setSummary(QStringLiteral("To-do2 Summary"));
55     todo2.setDescription(QStringLiteral("This is a description of the second to-do"));
56     todo2.setLocation(QStringLiteral("the other place"));
57     todo2.setCompleted(false);
58 
59     QVERIFY(!(todo1 == todo2));
60     QCOMPARE(todo1.dtDue(), todo2.dtStart());
61     QCOMPARE(todo2.summary(), QStringLiteral("To-do2 Summary"));
62     QVERIFY(!(todo1.isCompleted() == todo2.isCompleted()));
63 }
64 
testClone()65 void TodoTest::testClone()
66 {
67     QDate dt = QDate::currentDate();
68     Todo todo1;
69     todo1.setDtStart(QDateTime(dt, {}));
70     todo1.setDtDue(QDateTime(dt, {}).addDays(1));
71     todo1.setSummary(QStringLiteral("Todo1 Summary"));
72     todo1.setDescription(QStringLiteral("This is a description of the first todo"));
73     todo1.setLocation(QStringLiteral("the place"));
74 
75     Todo *todo2 = todo1.clone();
76     QCOMPARE(todo1.summary(), todo2->summary());
77     QCOMPARE(todo1.dtStart(), todo2->dtStart());
78     QCOMPARE(todo1.dtDue(), todo2->dtDue());
79     QCOMPARE(todo1.description(), todo2->description());
80     QCOMPARE(todo1.location(), todo2->location());
81     QCOMPARE(todo1.isCompleted(), todo2->isCompleted());
82     delete todo2;
83 }
84 
testCopyIncidence()85 void TodoTest::testCopyIncidence()
86 {
87     QDate dt = QDate::currentDate();
88     Event event;
89     event.setDtStart(QDateTime(dt, {}));
90     event.setSummary(QStringLiteral("Event1 Summary"));
91     event.setDescription(QStringLiteral("This is a description of the first event"));
92     event.setLocation(QStringLiteral("the place"));
93 
94     Todo todo(event);
95     QCOMPARE(todo.uid(), event.uid());
96     QCOMPARE(todo.dtStart(), event.dtStart());
97     QCOMPARE(todo.summary(), event.summary());
98     QCOMPARE(todo.description(), event.description());
99     QCOMPARE(todo.location(), event.location());
100 }
101 
testCopyConstructor()102 void TodoTest::testCopyConstructor()
103 {
104     QDate dt = QDate::currentDate();
105     Todo todo1;
106     todo1.setDtStart(QDateTime(dt, {}));
107     todo1.setDtDue(QDateTime(dt, {}).addDays(1));
108     todo1.setSummary(QStringLiteral("Todo1 Summary"));
109     todo1.setDescription(QStringLiteral("This is a description of the first todo"));
110     todo1.setLocation(QStringLiteral("the place"));
111 
112     Todo todo2 {todo1};
113     QVERIFY(todo1 == todo2);
114 }
115 
testAssign()116 void TodoTest::testAssign()
117 {
118     QDate dt = QDate::currentDate();
119     Todo todo1;
120     todo1.setDtStart(QDateTime(dt, {}));
121     todo1.setDtDue(QDateTime(dt, {}).addDays(1));
122     todo1.setSummary(QStringLiteral("Todo1 Summary"));
123     todo1.setDescription(QStringLiteral("This is a description of the first todo"));
124     todo1.setLocation(QStringLiteral("the place"));
125 
126     IncidenceBase *todo2 = new Todo;
127     *todo2 = todo1;     // Use IncidenceBase's virtual assignment.
128     QVERIFY(todo1 == *todo2);
129 }
130 
testSetCompletedWithDate()131 void TodoTest::testSetCompletedWithDate()
132 {
133     Todo t;
134     t.setStatus(Incidence::StatusNone);
135     QVERIFY(!t.isCompleted());
136     QVERIFY(!t.hasCompletedDate());
137     QVERIFY(t.status() != Incidence::StatusCompleted);
138     QVERIFY(t.percentComplete() != 100);
139 
140     const auto now = QDateTime::currentDateTimeUtc();
141     t.setCompleted(now);
142     QVERIFY(t.isCompleted());
143     QVERIFY(t.completed() == now);
144     QVERIFY(t.hasCompletedDate());
145     QVERIFY(t.status() == Incidence::StatusNone);
146     QVERIFY(t.percentComplete() == 100);
147 }
148 
testSetCompletedWithoutDate()149 void TodoTest::testSetCompletedWithoutDate()
150 {
151     Todo t;
152     t.setStatus(Incidence::StatusNeedsAction);
153     QVERIFY(!t.isCompleted());
154     QVERIFY(!t.hasCompletedDate());
155     QVERIFY(t.status() != Incidence::StatusCompleted);
156     QVERIFY(t.percentComplete() != 100);
157 
158     t.setCompleted(QDateTime());
159     QVERIFY(t.isCompleted());
160     QVERIFY(!t.hasCompletedDate());
161     QVERIFY(t.status() == Incidence::StatusCompleted);
162     QVERIFY(t.percentComplete() == 100);
163 }
164 
testSetCompleted()165 void TodoTest::testSetCompleted()
166 {
167     Todo todo1;
168     todo1.setSummary(QStringLiteral("Todo Summary"));
169     QDateTime today = QDateTime::currentDateTimeUtc();
170 
171     // due yesterday
172     QDateTime originalDueDate = today.addDays(-1);
173 
174     todo1.setDtStart(originalDueDate);
175     todo1.setDtDue(originalDueDate);
176     todo1.recurrence()->setDaily(1);
177     todo1.setCompleted(today);
178     QVERIFY(originalDueDate != todo1.dtDue());
179     QVERIFY(!todo1.isCompleted());
180 }
181 
testSetCompletedBool()182 void TodoTest::testSetCompletedBool()
183 {
184     Todo t;
185     QVERIFY(!t.isCompleted());
186     QVERIFY(!t.hasCompletedDate());
187     QVERIFY(t.status() != Incidence::StatusCompleted);
188     QVERIFY(t.percentComplete() != 100);
189 
190     t.setCompleted(true);
191     QVERIFY(t.isCompleted());
192     QVERIFY(!t.hasCompletedDate());
193     QVERIFY(t.status() == Incidence::StatusCompleted);
194     QVERIFY(t.percentComplete() == 100);
195 
196     const auto yesterday = QDateTime::currentDateTimeUtc().addDays(-1);
197     t.setCompleted(yesterday);
198     t.setCompleted(true);
199     QVERIFY(t.isCompleted());
200     QVERIFY(t.completed() == yesterday);
201     QVERIFY(t.status() == Incidence::StatusCompleted);
202     QVERIFY(t.percentComplete() == 100);
203 
204     t.setCompleted(false);
205     QVERIFY(!t.isCompleted());
206     QVERIFY(!t.hasCompletedDate());
207     QVERIFY(t.status() == Incidence::StatusNone);
208     QVERIFY(t.percentComplete() == 0);
209 }
210 
testSetPercent()211 void TodoTest::testSetPercent()
212 {
213     Todo t;
214     t.setStatus(Incidence::StatusCompleted);
215     t.setCompleted(QDateTime::currentDateTimeUtc());
216     t.setPercentComplete(100);
217     QVERIFY(t.percentComplete() == 100);
218     QVERIFY(t.isCompleted());
219 
220     // Completion reset if necessary.
221     QVERIFY(t.hasCompletedDate());
222     t.setPercentComplete(99);
223     QVERIFY(t.percentComplete() == 99);
224     QVERIFY(!t.isCompleted());
225     QVERIFY(!t.hasCompletedDate());
226     QVERIFY(t.status() != Incidence::StatusCompleted);
227 
228     t.setPercentComplete(0);
229     QVERIFY(t.percentComplete() == 0);
230     QVERIFY(!t.isCompleted());
231     QVERIFY(!t.hasCompletedDate());
232     QVERIFY(t.status() != Incidence::StatusCompleted);
233 }
234 
testStatus()235 void TodoTest::testStatus()
236 {
237     QDateTime today = QDateTime::currentDateTimeUtc();
238     QDateTime yesterday = today.addDays(-1);
239 
240     Todo todo1;
241     todo1.setAllDay(true);
242     todo1.setDtStart(yesterday);
243     todo1.setDtDue(today);
244     todo1.setPercentComplete(50);
245     QVERIFY(todo1.isInProgress(false));
246     QVERIFY(!todo1.isNotStarted(false));
247     QVERIFY(!todo1.isOverdue());
248     todo1.setPercentComplete(100);
249     QVERIFY(todo1.isCompleted());
250 
251     Todo todo2 = todo1;
252     todo2.setPercentComplete(33);
253     todo2.setDtDue(QDateTime());
254     QVERIFY(todo2.isOpenEnded());
255 }
256 
testSerializer_data()257 void TodoTest::testSerializer_data()
258 {
259     QTest::addColumn<KCalendarCore::Todo::Ptr>("todo");
260 
261     QDateTime today = QDateTime::currentDateTimeUtc();
262     QDateTime yesterday = today.addDays(-1);
263 
264     Todo::Ptr todo1 = Todo::Ptr(new Todo());
265     Todo::Ptr todo2 = Todo::Ptr(new Todo());
266     Todo::Ptr todo3 = Todo::Ptr(new Todo());
267     Todo::Ptr todo4 = Todo::Ptr(new Todo());
268     Todo::Ptr todo5 = Todo::Ptr(new Todo());
269     Todo::Ptr todo6 = Todo::Ptr(new Todo());
270 
271     todo1->setSummary(QStringLiteral("Summary"), false);
272     todo1->setDescription(QStringLiteral("description"), false);
273     todo1->setCreated(yesterday);
274     todo1->setRevision(50);
275     todo1->setDtDue(yesterday);
276     todo1->setDtStart(today);
277     todo1->setPercentComplete(50);
278     todo1->setLocation(QStringLiteral("<b>location</b>"), false);
279 
280     todo2->setDescription(QStringLiteral("<b>description</b>"), true);
281     todo2->setSummary(QStringLiteral("<b>Summary2</b>"), true);
282     todo2->setLocation(QStringLiteral("<b>location</b>"), true);
283     todo2->setDtDue(yesterday);
284     todo2->setPercentComplete(100);
285 
286     todo3->setDtStart(today);
287     todo3->setPercentComplete(100);
288     todo3->setCategories(QStringList() << QStringLiteral("a") << QStringLiteral("b") << QStringLiteral("c") << QStringLiteral("d"));
289     todo3->setResources(QStringList() << QStringLiteral("a") << QStringLiteral("b") << QStringLiteral("c") << QStringLiteral("d"));
290     todo3->setPriority(5);
291 
292     QVERIFY(!todo4->dirtyFields().contains(IncidenceBase::FieldRecurrence));
293     todo4->recurrence()->setDaily(1);
294     QVERIFY(todo4->dirtyFields().contains(IncidenceBase::FieldRecurrence));
295 
296     Attachment attachment(QStringLiteral("http://www.kde.org"));
297     todo4->addAttachment(attachment);
298 
299     todo5->recurrence()->setDaily(1);
300     todo5->setCompleted(today);
301     todo5->setStatus(Incidence::StatusDraft);
302     todo5->setSecrecy(Incidence::SecrecyPrivate);
303     todo5->setRelatedTo(QStringLiteral("uid1"), Incidence::RelTypeParent);
304     todo5->setHasGeo(true);
305     todo5->setGeoLatitude(40);
306     todo5->setGeoLongitude(40);
307     todo5->setOrganizer(QStringLiteral("organizer@mail.com"));
308 
309     todo6->recurrence()->setDaily(1);
310     todo6->setCompleted(today);
311     todo6->setRecurrenceId(yesterday);
312     todo6->setStatus(Incidence::StatusDraft);
313     todo6->setSecrecy(Incidence::SecrecyPrivate);
314     todo6->setRelatedTo(QStringLiteral("uid1"), Incidence::RelTypeParent);
315     todo6->setHasGeo(true);
316     todo6->setGeoLatitude(40);
317     todo6->setGeoLongitude(40);
318     todo6->setUid(QStringLiteral("uid22"));
319     todo6->setLastModified(today);
320     todo6->addContact(QStringLiteral("addContact"));
321 
322     // Remaining properties tested in testevent.cpp
323 
324     QTest::newRow("todo1") << todo1;
325     QTest::newRow("todo2") << todo2;
326     QTest::newRow("todo3") << todo3;
327     QTest::newRow("todo4") << todo4;
328     QTest::newRow("todo5") << todo5;
329     QTest::newRow("todo6") << todo6;
330 }
331 
testSerializer()332 void TodoTest::testSerializer()
333 {
334     QFETCH(KCalendarCore::Todo::Ptr, todo);
335     IncidenceBase::Ptr incidenceBase = todo.staticCast<KCalendarCore::IncidenceBase>();
336 
337     QByteArray array;
338     QDataStream stream(&array, QIODevice::WriteOnly);
339     stream << incidenceBase;
340 
341     Todo::Ptr todo2 = Todo::Ptr(new Todo());
342     IncidenceBase::Ptr incidenceBase2 = todo2.staticCast<KCalendarCore::IncidenceBase>();
343     QVERIFY(*todo != *todo2);
344     QDataStream stream2(&array, QIODevice::ReadOnly);
345     stream2 >> incidenceBase2;
346     QVERIFY(*todo == *todo2);
347 }
348 
testRoles()349 void TodoTest::testRoles()
350 {
351     const QDateTime today = QDateTime::currentDateTimeUtc();
352     const QDateTime yesterday = today.addDays(-1);
353     Todo todo;
354     todo.setDtStart(today.addDays(-1));
355     todo.setDtDue(today);
356     QCOMPARE(todo.dateTime(Incidence::RoleDisplayStart), today);
357     QCOMPARE(todo.dateTime(Incidence::RoleDisplayEnd), today);
358     todo.setDtDue(QDateTime());
359     QCOMPARE(todo.dateTime(Incidence::RoleDisplayStart), yesterday);
360     QCOMPARE(todo.dateTime(Incidence::RoleDisplayEnd), yesterday);
361 }
362 
testIconNameOneoff()363 void TodoTest::testIconNameOneoff()
364 {
365     const QDateTime now = QDateTime::currentDateTime();
366     Todo todo;
367     todo.setDtStart(now);
368 
369     QCOMPARE(todo.iconName(), QLatin1String("view-calendar-tasks"));
370     todo.setCompleted(now);
371     QCOMPARE(todo.iconName(), QLatin1String("task-complete"));
372 }
373 
testIconNameRecurringNeverDue()374 void TodoTest::testIconNameRecurringNeverDue()
375 {
376     const QDateTime now = QDateTime::currentDateTime();
377     Todo todo;
378     todo.setDtStart(now);
379     todo.recurrence()->setDaily(1);
380 
381     QCOMPARE(todo.iconName(now), QLatin1String("view-calendar-tasks"));
382 
383     todo.setCompleted(now);
384     QCOMPARE(todo.iconName(now), QLatin1String("task-complete"));
385     QCOMPARE(todo.iconName(now.addDays(1)), QLatin1String("view-calendar-tasks"));
386 }
387 
testIconNameRecurringDue()388 void TodoTest::testIconNameRecurringDue()
389 {
390     const QDateTime now = QDateTime::currentDateTime();
391     const QDateTime later = now.addSecs(3600);
392     Todo todo;
393     todo.setDtStart(now);
394     todo.setDtDue(later, /*first=*/true);
395     todo.recurrence()->setDaily(1);
396 
397     QCOMPARE(todo.iconName(now), QLatin1String("view-calendar-tasks"));
398     QCOMPARE(todo.iconName(later), QLatin1String("view-calendar-tasks")); // Legacy case
399 
400     todo.setCompleted(now);
401     QCOMPARE(todo.iconName(now), QLatin1String("task-complete"));
402     QCOMPARE(todo.iconName(later), QLatin1String("task-complete")); // Legacy case
403     QCOMPARE(todo.iconName(now.addDays(1)), QLatin1String("view-calendar-tasks"));
404 }
405 
testCategoriesComparison()406 void TodoTest::testCategoriesComparison()
407 {
408     QSharedPointer<Todo> small(new Todo);
409     small->setCategories({QStringLiteral("alpha")});
410     small->setSummary(QStringLiteral("alpha"));
411 
412     QSharedPointer<Todo> medium(new Todo);
413     medium->setCategories({QStringLiteral("beta")});
414     medium->setSummary(QStringLiteral("beta 1"));
415 
416     QSharedPointer<Todo> large(new Todo);
417     large->setCategories({QStringLiteral("beta")});
418     large->setSummary(QStringLiteral("beta 2"));
419 
420     QVERIFY(Incidences::categoriesLessThan(small, medium));
421     QVERIFY(!Incidences::categoriesLessThan(medium, small));
422     QVERIFY(Incidences::categoriesLessThan(medium, large));
423     QVERIFY(!Incidences::categoriesLessThan(large, medium));
424     QVERIFY(!Incidences::categoriesLessThan(small, small));
425 
426     QVERIFY(!Incidences::categoriesMoreThan(small, medium));
427     QVERIFY(Incidences::categoriesMoreThan(medium, small));
428     QVERIFY(!Incidences::categoriesMoreThan(medium, large));
429     QVERIFY(Incidences::categoriesMoreThan(large, medium));
430     QVERIFY(!Incidences::categoriesMoreThan(small, small));
431 }
432 
testDtDueComparison()433 void TodoTest::testDtDueComparison()
434 {
435     auto now = QDateTime::currentDateTime();
436     auto later = now.addSecs(1);
437 
438     QSharedPointer<Todo> small(new Todo);
439     small->setDtDue(now);
440     small->setAllDay(false);
441     small->setSummary(QStringLiteral("now"));
442 
443     QSharedPointer<Todo> medium(new Todo);
444     medium->setDtDue(later);
445     medium->setAllDay(false);
446     medium->setSummary(QStringLiteral("later 1"));
447 
448     QSharedPointer<Todo> large(new Todo);
449     large->setDtDue(later);
450     large->setAllDay(false);
451     large->setSummary(QStringLiteral("later 2"));
452 
453     QSharedPointer<Todo> never(new Todo);
454     never->setDtDue(QDateTime());
455     never->setAllDay(false);
456     never->setSummary(QStringLiteral("never"));
457 
458     QVERIFY(Todos::dueDateLessThan(small, medium));
459     QVERIFY(!Todos::dueDateLessThan(medium, small));
460     QVERIFY(Todos::dueDateLessThan(medium, large));
461     QVERIFY(!Todos::dueDateLessThan(large, medium));
462     QVERIFY(Todos::dueDateLessThan(large, never));
463     QVERIFY(!Todos::dueDateLessThan(never, large));
464     QVERIFY(!Todos::dueDateLessThan(small, small));
465     QVERIFY(!Todos::dueDateLessThan(never, never));
466 
467     QVERIFY(!Todos::dueDateMoreThan(small, medium));
468     QVERIFY(Todos::dueDateMoreThan(medium, small));
469     QVERIFY(!Todos::dueDateMoreThan(medium, large));
470     QVERIFY(Todos::dueDateMoreThan(large, medium));
471     QVERIFY(!Todos::dueDateMoreThan(large, never));
472     QVERIFY(Todos::dueDateMoreThan(never, large));
473     QVERIFY(!Todos::dueDateMoreThan(small, small));
474     QVERIFY(!Todos::dueDateLessThan(never, never));
475 }
476 
477