1 /* This file is part of the KDE project
2 Copyright (C) 2006-2007 Dag Andersen <danders@get2net.dk>
3 Copyright (C) 2016 Dag Andersen <danders@get2net.dk>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19 */
20 // clazy:excludeall=qstring-arg
21 #include "CalendarTester.h"
22 #include "DateTimeTester.h"
23 #include <kptcalendar.h>
24 #include <kptdatetime.h>
25 #include <kptduration.h>
26 #include <kptmap.h>
27 #include "kptappointment.h"
28
29 #include <QTimeZone>
30 #include <QDateTime>
31
32 #include "debug.cpp"
33
34
35 namespace KPlato
36 {
37
createTimeZoneWithOffsetFromSystem(int hours,const QString & name,int * shiftDays)38 QTimeZone createTimeZoneWithOffsetFromSystem(int hours, const QString & name, int *shiftDays)
39 {
40 QTimeZone systemTimeZone = QTimeZone::systemTimeZone();
41 int systemOffsetSeconds = systemTimeZone.standardTimeOffset(QDateTime(QDate(1980, 1, 1), QTime(), Qt::UTC));
42 int offsetSeconds = systemOffsetSeconds + 3600 * hours;
43 if (offsetSeconds >= (12*3600)) {
44 qDebug() << "reducing offset by 24h";
45 offsetSeconds -= (24*3600);
46 *shiftDays = -1;
47 } else if (offsetSeconds <= -(12*3600)) {
48 qDebug() << "increasing offset by 24h";
49 offsetSeconds += (24*3600);
50 *shiftDays = 1;
51 } else {
52 *shiftDays = 0;
53 }
54 qDebug() << "creating timezone for offset" << hours << offsetSeconds << "systemoffset" << systemOffsetSeconds
55 << "shiftDays" << *shiftDays;
56 return QTimeZone(name.toLatin1(), offsetSeconds, name, name);
57 }
58
testSingleDay()59 void CalendarTester::testSingleDay() {
60 Calendar t("Test");
61 QDate wdate(2006,1,2);
62 DateTime before = DateTime(wdate.addDays(-1), QTime());
63 DateTime after = DateTime(wdate.addDays(1), QTime());
64 QTime t1(8,0,0);
65 QTime t2(10,0,0);
66 DateTime wdt1(wdate, t1);
67 DateTime wdt2(wdate, t2);
68 int length = t1.msecsTo(t2);
69 CalendarDay *day = new CalendarDay(wdate, CalendarDay::Working);
70 day->addInterval(TimeInterval(t1, length));
71 t.addDay(day);
72 QVERIFY(t.findDay(wdate) == day);
73
74 QVERIFY(t.hasInterval(after, DateTime(after.addDays(1))) == false);
75 QVERIFY(t.hasInterval(before, DateTime(before.addDays(-1))) == false);
76
77 QVERIFY(t.hasInterval(after, before) == false);
78 QVERIFY(t.hasInterval(before, after));
79
80 QVERIFY((t.firstAvailableAfter(after, DateTime(after.addDays(10)))).isValid() == false);
81 QVERIFY((t.firstAvailableBefore(before, DateTime(before.addDays(-10)))).isValid() == false);
82
83 QCOMPARE(t.firstAvailableAfter(before,after), wdt1);
84 QCOMPARE(t.firstAvailableBefore(after, before), wdt2);
85
86 Duration e(0, 2, 0);
87 QCOMPARE((t.effort(before, after)), e);
88 }
89
testWeekdays()90 void CalendarTester::testWeekdays() {
91 Calendar t("Test");
92 QDate wdate(2006,1,4); // wednesday
93 DateTime before = DateTime(wdate.addDays(-2), QTime());
94 DateTime after = DateTime(wdate.addDays(2), QTime());
95 QTime t1(8,0,0);
96 QTime t2(10,0,0);
97 int length = t1.msecsTo(t2);
98
99 CalendarDay *wd1 = t.weekday(Qt::Wednesday);
100 QVERIFY(wd1 != 0);
101
102 wd1->setState(CalendarDay::Working);
103 wd1->addInterval(TimeInterval(t1, length));
104
105 QCOMPARE(t.firstAvailableAfter(before, after), DateTime(QDate(2006, 1, 4), QTime(8,0,0)));
106 QCOMPARE((t.firstAvailableBefore(after, before)), DateTime(QDate(2006, 1, 4), QTime(10,0,0)));
107
108 QCOMPARE(t.firstAvailableAfter(after, DateTime(QDate(QDate(2006,1,14)), QTime())), DateTime(QDate(2006, 1, 11), QTime(8,0,0)));
109 QCOMPARE(t.firstAvailableBefore(before, DateTime(QDate(2005,12,25), QTime())), DateTime(QDate(2005, 12, 28), QTime(10,0,0)));
110 }
111
testCalendarWithParent()112 void CalendarTester::testCalendarWithParent() {
113 Calendar p("Test 3 parent");
114 Calendar t("Test 3");
115 t.setParentCal(&p);
116 QDate wdate(2006,1,2);
117 DateTime before = DateTime(wdate.addDays(-1), QTime());
118 DateTime after = DateTime(wdate.addDays(1), QTime());
119 QTime t1(8,0,0);
120 QTime t2(10,0,0);
121 int length = t1.msecsTo(t2);
122 DateTime wdt1(wdate, t1);
123 DateTime wdt2(wdate, t2);
124
125 CalendarDay *day = new CalendarDay(wdate, CalendarDay::Working);
126 day->addInterval(TimeInterval(t1, length));
127 p.addDay(day);
128 QVERIFY(p.findDay(wdate) == day);
129
130 //same tests as in testSingleDay()
131 QVERIFY(t.hasInterval(after, DateTime(after.addDays(1))) == false);
132 QVERIFY(t.hasInterval(before, DateTime(before.addDays(-1))) == false);
133
134 QVERIFY(t.hasInterval(after, before) == false);
135 QVERIFY(t.hasInterval(before, after));
136
137 QVERIFY((t.firstAvailableAfter(after, DateTime(after.addDays(10)))).isValid() == false);
138 QVERIFY((t.firstAvailableBefore(before, DateTime(before.addDays(-10)))).isValid() == false);
139
140 QVERIFY(t.firstAvailableAfter(before, after).isValid());
141 QVERIFY(t.firstAvailableBefore(after, before).isValid());
142
143 QCOMPARE(t.firstAvailableAfter(before,after), wdt1);
144 QCOMPARE(t.firstAvailableBefore(after, before), wdt2);
145
146 Duration e(0, 2, 0);
147 QCOMPARE((t.effort(before, after)), e);
148 }
149
testTimezone()150 void CalendarTester::testTimezone()
151 {
152 Calendar t("Test");
153 QDate wdate(2006,1,2);
154 DateTime before = DateTime(wdate.addDays(-1), QTime());
155 DateTime after = DateTime(wdate.addDays(1), QTime());
156 QTime t1(8,0,0);
157 QTime t2(10,0,0);
158 DateTime wdt1(wdate, t1);
159 DateTime wdt2(wdate, t2);
160 int length = t1.msecsTo(t2);
161 CalendarDay *day = new CalendarDay(wdate, CalendarDay::Working);
162 day->addInterval(TimeInterval(t1, length));
163 t.addDay(day);
164 Debug::print(&t, "Time zone testing");
165 QVERIFY(t.findDay(wdate) == day);
166
167 // local zone: Europe/Berlin (1 hours from London)
168 int loShiftDays;
169 QTimeZone lo = createTimeZoneWithOffsetFromSystem(-1, "DummyLondon", &loShiftDays);
170 QVERIFY(lo.isValid());
171 QDateTime dt1 = QDateTime(wdate, t1, lo).addDays(loShiftDays).addSecs(-2 * 3600);
172 QDateTime dt2 = QDateTime(wdate, t2, lo).addDays(loShiftDays).addSecs(0 * 3600);
173
174 qDebug()<<QDateTime(wdt1)<<QDateTime(wdt2);
175 qDebug()<<dt1<<dt2<<"("<<dt1.toLocalTime()<<dt2.toLocalTime()<<")";
176 QCOMPARE(t.firstAvailableAfter(DateTime(dt1), after), wdt1);
177 QCOMPARE(t.firstAvailableBefore(DateTime(dt2), before), wdt2);
178
179 Duration e(0, 2, 0);
180 QCOMPARE(t.effort(DateTime(dt1), DateTime(dt2)), e);
181
182 // local zone: Europe/Berlin (9 hours from America/Los_Angeles)
183 int laShiftDays;
184 QTimeZone la = createTimeZoneWithOffsetFromSystem(-9, "DummyLos_Angeles", &laShiftDays);
185 QVERIFY(la.isValid());
186 QDateTime dt3 = QDateTime(wdate, t1, la).addDays(laShiftDays).addSecs(-10 * 3600);
187 QDateTime dt4 = QDateTime(wdate, t2, la).addDays(laShiftDays).addSecs(-8 * 3600);
188
189 qDebug()<<QDateTime(wdt1)<<QDateTime(wdt2);
190 qDebug()<<dt3<<dt4<<"("<<dt3.toLocalTime()<<dt4.toLocalTime()<<")";
191 QCOMPARE(t.firstAvailableAfter(DateTime(dt3), after), wdt1);
192 QCOMPARE(t.firstAvailableBefore(DateTime(dt4), before), wdt2);
193
194 QCOMPARE(t.effort(DateTime(dt3), DateTime(dt4)), e);
195
196 QString s = "Test Cairo:";
197 qDebug()<<s;
198 // local zone: Europe/Berlin (1 hour from cairo)
199 int caShiftDays;
200 QTimeZone ca = createTimeZoneWithOffsetFromSystem(1, "DummyCairo", &caShiftDays);
201 QDateTime dt5 = QDateTime(wdate, t1, ca).addDays(caShiftDays).addSecs(0 * 3600);
202 QDateTime dt6 = QDateTime(wdate, t2, ca).addDays(caShiftDays).addSecs(2 * 3600);
203
204 qDebug()<<QDateTime(wdt1)<<QDateTime(wdt2);
205 qDebug()<<dt5<<dt6<<"("<<dt5.toLocalTime()<<dt6.toLocalTime()<<")";
206 QCOMPARE(t.firstAvailableAfter(DateTime(dt5), after), wdt1);
207 QCOMPARE(t.firstAvailableBefore(DateTime(dt6), before), wdt2);
208
209 QCOMPARE(t.effort(DateTime(dt5), DateTime(dt6)), e);
210 }
211
workIntervals()212 void CalendarTester::workIntervals()
213 {
214 Calendar t("Test");
215 QDate wdate(2006,1,2);
216 DateTime before = DateTime(wdate.addDays(-1), QTime());
217 DateTime after = DateTime(wdate.addDays(1), QTime());
218 QTime t1(8,0,0);
219 QTime t2(10,0,0);
220 DateTime wdt1(wdate, t1);
221 DateTime wdt2(wdate, t2);
222 int length = t1.msecsTo(t2);
223 CalendarDay *day = new CalendarDay(wdate, CalendarDay::Working);
224 day->addInterval(TimeInterval(t1, length));
225 t.addDay(day);
226 QVERIFY(t.findDay(wdate) == day);
227
228 AppointmentIntervalList lst = t.workIntervals(before, after, 100.);
229 QCOMPARE(lst.map().count(), 1);
230 QCOMPARE(wdate, lst.map().values().first().startTime().date());
231 QCOMPARE(t1, lst.map().values().first().startTime().time());
232 QCOMPARE(wdate, lst.map().values().first().endTime().date());
233 QCOMPARE(t2, lst.map().values().first().endTime().time());
234 QCOMPARE(100., lst.map().values().first().load());
235
236 QTime t3(12, 0, 0);
237 day->addInterval(TimeInterval(t3, length));
238
239 lst = t.workIntervals(before, after, 100.);
240 Debug::print(lst);
241 QCOMPARE(lst.map().count(), 2);
242 QCOMPARE(wdate, lst.map().values().first().startTime().date());
243 QCOMPARE(t1, lst.map().values().first().startTime().time());
244 QCOMPARE(wdate, lst.map().values().first().endTime().date());
245 QCOMPARE(t2, lst.map().values().first().endTime().time());
246 QCOMPARE(100., lst.map().values().first().load());
247
248 QCOMPARE(wdate, lst.map().values().at(1).startTime().date());
249 QCOMPARE(t3, lst.map().values().at(1).startTime().time());
250 QCOMPARE(wdate, lst.map().values().at(1).endTime().date());
251 QCOMPARE(t3.addMSecs(length), lst.map().values().at(1).endTime().time());
252 QCOMPARE(100., lst.map().values().at(1).load());
253
254 // add interval before the existing
255 QTime t4(5, 30, 0);
256 day->addInterval(TimeInterval(t4, length));
257
258 lst = t.workIntervals(before, after, 100.);
259 Debug::print(lst);
260 QCOMPARE(lst.map().count(), 3);
261 QCOMPARE(wdate, lst.map().values().first().startTime().date());
262 QCOMPARE(t4, lst.map().values().first().startTime().time());
263 QCOMPARE(wdate, lst.map().values().first().endTime().date());
264 QCOMPARE(t4.addMSecs(length), lst.map().values().first().endTime().time());
265 QCOMPARE(100., lst.map().values().first().load());
266
267 QCOMPARE(wdate, lst.map().values().at(1).startTime().date());
268 QCOMPARE(t1, lst.map().values().at(1).startTime().time());
269 QCOMPARE(wdate, lst.map().values().at(1).endTime().date());
270 QCOMPARE(t2, lst.map().values().at(1).endTime().time());
271 QCOMPARE(100., lst.map().values().at(1).load());
272
273 QCOMPARE(wdate, lst.map().values().at(2).startTime().date());
274 QCOMPARE(t3, lst.map().values().at(2).startTime().time());
275 QCOMPARE(wdate, lst.map().values().at(2).endTime().date());
276 QCOMPARE(t3.addMSecs(length), lst.map().values().at(2).endTime().time());
277 QCOMPARE(100., lst.map().values().at(2).load());
278 }
279
workIntervalsFullDays()280 void CalendarTester::workIntervalsFullDays()
281 {
282 Calendar t("Test");
283 QDate wdate(2006,1,2);
284 DateTime before = DateTime(wdate.addDays(-1), QTime());
285 DateTime after = DateTime(wdate.addDays(10), QTime());
286
287 CalendarDay *day = new CalendarDay(wdate, CalendarDay::Working);
288 day->addInterval(TimeInterval(QTime(0, 0, 0), 24*60*60*1000));
289 t.addDay(day);
290
291 QCOMPARE(day->numIntervals(), 1);
292 QVERIFY(day->timeIntervals().constFirst()->endsMidnight());
293
294 DateTime start = day->start();
295 DateTime end = day->end();
296
297 QCOMPARE(t.workIntervals(start, end, 100.).map().count(), 1);
298 QCOMPARE(t.workIntervals(before, after, 100.).map().count(), 1);
299
300 day = new CalendarDay(wdate.addDays(1), CalendarDay::Working);
301 day->addInterval(TimeInterval(QTime(0, 0, 0), 24*60*60*1000));
302 t.addDay(day);
303
304 end = day->end();
305
306 QCOMPARE(t.workIntervals(start, end, 100.).map().count(), 2);
307 QCOMPARE(t.workIntervals(before, after, 100.).map().count(), 2);
308
309 day = new CalendarDay(wdate.addDays(2), CalendarDay::Working);
310 day->addInterval(TimeInterval(QTime(0, 0, 0), 24*60*60*1000));
311 t.addDay(day);
312
313 end = day->end();
314
315 QCOMPARE(t.workIntervals(start, end, 100.).map().count(), 3);
316 QCOMPARE(t.workIntervals(before, after, 100.).map().count(), 3);
317
318 }
319
dstSpring()320 void CalendarTester::dstSpring()
321 {
322 QByteArray tz("TZ=Europe/Copenhagen");
323 putenv(tz.data());
324
325 Calendar t("DST");
326 QDate wdate(2016,3,27);
327
328 DateTime before = DateTime(wdate.addDays(-1), QTime());
329 DateTime after = DateTime(wdate.addDays(1), QTime());
330 QTime t1(0,0,0);
331 int length = 24*60*60*1000;
332
333 CalendarDay *wd1 = t.weekday(Qt::Sunday);
334 QVERIFY(wd1 != 0);
335
336 wd1->setState(CalendarDay::Working);
337 wd1->addInterval(TimeInterval(t1, length));
338 AppointmentIntervalList lst = t.workIntervals(before, after, 100.);
339 qDebug()<<lst;
340 QCOMPARE(lst.map().count(), 1);
341 QCOMPARE(lst.map().first().effort().toHours(), 23.); // clazy:exclude=detaching-temporary
342
343 wd1->clearIntervals();
344 qDebug()<<"clear list";
345 wd1->addInterval(TimeInterval(QTime(0,0,0), Duration(2., Duration::Unit_h).milliseconds()));
346 wd1->addInterval(TimeInterval(QTime(2,0,0), Duration(2., Duration::Unit_h).milliseconds()));
347
348 lst = t.workIntervals(before, after, 100.);
349 qDebug()<<"DST?"<<DateTime(wdate, QTime(2,0,0))<<endl<<lst;
350 QCOMPARE(lst.map().count(), 2);
351
352 AppointmentInterval ai = lst.map().values().value(0);
353 QCOMPARE(ai.startTime(), DateTime(wdate, QTime()));
354 QCOMPARE(ai.endTime(), DateTime(wdate, QTime(2,0,0)));
355 QCOMPARE(ai.effort().toHours(), 2.);
356
357 Debug::print(lst);
358 ai = lst.map().values().value(1);
359 QCOMPARE(ai.startTime(), DateTime(wdate, QTime(2,0,0)));
360 QCOMPARE(ai.endTime(), DateTime(wdate, QTime(4,0,0)));
361 QCOMPARE(ai.effort().toHours(), 1.); // Missing DST hour is skipped
362
363 unsetenv("TZ");
364 }
365
366 } //namespace KPlato
367
368 QTEST_GUILESS_MAIN(KPlato::CalendarTester)
369