1 /* This file is part of the KDE project
2 Copyright (C) 2009, 2010 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
21 // clazy:excludeall=qstring-arg
22 #include "kptappointment.h"
23 #include "kptcalendar.h"
24 #include "kptdatetime.h"
25 #include "kptproject.h"
26 #include "kptresource.h"
27 #include "kptnode.h"
28 #include "kpttask.h"
29 #include "kptschedule.h"
30
31 #include <QTest>
32 #include <QStringList>
33 #include <QString>
34
35
36 namespace QTest
37 {
38 template<>
toString(const KPlato::DateTime & dt)39 char *toString(const KPlato::DateTime &dt)
40 {
41 QString s;
42 switch (dt.timeSpec()) {
43 case Qt::LocalTime: s = " LocalTime"; break;
44 case Qt::UTC: s = " UTC"; break;
45 case Qt::OffsetFromUTC: s = " OffsetFromUTC"; break;
46 case Qt::TimeZone: s = " TimeZone (" + dt.timeZone().id() + ')'; break;
47 }
48 return toString(QString("%1T%2 %3").arg(dt.date().toString(Qt::ISODate), dt.time().toString("hh:mm:ss.zzz"), s));
49 }
50
51 template<>
toString(const KPlato::Duration & d)52 char *toString(const KPlato::Duration &d)
53 {
54 return toString(d.toString());
55 }
56 }
57
58 namespace KPlato
59 {
60
61 class Debug
62 {
63 public:
Debug()64 Debug() {}
65 static
print(Calendar * c,const QString & str,bool full=true)66 void print(Calendar *c, const QString &str, bool full = true) {
67 Q_UNUSED(full);
68 QTimeZone tz = c->timeZone();
69 QString s = tz.isValid() ? QString::fromLatin1(tz.id()) : QStringLiteral("LocalTime");
70
71 qDebug()<<"Debug info: Calendar"<<c->name()<<s<<str;
72 for (int wd = 1; wd <= 7; ++wd) {
73 CalendarDay *d = c->weekday(wd);
74 qDebug()<<" "<<wd<<':'<<d->stateToString(d->state());
75 foreach (TimeInterval *t, d->timeIntervals()) {
76 qDebug()<<" interval:"<<t->first<<t->second<<'('<<Duration(qint64(t->second)).toString()<<')';
77 }
78 }
79 foreach (const CalendarDay *d, c->days()) {
80 qDebug()<<" "<<d->date()<<':';
81 foreach (TimeInterval *t, d->timeIntervals()) {
82 qDebug()<<" interval:"<<t->first<<t->second;
83 }
84 }
85 }
86 static
printAvailable(Resource * r,const QString & lead=QString ())87 QString printAvailable(Resource *r, const QString &lead = QString()) {
88 QStringList sl;
89 sl<<lead<<"Available:"
90 <<(r->availableFrom().isValid()
91 ? r->availableFrom().toString()
92 : (r->project() ? ('('+r->project()->constraintStartTime().toString()+')') : QString()))
93 <<(r->availableUntil().isValid()
94 ? r->availableUntil().toString()
95 : (r->project() ? ('('+r->project()->constraintEndTime().toString()+')') : QString()))
96 <<QString::number(r->units())<<"%"
97 <<"cost: normal"<<QString::number(r->normalRate())<<" overtime"<<QString::number(r->overtimeRate());
98 return sl.join(" ");
99 }
100 static
print(Resource * r,const QString & str,bool full=true)101 void print(Resource *r, const QString &str, bool full = true) {
102 qDebug()<<"Debug info: Resource"<<r->name()<<str;
103 qDebug()<<"Parent group:"<<(r->parentGroup()
104 ? (r->parentGroup()->name() + " Type: "+ r->parentGroup()->typeToString())
105 : QString("None"));
106 qDebug()<<"Available:"
107 <<(r->availableFrom().isValid()
108 ? r->availableFrom().toString()
109 : (r->project() ? ('('+r->project()->constraintStartTime().toString()+')') : QString()))
110 <<(r->availableUntil().isValid()
111 ? r->availableUntil().toString()
112 : (r->project() ? ('('+r->project()->constraintEndTime().toString()+')') : QString()))
113 <<r->units()<<'%';
114 qDebug()<<"Type:"<<r->typeToString();
115 if (r->type() == Resource::Type_Team) {
116 qDebug()<<"Team members:"<<r->teamMembers().count();
117 foreach (Resource *tm, r->teamMembers()) {
118 qDebug()<<" "<<tm->name()<<"Available:"
119 <<(r->availableFrom().isValid()
120 ? r->availableFrom().toString()
121 : (r->project() ? ('('+r->project()->constraintStartTime().toString()+')') : QString()))
122 <<(r->availableUntil().isValid()
123 ? r->availableUntil().toString()
124 : (r->project() ? ('('+r->project()->constraintEndTime().toString()+')') : QString()))
125 <<r->units()<<'%';
126 }
127 } else {
128 Calendar *cal = r->calendar(true);
129 QString s;
130 if (cal) {
131 s = cal->name();
132 } else {
133 cal = r->calendar(false);
134 if (cal) {
135 s = cal->name() + " (Default)";
136 } else {
137 s = "No calendar";
138 }
139 }
140 qDebug()<<"Calendar:"<<s;
141 if (cal) {
142 print(cal, "Resource calendar");
143 }
144 }
145 if (! full) return;
146 qDebug()<<"External appointments:"<<r->numExternalAppointments();
147 foreach (Appointment *a, r->externalAppointmentList()) {
148 qDebug()<<" appointment:"<<a->startTime().toString()<<a->endTime().toString();
149 foreach(const AppointmentInterval &i, a->intervals().map()) {
150 qDebug()<<" "<<i.startTime().toString()<<i.endTime().toString()<<i.load();
151 }
152 }
153 }
154 static
print(Project * p,const QString & str,bool all=false)155 void print(Project *p, const QString &str, bool all = false) {
156 qDebug()<<"Debug info: Project"<<p->name()<<str;
157 qDebug()<<"project target start:"<<QTest::toString(QDateTime(p->constraintStartTime()));
158 qDebug()<<" project target end:"<<QTest::toString(QDateTime(p->constraintEndTime()));
159 if (p->isScheduled()) {
160 qDebug()<<" project start time:"<<QTest::toString(QDateTime(p->startTime()));
161 qDebug()<<" project end time:"<<QTest::toString(QDateTime(p->endTime()));
162 } else {
163 qDebug()<<" Not scheduled";
164 }
165
166 if (! all) {
167 return;
168 }
169 for (int i = 0; i < p->numChildren(); ++i) {
170 qDebug();
171 print(static_cast<Task*>(p->childNode(i)), true);
172 }
173 }
174 static
print(Project * p,Task * t,const QString & str,bool full=true)175 void print(Project *p, Task *t, const QString &str, bool full = true) {
176 Q_UNUSED(full);
177 print(p, str);
178 print(t);
179 }
180 static
print(Task * t,const QString & str,bool full=true)181 void print(Task *t, const QString &str, bool full = true) {
182 qDebug()<<"Debug info: Task"<<t->name()<<str;
183 print(t, full);
184 }
185 static
print(Task * t,bool full=true)186 void print(Task *t, bool full = true) {
187 QString pad;
188 if (t->level() > 0) {
189 pad = QString("%1").arg("", t->level()*2, ' ');
190 }
191 qDebug()<<pad<<"Task"<<t->name()<<t->typeToString()<<t->constraintToString();
192 if (t->isScheduled()) {
193 qDebug()<<pad<<" earlyStart:"<<QTest::toString(QDateTime(t->earlyStart()));
194 qDebug()<<pad<<" lateStart:"<<QTest::toString(QDateTime(t->lateStart()));
195 qDebug()<<pad<<" earlyFinish:"<<QTest::toString(QDateTime(t->earlyFinish()));
196 qDebug()<<pad<<" lateFinish:"<<QTest::toString(QDateTime(t->lateFinish()));
197 qDebug()<<pad<<" startTime:"<<QTest::toString(QDateTime(t->startTime()));
198 qDebug()<<pad<<" endTime:"<<QTest::toString(QDateTime(t->endTime()));
199 } else {
200 qDebug()<<pad<<" Not scheduled";
201 }
202 qDebug()<<pad;
203 switch (t->constraint()) {
204 case Node::MustStartOn:
205 case Node::StartNotEarlier:
206 qDebug()<<pad<<"startConstraint:"<<QTest::toString(QDateTime(t->constraintStartTime()));
207 break;
208 case Node::FixedInterval:
209 qDebug()<<pad<<"startConstraint:"<<QTest::toString(QDateTime(t->constraintStartTime()));
210 case Node::MustFinishOn:
211 case Node::FinishNotLater:
212 qDebug()<<pad<<" endConstraint:"<<QTest::toString(QDateTime(t->constraintEndTime()));
213 break;
214 default: break;
215 }
216 qDebug()<<pad<<"Estimate :"<<t->estimate()->expectedEstimate()<<Duration::unitToString(t->estimate()->unit())
217 <<t->estimate()->typeToString()
218 <<(t->estimate()->type() == Estimate::Type_Duration
219 ? (t->estimate()->calendar()?t->estimate()->calendar()->name():"Fixed")
220 : QString("%1 h").arg(t->estimate()->expectedValue().toDouble(Duration::Unit_h)));
221
222 foreach (ResourceGroupRequest *gr, t->requests().requests()) {
223 qDebug()<<pad<<"Group request:"<<gr->group()->name()<<gr->units();
224 foreach (ResourceRequest *rr, gr->resourceRequests()) {
225 qDebug()<<pad<<printAvailable(rr->resource(), " " + rr->resource()->name());
226 }
227 }
228 if (t->isStartNode()) {
229 qDebug()<<pad<<"Start node";
230 }
231 QStringList rel;
232 foreach (Relation *r, t->dependChildNodes()) {
233 QString type;
234 switch(r->type()) {
235 case Relation::StartStart: type = "SS"; break;
236 case Relation::FinishFinish: type = "FF"; break;
237 default: type = "FS"; break;
238 }
239 rel << QString("(%1) -> %2, %3 %4").arg(r->parent()->name()).arg(r->child()->name()).arg(type).arg(r->lag() == 0?QString():r->lag().toString(Duration::Format_HourFraction));
240 }
241 if (!rel.isEmpty()) {
242 qDebug()<<pad<<"Successors:"<<rel.join(" : ");
243 }
244 if (t->isEndNode()) {
245 qDebug()<<pad<<"End node";
246 }
247 rel.clear();
248 foreach (Relation *r, t->dependParentNodes()) {
249 QString type;
250 switch(r->type()) {
251 case Relation::StartStart: type = "SS"; break;
252 case Relation::FinishFinish: type = "FF"; break;
253 default: type = "FS"; break;
254 }
255 rel << QString("%1 -> (%2), %3 %4").arg(r->parent()->name()).arg(r->child()->name()).arg(type).arg(r->lag() == 0?QString():r->lag().toString(Duration::Format_HourFraction));
256 }
257 if (!rel.isEmpty()) {
258 qDebug()<<pad<<"Predeccessors:"<<rel.join(" : ");
259 }
260 qDebug()<<pad;
261 Schedule *s = t->currentSchedule();
262 if (s) {
263 qDebug()<<pad<<"Appointments:"<<s->appointments().count();
264 foreach (Appointment *a, s->appointments()) {
265 qDebug()<<pad<<" Resource:"<<a->resource()->resource()->name()<<"booked:"<<QTest::toString(QDateTime(a->startTime()))<<QTest::toString(QDateTime(a->endTime()))<<"effort:"<<a->effort(a->startTime(), a->endTime()).toDouble(Duration::Unit_h)<<'h';
266 if (! full) { continue; }
267 foreach(const AppointmentInterval &i, a->intervals().map()) {
268 qDebug()<<pad<<" "<<QTest::toString(QDateTime(i.startTime()))<<QTest::toString(QDateTime(i.endTime()))<<i.load()<<"effort:"<<i.effort(i.startTime(), i.endTime()).toDouble(Duration::Unit_h)<<'h';
269 }
270 }
271 }
272 if (t->runningAccount()) {
273 qDebug()<<pad<<"Running account :"<<t->runningAccount()->name();
274 }
275 if (t->startupAccount()) {
276 qDebug()<<pad<<"Startup account :"<<t->startupAccount()->name()<<" cost:"<<t->startupCost();
277 }
278 if (t->shutdownAccount()) {
279 qDebug()<<pad<<"Shutdown account:"<<t->shutdownAccount()->name()<<" cost:"<<t->shutdownCost();
280 }
281 if (full) {
282 for (int i = 0; i < t->numChildren(); ++i) {
283 qDebug()<<pad;
284 print(static_cast<Task*>(t->childNode(i)), full);
285 }
286 }
287 }
288 static
print(const Completion & c,const QString & name,const QString & s=QString ())289 void print(const Completion &c, const QString &name, const QString &s = QString()) {
290 qDebug()<<"Completion:"<<name<<s;
291 qDebug()<<" Entry mode:"<<c.entryModeToString();
292 qDebug()<<" Started:"<<c.isStarted()<<c.startTime();
293 qDebug()<<" Finished:"<<c.isFinished()<<c.finishTime();
294 qDebug()<<" Completion:"<<c.percentFinished()<<'%';
295
296 if (! c.usedEffortMap().isEmpty()) {
297 qDebug()<<" Used effort:";
298 foreach (const Resource *r, c.usedEffortMap().keys()) { // clazy:exclude=container-anti-pattern
299 Completion::UsedEffort *ue = c.usedEffort(r);
300 foreach (const QDate &d, ue->actualEffortMap().keys()) { // clazy:exclude=container-anti-pattern
301 qDebug()<<" "<<r->name()<<':';
302 qDebug()<<" "<<d.toString(Qt::ISODate)<<':'<<c.actualEffort(d).toString()<<c.actualCost(d);
303 }
304 }
305 }
306 }
307 static
print(const EffortCostMap & m,const QString & s=QString ())308 void print(const EffortCostMap &m, const QString &s = QString()) {
309 qDebug()<<"EffortCostMap"<<s;
310 if (m.days().isEmpty()) {
311 qDebug()<<" Empty";
312 return;
313 }
314 qDebug()<<m.startDate().toString(Qt::ISODate)<<m.endDate().toString(Qt::ISODate)
315 <<" total="
316 <<m.totalEffort().toString()
317 <<m.totalCost()
318 <<'('
319 <<m.bcwpTotalEffort()
320 <<m.bcwpTotalCost()
321 <<')';
322
323 if (! m.days().isEmpty()) {
324 foreach (const QDate &d, m.days().keys()) { // clazy:exclude=container-anti-pattern
325 qDebug()<<" "<<d.toString(Qt::ISODate)<<':'<<m.hoursOnDate(d)<<'h'<<m.costOnDate(d)
326 <<'('<<m.bcwpEffortOnDate(d)<<'h'<<m.bcwpCostOnDate(d)<<')';
327 }
328 }
329 }
330 static
printSchedulingLog(const ScheduleManager & sm,const QString & s)331 void printSchedulingLog(const ScheduleManager &sm, const QString &s)
332 {
333 qDebug()<<"Scheduling log"<<s;
334 qDebug()<<"Scheduling:"<<sm.name()<<(sm.recalculate()?QString("recalculate from %1").arg(sm.recalculateFrom().toString()):"");
335 foreach (const QString &s, sm.expected()->logMessages()) {
336 qDebug()<<s;
337 }
338 }
339 static
print(Account * a,long id=-1,const QString & s=QString ())340 void print(Account *a, long id = -1, const QString &s = QString())
341 {
342 qDebug()<<"Debug info Account:"<<a->name()<<s;
343 qDebug()<<"Account:"<<a->name()<<(a->isDefaultAccount() ? "Default" : "");
344 EffortCostMap ec = a->plannedCost(id);
345 qDebug()<<"Planned cost:"<<ec.totalCost()<<"effort:"<<ec.totalEffort().toString();
346 if (! a->isElement()) {
347 foreach (Account *c, a->accountList()) {
348 print(c);
349 }
350 return;
351 }
352 qDebug()<<"Cost places:";
353 foreach (Account::CostPlace *cp, a->costPlaces()) {
354 qDebug()<<" Node:"<<(cp->node() ? cp->node()->name() : "");
355 qDebug()<<" running:"<<cp->running();
356 qDebug()<<" startup:"<<cp->startup();
357 qDebug()<<" shutdown:"<<cp->shutdown();
358 }
359 }
360
361 static
print(const AppointmentInterval & i,const QString & indent=QString ())362 void print(const AppointmentInterval &i, const QString &indent = QString())
363 {
364 QString s = indent + "Interval:";
365 if (! i.isValid()) {
366 qDebug()<<s<<"Not valid";
367 } else {
368 qDebug()<<s<<i.startTime()<<i.endTime()<<i.load()<<'%';
369 }
370 }
371
372 static
print(const AppointmentIntervalList & lst,const QString & s=QString ())373 void print(const AppointmentIntervalList &lst, const QString &s = QString())
374 {
375 qDebug()<<"Interval list:"<<lst.map().count()<<s;
376 foreach (const AppointmentInterval &i, lst.map()) {
377 print(i, " ");
378 }
379 }
380
381 };
382
383 }
384