MailJDTourDownloadDocsQualityTestsCoverage

Which source lines and branches the tests execute. Lines 91.5 %, branches 75.0 % (minimum 85 % and 72 %). main @ 399ffe3d · 21 October 2026 · pipeline 7238.

GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 94.8% 276 / 0 / 291
Functions: 100.0% 19 / 0 / 19
Branches: 72.4% 452 / 0 / 624

data/ICalEventCodec.cpp
Line Branch Exec Source
1 #include "ICalEventCodec.h"
2
3 #include <QLoggingCategory>
4 #include <QSet>
5 #include <algorithm>
6
7
3/7
✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 8 not taken.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
2 Q_LOGGING_CATEGORY(lcEventCodec, "mailjd.calendar.codec")
8
9 namespace ICalEventCodec {
10
11 namespace {
12
13 694 bool sameInstant(const QDateTime &a, const QDateTime &b, bool dateOnly) {
14
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 692 times.
694 if (a.isValid() != b.isValid())
15 2 return false;
16
2/2
✓ Branch 7 → 8 taken 6 times.
✓ Branch 7 → 9 taken 686 times.
692 if (!a.isValid())
17 6 return true;
18
11/15
✓ Branch 9 → 10 taken 49 times.
✓ Branch 9 → 14 taken 637 times.
✓ Branch 10 → 11 taken 49 times.
✓ Branch 11 → 12 taken 49 times.
✓ Branch 14 → 15 taken 637 times.
✓ Branch 15 → 16 taken 637 times.
✓ Branch 16 → 17 taken 637 times.
✓ Branch 19 → 20 taken 637 times.
✓ Branch 19 → 21 taken 49 times.
✓ Branch 21 → 22 taken 637 times.
✓ Branch 21 → 23 taken 49 times.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
686 return dateOnly ? a.date() == b.date() : a.toUTC() == b.toUTC();
19 }
20
21 676 bool sameStamp(const QDateTime &a, bool aDate, const QDateTime &b, bool bDate) {
22
5/6
✓ Branch 2 → 3 taken 676 times.
✗ Branch 2 → 5 not taken.
✓ Branch 4 → 5 taken 123 times.
✓ Branch 4 → 6 taken 553 times.
✓ Branch 7 → 8 taken 123 times.
✓ Branch 7 → 9 taken 553 times.
676 if (aDate != bDate || !sameInstant(a, b, aDate))
23 123 return false;
24
9/14
✓ Branch 9 → 10 taken 501 times.
✓ Branch 9 → 14 taken 52 times.
✓ Branch 10 → 11 taken 501 times.
✓ Branch 11 → 12 taken 501 times.
✓ Branch 13 → 14 taken 501 times.
✗ Branch 13 → 15 not taken.
✓ Branch 17 → 18 taken 501 times.
✓ Branch 17 → 19 taken 52 times.
✓ Branch 19 → 20 taken 501 times.
✓ Branch 19 → 21 taken 52 times.
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 25 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
553 return aDate || ICalTime::zoneId(a, false) == ICalTime::zoneId(b, false);
25 }
26
27 1352 QSet<qint64> instantSet(const QList<QDateTime> &values, bool dateOnly) {
28 1352 QSet<qint64> set;
29
2/2
✓ Branch 17 → 5 taken 38 times.
✓ Branch 17 → 18 taken 1352 times.
1390 for (const QDateTime &value : values)
30
2/3
✓ Branch 6 → 7 taken 38 times.
✓ Branch 7 → 8 taken 38 times.
✗ Branch 7 → 15 not taken.
38 if (value.isValid())
31
3/5
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 11 taken 38 times.
✗ Branch 9 → 10 not taken.
✓ Branch 11 → 12 taken 38 times.
✓ Branch 13 → 14 taken 38 times.
38 set.insert(dateOnly ? value.date().toJulianDay() : value.toSecsSinceEpoch());
32 1352 return set;
33 ✗ }
34
35 862 QString nowStamp() {
36
2/2
✓ Branch 2 → 3 taken 862 times.
✓ Branch 6 → 7 taken 862 times.
1724 return QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyyMMdd'T'HHmmss'Z'"));
37 }
38
39 802 void setText(ICal::Component *component, const QString &name, const QString &value) {
40
2/2
✓ Branch 3 → 4 taken 260 times.
✓ Branch 3 → 6 taken 542 times.
802 if (value.isEmpty()) {
41 260 component->removeProperties(name);
42 260 return;
43 }
44 542 ICal::Property *existing = component->property(name);
45
2/2
✓ Branch 7 → 8 taken 162 times.
✓ Branch 7 → 12 taken 380 times.
542 if (existing) {
46 // Language and other parameters stay with the value they describe.
47
2/2
✓ Branch 8 → 9 taken 162 times.
✓ Branch 9 → 10 taken 162 times.
162 existing->setValue(ICal::escapeText(value));
48 162 return;
49 }
50
2/2
✓ Branch 12 → 13 taken 380 times.
✓ Branch 13 → 14 taken 380 times.
380 component->setPropertyValue(name, ICal::escapeText(value));
51 }
52
53 // The TZID parameter to write for `value`: the original text when it still
54 // names the same zone (keeps Outlook's "W. Europe Standard Time" and its
55 // VTIMEZONE), else the IANA id.
56 547 QString tzidFor(const QDateTime &value, const ICal::Property *original,
57 const ICalTime::ZoneResolver &resolver) {
58
2/2
✓ Branch 3 → 4 taken 34 times.
✓ Branch 3 → 5 taken 513 times.
547 if (value.timeSpec() != Qt::TimeZone)
59 34 return {};
60
2/2
✓ Branch 5 → 6 taken 124 times.
✓ Branch 5 → 33 taken 389 times.
513 if (original) {
61
1/1
✓ Branch 9 → 10 taken 124 times.
248 const QString tzid = original->parameter(QStringLiteral("TZID"));
62
11/16
✓ Branch 13 → 14 taken 122 times.
✓ Branch 13 → 19 taken 2 times.
✓ Branch 14 → 15 taken 122 times.
✓ Branch 15 → 16 taken 122 times.
✓ Branch 17 → 18 taken 122 times.
✗ Branch 17 → 19 not taken.
✓ Branch 20 → 21 taken 122 times.
✓ Branch 20 → 22 taken 2 times.
✓ Branch 22 → 23 taken 122 times.
✓ Branch 22 → 24 taken 2 times.
✓ Branch 24 → 25 taken 122 times.
✓ Branch 24 → 27 taken 2 times.
✗ Branch 47 → 48 not taken.
✗ Branch 47 → 49 not taken.
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 53 not taken.
124 if (!tzid.isEmpty() && resolver.resolve(tzid) == value.timeZone())
63 122 return tzid;
64 124 }
65
3/3
✓ Branch 33 → 34 taken 391 times.
✓ Branch 34 → 35 taken 391 times.
✓ Branch 35 → 36 taken 391 times.
391 return QString::fromUtf8(value.timeZone().id());
66 }
67
68 544 void writeDate(ICal::Component *component, const QString &name, const QDateTime &value,
69 bool dateOnly, const ICalTime::ZoneResolver &resolver) {
70
1/1
✓ Branch 2 → 3 taken 544 times.
544 const ICal::Property *original = component->property(name);
71 544 ICal::Property property;
72
2/2
✓ Branch 3 → 4 taken 121 times.
✓ Branch 3 → 5 taken 423 times.
544 if (original) {
73 121 property = *original;
74 } else {
75 423 property.name = name;
76 }
77
2/2
✓ Branch 6 → 7 taken 544 times.
✓ Branch 7 → 8 taken 544 times.
544 ICalTime::writeProperty(&property, value, dateOnly, tzidFor(value, original, resolver));
78
1/1
✓ Branch 9 → 10 taken 544 times.
544 component->setProperty(property);
79 544 }
80
81 424 void writeDateList(ICal::Component *component, const QString &name, const QList<QDateTime> &values,
82 bool dateOnly, const QDateTime &zoneSource,
83 const ICalTime::ZoneResolver &resolver) {
84
1/1
✓ Branch 5 → 6 taken 424 times.
848 const ICal::Property *anchor = component->property(QStringLiteral("DTSTART"));
85 424 component->removeProperties(name);
86
2/2
✓ Branch 48 → 11 taken 3 times.
✓ Branch 48 → 49 taken 424 times.
427 for (const QDateTime &value : values) {
87
2/3
✓ Branch 12 → 13 taken 3 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 3 times.
3 if (!value.isValid())
88 ✗ continue;
89 // Exclusions/additions follow DTSTART's zone so they match occurrences.
90 3 QDateTime zoned = value;
91
4/7
✓ Branch 16 → 17 taken 3 times.
✗ Branch 16 → 20 not taken.
✓ Branch 17 → 18 taken 3 times.
✓ Branch 18 → 19 taken 3 times.
✗ Branch 18 → 20 not taken.
✓ Branch 21 → 22 taken 3 times.
✗ Branch 21 → 28 not taken.
3 if (!dateOnly && zoneSource.timeSpec() == Qt::TimeZone)
92
2/2
✓ Branch 22 → 23 taken 3 times.
✓ Branch 23 → 24 taken 3 times.
3 zoned = value.toTimeZone(zoneSource.timeZone());
93 ✗ else if (!dateOnly && zoneSource.timeSpec() == Qt::LocalTime)
94 ✗ zoned = value.toLocalTime();
95 3 ICal::Property property;
96 3 property.name = name;
97
2/2
✓ Branch 39 → 40 taken 3 times.
✓ Branch 40 → 41 taken 3 times.
3 ICalTime::writeProperty(&property, zoned, dateOnly, tzidFor(zoned, anchor, resolver));
98
1/1
✓ Branch 42 → 43 taken 3 times.
3 component->appendProperty(property);
99 3 }
100 424 }
101
102 736 ICal::Property makeProperty(const QString &name, const QString &value) {
103 736 ICal::Property property;
104 736 property.name = name;
105 736 property.value = value;
106 736 return property;
107 }
108
109 // Every TZID referenced by the VEVENTs of `calendar`.
110 508 QSet<QString> referencedTzids(const ICal::Component &calendar) {
111 508 QSet<QString> tzids;
112
2/2
✓ Branch 30 → 5 taken 795 times.
✓ Branch 30 → 31 taken 508 times.
1303 for (const ICal::Component &component : calendar.components) {
113
2/2
✓ Branch 8 → 9 taken 269 times.
✓ Branch 8 → 10 taken 526 times.
795 if (component.name != QLatin1String("VEVENT"))
114 269 continue;
115
2/2
✓ Branch 26 → 12 taken 4457 times.
✓ Branch 26 → 27 taken 526 times.
4983 for (const ICal::Property &property : component.properties) {
116
1/1
✓ Branch 16 → 17 taken 4457 times.
4457 const QString tzid = property.parameter(QStringLiteral("TZID"));
117
2/2
✓ Branch 20 → 21 taken 817 times.
✓ Branch 20 → 23 taken 3640 times.
4457 if (!tzid.isEmpty())
118
1/1
✓ Branch 21 → 22 taken 817 times.
817 tzids.insert(tzid);
119 4457 }
120 }
121 508 return tzids;
122 ✗ }
123
124 // S106-838: a DATE EXDATE on a timed series excludes that day's occurrence
125 // (libical semantics). Exclusions match occurrences by instant, so the date
126 // takes DTSTART's time and zone; UTC midnight would exclude nothing.
127 47 QDateTime exclusionInstant(const ICalTime::Value &value, const CalendarEvent &event) {
128
9/12
✓ Branch 2 → 3 taken 6 times.
✓ Branch 2 → 8 taken 41 times.
✓ Branch 3 → 4 taken 6 times.
✗ Branch 3 → 8 not taken.
✓ Branch 4 → 5 taken 6 times.
✓ Branch 5 → 6 taken 6 times.
✗ Branch 5 → 8 not taken.
✓ Branch 6 → 7 taken 6 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 6 times.
✓ Branch 10 → 11 taken 41 times.
✓ Branch 10 → 12 taken 6 times.
47 if (!value.dateOnly || event.allDay || !event.dtStart.isValid() || !value.dateTime.isValid())
129 41 return value.dateTime;
130 6 QDateTime instant = event.dtStart;
131
2/2
✓ Branch 13 → 14 taken 6 times.
✓ Branch 14 → 15 taken 6 times.
6 instant.setDate(value.dateTime.date());
132 6 return instant;
133 6 }
134
135 } // namespace
136
137 1810 QString componentText(const ICal::Component &component) {
138 1810 return ICal::serialize(component);
139 }
140
141 1692 CalendarEvent eventFromComponent(const ICal::Component &vevent,
142 const ICalTime::ZoneResolver &resolver) {
143 1692 CalendarEvent event;
144
3/3
✓ Branch 6 → 7 taken 1692 times.
✓ Branch 7 → 8 taken 1692 times.
✓ Branch 8 → 9 taken 1692 times.
1692 event.uid = ICal::unescapeText(vevent.value(QStringLiteral("UID"))).trimmed();
145
3/3
✓ Branch 18 → 19 taken 1692 times.
✓ Branch 19 → 20 taken 1692 times.
✓ Branch 20 → 21 taken 1692 times.
1692 event.summary = ICal::unescapeText(vevent.value(QStringLiteral("SUMMARY"))).trimmed();
146
3/3
✓ Branch 30 → 31 taken 1692 times.
✓ Branch 31 → 32 taken 1692 times.
✓ Branch 32 → 33 taken 1692 times.
1692 event.description = ICal::unescapeText(vevent.value(QStringLiteral("DESCRIPTION"))).trimmed();
147
3/3
✓ Branch 42 → 43 taken 1692 times.
✓ Branch 43 → 44 taken 1692 times.
✓ Branch 44 → 45 taken 1692 times.
1692 event.location = ICal::unescapeText(vevent.value(QStringLiteral("LOCATION"))).trimmed();
148
2/2
✓ Branch 54 → 55 taken 1692 times.
✓ Branch 55 → 56 taken 1692 times.
1692 event.rrule = vevent.value(QStringLiteral("RRULE")).trimmed();
149
3/3
✓ Branch 64 → 65 taken 1692 times.
✓ Branch 67 → 68 taken 1691 times.
✓ Branch 67 → 72 taken 1 time.
1692 if (const ICal::Property *start = vevent.property(QStringLiteral("DTSTART"))) {
150
1/1
✓ Branch 68 → 69 taken 1691 times.
1691 const ICalTime::Value value = ICalTime::parseProperty(*start, resolver);
151 1691 event.dtStart = value.dateTime;
152 1691 event.allDay = value.dateOnly;
153 1691 }
154
3/3
✓ Branch 75 → 76 taken 1692 times.
✓ Branch 78 → 79 taken 1299 times.
✓ Branch 78 → 83 taken 393 times.
1692 if (const ICal::Property *end = vevent.property(QStringLiteral("DTEND"))) {
155
1/1
✓ Branch 79 → 80 taken 1299 times.
1299 event.dtEnd = ICalTime::parseProperty(*end, resolver).dateTime;
156
10/16
✓ Branch 86 → 87 taken 393 times.
✓ Branch 87 → 88 taken 57 times.
✓ Branch 87 → 91 taken 336 times.
✓ Branch 88 → 89 taken 57 times.
✓ Branch 89 → 90 taken 56 times.
✓ Branch 89 → 91 taken 1 time.
✓ Branch 92 → 93 taken 393 times.
✗ Branch 92 → 94 not taken.
✓ Branch 94 → 95 taken 393 times.
✗ Branch 94 → 96 not taken.
✓ Branch 96 → 97 taken 56 times.
✓ Branch 96 → 122 taken 337 times.
✗ Branch 288 → 289 not taken.
✗ Branch 288 → 290 not taken.
✗ Branch 292 → 293 not taken.
✗ Branch 292 → 294 not taken.
786 } else if (vevent.hasProperty(QStringLiteral("DURATION")) && event.dtStart.isValid()) {
157 56 qint64 seconds = 0;
158
8/20
✓ Branch 100 → 101 taken 56 times.
✓ Branch 101 → 102 taken 56 times.
✓ Branch 102 → 103 taken 56 times.
✗ Branch 102 → 105 not taken.
✓ Branch 103 → 104 taken 56 times.
✗ Branch 103 → 105 not taken.
✓ Branch 106 → 107 taken 56 times.
✗ Branch 106 → 108 not taken.
✓ Branch 108 → 109 taken 56 times.
✗ Branch 108 → 110 not taken.
✓ Branch 110 → 111 taken 56 times.
✗ Branch 110 → 112 not taken.
✓ Branch 112 → 113 taken 56 times.
✗ Branch 112 → 121 not taken.
✗ Branch 296 → 297 not taken.
✗ Branch 296 → 298 not taken.
✗ Branch 300 → 301 not taken.
✗ Branch 300 → 302 not taken.
✗ Branch 304 → 305 not taken.
✗ Branch 304 → 306 not taken.
112 if (parseDuration(vevent.value(QStringLiteral("DURATION")), &seconds) && seconds > 0)
159
2/6
✗ Branch 113 → 114 not taken.
✓ Branch 113 → 117 taken 56 times.
✗ Branch 114 → 115 not taken.
✗ Branch 114 → 117 not taken.
✗ Branch 115 → 116 not taken.
✓ Branch 117 → 118 taken 56 times.
112 event.dtEnd = event.allDay && seconds % 86400 == 0 ? event.dtStart.addDays(seconds / 86400)
160 56 : event.dtStart.addSecs(seconds);
161 }
162 // A DATE DTSTART without end lasts one day (RFC 5545 §3.6.1); the end
163 // stays unset here so the stored record matches the resource.
164
3/3
✓ Branch 125 → 126 taken 1692 times.
✓ Branch 128 → 129 taken 46 times.
✓ Branch 128 → 133 taken 1646 times.
1692 if (const ICal::Property *recurrence = vevent.property(QStringLiteral("RECURRENCE-ID")))
165
1/1
✓ Branch 129 → 130 taken 46 times.
46 event.recurrenceId = ICalTime::parseProperty(*recurrence, resolver).dateTime;
166
5/5
✓ Branch 136 → 137 taken 1692 times.
✓ Branch 139 → 140 taken 1692 times.
✓ Branch 140 → 141 taken 1692 times.
✓ Branch 157 → 142 taken 37 times.
✓ Branch 157 → 158 taken 1692 times.
1729 for (const ICal::Property &exdate : vevent.propertiesNamed(QStringLiteral("EXDATE")))
167
5/5
✓ Branch 143 → 144 taken 37 times.
✓ Branch 144 → 145 taken 37 times.
✓ Branch 145 → 146 taken 37 times.
✓ Branch 153 → 147 taken 47 times.
✓ Branch 153 → 154 taken 37 times.
84 for (const ICalTime::Value &value : ICalTime::parseList(exdate, resolver))
168
2/2
✓ Branch 148 → 149 taken 47 times.
✓ Branch 149 → 150 taken 47 times.
1776 event.exdates.append(exclusionInstant(value, event));
169
5/5
✓ Branch 162 → 163 taken 1692 times.
✓ Branch 165 → 166 taken 1692 times.
✓ Branch 166 → 167 taken 1692 times.
✓ Branch 181 → 168 taken 14 times.
✓ Branch 181 → 182 taken 1692 times.
1706 for (const ICal::Property &rdate : vevent.propertiesNamed(QStringLiteral("RDATE")))
170
5/5
✓ Branch 169 → 170 taken 14 times.
✓ Branch 170 → 171 taken 14 times.
✓ Branch 171 → 172 taken 14 times.
✓ Branch 177 → 173 taken 21 times.
✓ Branch 177 → 178 taken 14 times.
35 for (const ICalTime::Value &value : ICalTime::parseList(rdate, resolver))
171
1/1
✓ Branch 174 → 175 taken 21 times.
1727 event.rdates.append(value.dateTime);
172
1/1
✓ Branch 183 → 184 taken 1692 times.
1692 event.reminders = reminderMinutes(vevent, event.dtStart, event.dtEnd, resolver);
173
1/1
✓ Branch 186 → 187 taken 1692 times.
1692 event.people = peopleOf(vevent);
174 1692 event.peopleSet = true;
175
1/1
✓ Branch 189 → 190 taken 1692 times.
1692 event.attendees = attendeesOf(vevent);
176
1/1
✓ Branch 195 → 196 taken 1692 times.
1692 const QString lastModified = vevent.value(QStringLiteral("LAST-MODIFIED"));
177
2/2
✓ Branch 199 → 200 taken 353 times.
✓ Branch 199 → 206 taken 1339 times.
1692 if (!lastModified.isEmpty())
178
1/1
✓ Branch 201 → 202 taken 353 times.
353 event.lastModified = ICalTime::parseValue(lastModified, {}, false, resolver).dateTime;
179
1/1
✓ Branch 206 → 207 taken 1692 times.
1692 event.icalRaw = componentText(vevent);
180 1692 return event;
181 1692 }
182
183 1078 bool parseResource(const QString &text, Resource *resource, QString *error) {
184 1078 QString parseError;
185
1/1
✓ Branch 5 → 6 taken 1078 times.
1078 const ICal::Component calendar = ICal::parseFirst(text, QStringLiteral("VCALENDAR"), &parseError);
186
2/2
✓ Branch 9 → 10 taken 5 times.
✓ Branch 9 → 13 taken 1073 times.
1078 if (calendar.name.isEmpty()) {
187
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 4 times.
5 if (error)
188 1 *error = parseError;
189 5 return false;
190 }
191 1073 resource->calendar = calendar;
192
3/3
✓ Branch 17 → 18 taken 1073 times.
✓ Branch 18 → 19 taken 1073 times.
✓ Branch 19 → 20 taken 1073 times.
1073 resource->method = calendar.value(QStringLiteral("METHOD")).trimmed().toUpper();
193
1/1
✓ Branch 26 → 27 taken 1073 times.
1073 resource->events.clear();
194
1/1
✓ Branch 27 → 28 taken 1073 times.
1073 const ICalTime::ZoneResolver resolver(calendar);
195
2/2
✓ Branch 59 → 30 taken 1340 times.
✓ Branch 59 → 60 taken 1073 times.
2413 for (const ICal::Component &component : calendar.components) {
196
2/2
✓ Branch 33 → 34 taken 251 times.
✓ Branch 33 → 35 taken 1089 times.
1340 if (component.name != QLatin1String("VEVENT"))
197 253 continue;
198
1/1
✓ Branch 35 → 36 taken 1089 times.
1089 CalendarEvent event = eventFromComponent(component, resolver);
199
2/2
✓ Branch 37 → 38 taken 2 times.
✓ Branch 37 → 49 taken 1087 times.
1089 if (event.uid.isEmpty()) {
200
5/5
✓ Branch 38 → 39 taken 2 times.
✓ Branch 42 → 43 taken 2 times.
✓ Branch 43 → 44 taken 2 times.
✓ Branch 47 → 40 taken 2 times.
✓ Branch 47 → 48 taken 2 times.
4 qCWarning(lcEventCodec) << "Skipping VEVENT without UID";
201 2 continue;
202 2 }
203 1087 event.resourceIcal = text;
204
1/1
✓ Branch 50 → 51 taken 1087 times.
1087 resource->events.append(event);
205 1089 }
206 1073 return true;
207 1078 }
208
209 550 ICal::Component eventComponent(const CalendarEvent &event, const ICalTime::ZoneResolver &resolver) {
210 550 ICal::Component component;
211
2/2
✓ Branch 3 → 4 taken 338 times.
✓ Branch 3 → 13 taken 212 times.
550 if (!event.icalRaw.isEmpty())
212
1/1
✓ Branch 7 → 8 taken 338 times.
338 component = ICal::parseFirst(event.icalRaw, QStringLiteral("VEVENT"));
213 550 const bool fresh = component.name.isEmpty();
214
2/2
✓ Branch 14 → 15 taken 212 times.
✓ Branch 14 → 42 taken 338 times.
550 if (fresh) {
215 212 component.name = QStringLiteral("VEVENT");
216
2/2
✓ Branch 21 → 22 taken 212 times.
✓ Branch 26 → 27 taken 212 times.
424 component.appendProperty(makeProperty(QStringLiteral("UID"), ICal::escapeText(event.uid)));
217
2/2
✓ Branch 31 → 32 taken 212 times.
✓ Branch 36 → 37 taken 212 times.
424 component.appendProperty(makeProperty(QStringLiteral("DTSTAMP"), nowStamp()));
218 }
219
3/63
✓ Branch 42 → 43 taken 212 times.
✓ Branch 42 → 48 taken 338 times.
✓ Branch 48 → 49 taken 338 times.
✗ Branch 372 → 373 not taken.
✗ Branch 372 → 375 not taken.
✗ Branch 373 → 374 not taken.
✗ Branch 373 → 375 not taken.
✗ Branch 376 → 377 not taken.
✗ Branch 376 → 379 not taken.
✗ Branch 377 → 378 not taken.
✗ Branch 377 → 379 not taken.
✗ Branch 380 → 381 not taken.
✗ Branch 380 → 383 not taken.
✗ Branch 381 → 382 not taken.
✗ Branch 381 → 383 not taken.
✗ Branch 384 → 385 not taken.
✗ Branch 384 → 387 not taken.
✗ Branch 385 → 386 not taken.
✗ Branch 385 → 387 not taken.
✗ Branch 388 → 389 not taken.
✗ Branch 388 → 391 not taken.
✗ Branch 389 → 390 not taken.
✗ Branch 389 → 391 not taken.
✗ Branch 392 → 393 not taken.
✗ Branch 392 → 395 not taken.
✗ Branch 393 → 394 not taken.
✗ Branch 393 → 395 not taken.
✗ Branch 396 → 397 not taken.
✗ Branch 396 → 399 not taken.
✗ Branch 397 → 398 not taken.
✗ Branch 397 → 399 not taken.
✗ Branch 400 → 401 not taken.
✗ Branch 400 → 403 not taken.
✗ Branch 401 → 402 not taken.
✗ Branch 401 → 403 not taken.
✗ Branch 404 → 405 not taken.
✗ Branch 404 → 407 not taken.
✗ Branch 405 → 406 not taken.
✗ Branch 405 → 407 not taken.
✗ Branch 408 → 409 not taken.
✗ Branch 408 → 411 not taken.
✗ Branch 409 → 410 not taken.
✗ Branch 409 → 411 not taken.
✗ Branch 412 → 413 not taken.
✗ Branch 412 → 415 not taken.
✗ Branch 413 → 414 not taken.
✗ Branch 413 → 415 not taken.
✗ Branch 416 → 417 not taken.
✗ Branch 416 → 419 not taken.
✗ Branch 417 → 418 not taken.
✗ Branch 417 → 419 not taken.
✗ Branch 420 → 421 not taken.
✗ Branch 420 → 423 not taken.
✗ Branch 421 → 422 not taken.
✗ Branch 421 → 423 not taken.
✗ Branch 424 → 425 not taken.
✗ Branch 424 → 427 not taken.
✗ Branch 425 → 426 not taken.
✗ Branch 425 → 427 not taken.
✗ Branch 428 → 429 not taken.
✗ Branch 428 → 431 not taken.
✗ Branch 429 → 430 not taken.
✗ Branch 429 → 431 not taken.
550 const CalendarEvent before = fresh ? CalendarEvent{} : eventFromComponent(component, resolver);
220 550 bool changed = fresh;
221 550 bool significant = false;
222
6/6
✓ Branch 49 → 50 taken 338 times.
✓ Branch 49 → 53 taken 212 times.
✓ Branch 51 → 52 taken 1 time.
✓ Branch 51 → 53 taken 337 times.
✓ Branch 54 → 55 taken 1 time.
✓ Branch 54 → 64 taken 549 times.
550 if (!fresh && before.uid != event.uid) {
223 // A copy (duplicate, "keep both") is a new resource with its own UID.
224
2/2
✓ Branch 55 → 56 taken 1 time.
✓ Branch 59 → 60 taken 1 time.
2 component.setPropertyValue(QStringLiteral("UID"), ICal::escapeText(event.uid));
225 1 changed = true;
226 }
227 1650 const auto text = [&](const QString &name, const QString &oldValue, const QString &newValue) {
228
6/6
✓ Branch 2 → 3 taken 1014 times.
✓ Branch 2 → 6 taken 636 times.
✓ Branch 4 → 5 taken 848 times.
✓ Branch 4 → 6 taken 166 times.
✓ Branch 7 → 8 taken 848 times.
✓ Branch 7 → 9 taken 802 times.
1650 if (!fresh && oldValue == newValue)
229 848 return;
230 802 setText(&component, name, newValue);
231 802 changed = true;
232 550 };
233
4/5
✓ Branch 64 → 65 taken 550 times.
✓ Branch 65 → 66 taken 18 times.
✓ Branch 65 → 70 taken 532 times.
✗ Branch 71 → 72 not taken.
✓ Branch 71 → 93 taken 550 times.
568 if (event.recurrenceId.isValid() &&
234
3/5
✓ Branch 66 → 67 taken 18 times.
✗ Branch 66 → 69 not taken.
✓ Branch 67 → 68 taken 18 times.
✗ Branch 68 → 69 not taken.
✓ Branch 68 → 70 taken 18 times.
18 (fresh || !sameInstant(before.recurrenceId, event.recurrenceId, event.allDay))) {
235 ✗ QDateTime anchor = event.recurrenceId;
236 ✗ if (!event.allDay && event.dtStart.timeSpec() == Qt::TimeZone)
237 ✗ anchor = anchor.toTimeZone(event.dtStart.timeZone());
238 ✗ writeDate(&component, QStringLiteral("RECURRENCE-ID"), anchor, event.allDay, resolver);
239 ✗ changed = true;
240 ✗ }
241
7/7
✓ Branch 93 → 94 taken 338 times.
✓ Branch 93 → 96 taken 212 times.
✓ Branch 94 → 95 taken 338 times.
✓ Branch 95 → 96 taken 62 times.
✓ Branch 95 → 97 taken 276 times.
✓ Branch 98 → 99 taken 274 times.
✓ Branch 98 → 136 taken 276 times.
550 if (fresh || !sameStamp(before.dtStart, before.allDay, event.dtStart, event.allDay)) {
242
3/3
✓ Branch 99 → 100 taken 274 times.
✓ Branch 100 → 101 taken 272 times.
✓ Branch 100 → 108 taken 2 times.
274 if (event.dtStart.isValid())
243
1/1
✓ Branch 104 → 105 taken 272 times.
544 writeDate(&component, QStringLiteral("DTSTART"), event.dtStart, event.allDay, resolver);
244 // F-10 (SPRINT-104): a stored DURATION would recompute the end from
245 // the NEW start on the server (10:00+PT1H edited to 10:30 read back
246 // as 10:30–11:30). Write the intended end explicitly instead.
247
12/16
✓ Branch 108 → 109 taken 274 times.
✓ Branch 109 → 110 taken 272 times.
✓ Branch 109 → 116 taken 2 times.
✓ Branch 113 → 114 taken 272 times.
✓ Branch 114 → 115 taken 1 time.
✓ Branch 114 → 116 taken 271 times.
✓ Branch 117 → 118 taken 272 times.
✓ Branch 117 → 119 taken 2 times.
✓ Branch 119 → 120 taken 272 times.
✓ Branch 119 → 121 taken 2 times.
✓ Branch 121 → 122 taken 1 time.
✓ Branch 121 → 135 taken 273 times.
✗ Branch 460 → 461 not taken.
✗ Branch 460 → 462 not taken.
✗ Branch 464 → 465 not taken.
✗ Branch 464 → 466 not taken.
546 if (event.dtEnd.isValid() && component.hasProperty(QStringLiteral("DURATION"))) {
248
1/1
✓ Branch 125 → 126 taken 1 time.
1 component.removeProperties(QStringLiteral("DURATION"));
249
1/1
✓ Branch 131 → 132 taken 1 time.
2 writeDate(&component, QStringLiteral("DTEND"), event.dtEnd, event.allDay, resolver);
250 }
251 274 changed = significant = true;
252 }
253
5/6
✓ Branch 136 → 137 taken 338 times.
✓ Branch 136 → 140 taken 212 times.
✓ Branch 137 → 138 taken 338 times.
✗ Branch 137 → 140 not taken.
✓ Branch 142 → 143 taken 273 times.
✓ Branch 142 → 166 taken 277 times.
888 if (fresh || before.allDay != event.allDay ||
254
3/3
✓ Branch 138 → 139 taken 338 times.
✓ Branch 139 → 140 taken 61 times.
✓ Branch 139 → 141 taken 277 times.
338 !sameStamp(before.dtEnd, before.allDay, event.dtEnd, event.allDay)) {
255
1/1
✓ Branch 146 → 147 taken 273 times.
273 component.removeProperties(QStringLiteral("DURATION"));
256
3/3
✓ Branch 149 → 150 taken 273 times.
✓ Branch 150 → 151 taken 271 times.
✓ Branch 150 → 158 taken 2 times.
273 if (event.dtEnd.isValid())
257
1/1
✓ Branch 154 → 155 taken 271 times.
542 writeDate(&component, QStringLiteral("DTEND"), event.dtEnd, event.allDay, resolver);
258 else
259
1/1
✓ Branch 161 → 162 taken 2 times.
2 component.removeProperties(QStringLiteral("DTEND"));
260 273 changed = significant = true;
261 }
262
1/1
✓ Branch 169 → 170 taken 550 times.
1100 text(QStringLiteral("SUMMARY"), before.summary, event.summary);
263
1/1
✓ Branch 175 → 176 taken 550 times.
1100 text(QStringLiteral("LOCATION"), before.location, event.location);
264
1/1
✓ Branch 181 → 182 taken 550 times.
1100 text(QStringLiteral("DESCRIPTION"), before.description, event.description);
265
11/16
✓ Branch 184 → 185 taken 338 times.
✓ Branch 184 → 189 taken 212 times.
✓ Branch 185 → 186 taken 338 times.
✓ Branch 186 → 187 taken 338 times.
✗ Branch 188 → 189 not taken.
✓ Branch 188 → 190 taken 338 times.
✓ Branch 191 → 192 taken 338 times.
✓ Branch 191 → 193 taken 212 times.
✓ Branch 193 → 194 taken 338 times.
✓ Branch 193 → 195 taken 212 times.
✓ Branch 195 → 196 taken 212 times.
✓ Branch 195 → 221 taken 338 times.
✗ Branch 516 → 517 not taken.
✗ Branch 516 → 518 not taken.
✗ Branch 520 → 521 not taken.
✗ Branch 520 → 522 not taken.
550 if (fresh || before.rrule.trimmed() != event.rrule.trimmed()) {
266
1/1
✓ Branch 196 → 197 taken 212 times.
212 QString rule = event.rrule.trimmed();
267 // T-610/SEC-09: RRULE delimiters stay unescaped; strip CR/LF injection.
268
1/1
✓ Branch 199 → 200 taken 212 times.
212 rule.remove(QLatin1Char('\r'));
269
1/1
✓ Branch 202 → 203 taken 212 times.
212 rule.remove(QLatin1Char('\n'));
270
2/2
✓ Branch 204 → 205 taken 204 times.
✓ Branch 204 → 212 taken 8 times.
212 if (rule.isEmpty())
271
1/1
✓ Branch 208 → 209 taken 204 times.
204 component.removeProperties(QStringLiteral("RRULE"));
272 else
273
1/1
✓ Branch 215 → 216 taken 8 times.
8 component.setPropertyValue(QStringLiteral("RRULE"), rule);
274 212 changed = true;
275 212 significant = true;
276 212 }
277
11/16
✓ Branch 221 → 222 taken 338 times.
✓ Branch 221 → 227 taken 212 times.
✓ Branch 222 → 223 taken 338 times.
✓ Branch 223 → 224 taken 338 times.
✓ Branch 225 → 226 taken 338 times.
✗ Branch 225 → 227 not taken.
✓ Branch 229 → 230 taken 338 times.
✓ Branch 229 → 231 taken 212 times.
✓ Branch 231 → 232 taken 338 times.
✓ Branch 231 → 233 taken 212 times.
✓ Branch 233 → 234 taken 212 times.
✓ Branch 233 → 241 taken 338 times.
✗ Branch 543 → 544 not taken.
✗ Branch 543 → 545 not taken.
✗ Branch 547 → 548 not taken.
✗ Branch 547 → 549 not taken.
888 if (fresh || instantSet(before.exdates, before.allDay) != instantSet(event.exdates, event.allDay) ||
278
1/2
✗ Branch 226 → 227 not taken.
✓ Branch 226 → 228 taken 338 times.
338 before.allDay != event.allDay) {
279
1/1
✓ Branch 237 → 238 taken 212 times.
424 writeDateList(&component, QStringLiteral("EXDATE"), event.exdates, event.allDay, event.dtStart,
280 resolver);
281 212 changed = significant = true;
282 }
283
11/16
✓ Branch 241 → 242 taken 338 times.
✓ Branch 241 → 247 taken 212 times.
✓ Branch 242 → 243 taken 338 times.
✓ Branch 243 → 244 taken 338 times.
✓ Branch 245 → 246 taken 338 times.
✗ Branch 245 → 247 not taken.
✓ Branch 249 → 250 taken 338 times.
✓ Branch 249 → 251 taken 212 times.
✓ Branch 251 → 252 taken 338 times.
✓ Branch 251 → 253 taken 212 times.
✓ Branch 253 → 254 taken 212 times.
✓ Branch 253 → 261 taken 338 times.
✗ Branch 557 → 558 not taken.
✗ Branch 557 → 559 not taken.
✗ Branch 561 → 562 not taken.
✗ Branch 561 → 563 not taken.
888 if (fresh || instantSet(before.rdates, before.allDay) != instantSet(event.rdates, event.allDay) ||
284
1/2
✗ Branch 246 → 247 not taken.
✓ Branch 246 → 248 taken 338 times.
338 before.allDay != event.allDay) {
285
1/1
✓ Branch 257 → 258 taken 212 times.
424 writeDateList(&component, QStringLiteral("RDATE"), event.rdates, event.allDay, event.dtStart,
286 resolver);
287 212 changed = significant = true;
288 }
289 // D-14: people are CONTACT lines, never ATTENDEE; naming people is no
290 // significant change (no SEQUENCE bump, nobody to re-invite).
291
11/11
✓ Branch 261 → 262 taken 158 times.
✓ Branch 261 → 268 taken 392 times.
✓ Branch 262 → 263 taken 15 times.
✓ Branch 262 → 265 taken 143 times.
✓ Branch 264 → 267 taken 12 times.
✓ Branch 264 → 268 taken 3 times.
✓ Branch 265 → 266 taken 143 times.
✓ Branch 266 → 267 taken 2 times.
✓ Branch 266 → 268 taken 141 times.
✓ Branch 269 → 270 taken 14 times.
✓ Branch 269 → 272 taken 536 times.
550 if (event.peopleSet && (fresh ? !event.people.isEmpty() : before.people != event.people)) {
292
1/1
✓ Branch 270 → 271 taken 14 times.
14 writePeople(&component, event.people);
293 14 changed = true;
294 }
295
5/5
✓ Branch 272 → 273 taken 212 times.
✓ Branch 272 → 275 taken 338 times.
✓ Branch 275 → 276 taken 338 times.
✓ Branch 277 → 278 taken 4 times.
✓ Branch 277 → 280 taken 546 times.
550 if (fresh ? !event.reminders.isEmpty() : before.reminders != event.reminders) {
296
1/1
✓ Branch 278 → 279 taken 4 times.
4 writeReminders(&component, event.reminders, event.summary);
297 4 changed = true; // not significant: attendees need no new invitation
298 }
299
2/2
✓ Branch 280 → 281 taken 212 times.
✓ Branch 280 → 301 taken 338 times.
550 if (fresh) {
300
1/1
✓ Branch 287 → 288 taken 212 times.
424 component.setPropertyValue(QStringLiteral("SEQUENCE"), QStringLiteral("0"));
301
2/2
✓ Branch 292 → 293 taken 212 times.
✓ Branch 296 → 297 taken 212 times.
424 component.setPropertyValue(QStringLiteral("LAST-MODIFIED"), nowStamp());
302 212 return component;
303 }
304
2/2
✓ Branch 301 → 302 taken 119 times.
✓ Branch 301 → 303 taken 219 times.
338 if (!changed)
305 119 return component;
306
2/2
✓ Branch 303 → 304 taken 62 times.
✓ Branch 303 → 321 taken 157 times.
219 if (significant) {
307
2/2
✓ Branch 307 → 308 taken 62 times.
✓ Branch 308 → 309 taken 62 times.
62 const int sequence = component.value(QStringLiteral("SEQUENCE")).toInt();
308
2/2
✓ Branch 312 → 313 taken 62 times.
✓ Branch 316 → 317 taken 62 times.
124 component.setPropertyValue(QStringLiteral("SEQUENCE"), QString::number(sequence + 1));
309 }
310
2/2
✓ Branch 321 → 322 taken 219 times.
✓ Branch 325 → 326 taken 219 times.
438 component.setPropertyValue(QStringLiteral("DTSTAMP"), nowStamp());
311
2/2
✓ Branch 329 → 330 taken 219 times.
✓ Branch 333 → 334 taken 219 times.
438 component.setPropertyValue(QStringLiteral("LAST-MODIFIED"), nowStamp());
312 219 return component;
313 550 }
314
315 95 void adoptPushedResource(CalendarEvent *event, const QByteArray &body) {
316
1/1
✓ Branch 2 → 3 taken 95 times.
95 const QString text = QString::fromUtf8(body);
317
1/1
✓ Branch 6 → 7 taken 95 times.
95 const ICal::Component calendar = ICal::parseFirst(text, QStringLiteral("VCALENDAR"));
318
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 95 times.
95 if (calendar.name.isEmpty())
319 ✗ return;
320
1/1
✓ Branch 12 → 13 taken 95 times.
95 const ICalTime::ZoneResolver resolver(calendar);
321
4/5
✓ Branch 16 → 17 taken 95 times.
✓ Branch 19 → 20 taken 95 times.
✓ Branch 20 → 21 taken 95 times.
✓ Branch 76 → 22 taken 96 times.
✗ Branch 76 → 77 not taken.
96 for (const ICal::Component &component : calendar.componentsNamed(QStringLiteral("VEVENT"))) {
322
4/5
✓ Branch 26 → 27 taken 96 times.
✓ Branch 27 → 28 taken 96 times.
✓ Branch 28 → 29 taken 96 times.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 96 times.
192 if (ICal::unescapeText(component.value(QStringLiteral("UID"))).trimmed() != event->uid)
323 1 continue;
324
1/1
✓ Branch 40 → 41 taken 96 times.
96 const ICal::Property *recurrence = component.property(QStringLiteral("RECURRENCE-ID"));
325 const QDateTime anchor =
326
5/7
✓ Branch 43 → 44 taken 1 time.
✓ Branch 43 → 46 taken 95 times.
✓ Branch 44 → 45 taken 1 time.
✓ Branch 47 → 48 taken 1 time.
✓ Branch 47 → 49 taken 95 times.
✗ Branch 128 → 129 not taken.
✗ Branch 128 → 130 not taken.
96 recurrence ? ICalTime::parseProperty(*recurrence, resolver).dateTime : QDateTime();
327
4/4
✓ Branch 49 → 50 taken 96 times.
✓ Branch 50 → 51 taken 96 times.
✓ Branch 51 → 52 taken 95 times.
✓ Branch 51 → 58 taken 1 time.
191 if (anchor.isValid() != event->recurrenceId.isValid() ||
328
13/18
✓ Branch 52 → 53 taken 95 times.
✓ Branch 53 → 54 taken 1 time.
✓ Branch 53 → 59 taken 94 times.
✓ Branch 54 → 55 taken 1 time.
✓ Branch 55 → 56 taken 1 time.
✓ Branch 56 → 57 taken 1 time.
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 59 taken 1 time.
✓ Branch 60 → 61 taken 1 time.
✓ Branch 60 → 62 taken 95 times.
✓ Branch 62 → 63 taken 1 time.
✓ Branch 62 → 64 taken 95 times.
✓ Branch 64 → 65 taken 1 time.
✓ Branch 64 → 66 taken 95 times.
✗ Branch 132 → 133 not taken.
✗ Branch 132 → 134 not taken.
✗ Branch 136 → 137 not taken.
✗ Branch 136 → 138 not taken.
191 (anchor.isValid() && anchor.toUTC() != event->recurrenceId.toUTC()))
329 1 continue;
330
1/1
✓ Branch 66 → 67 taken 95 times.
95 event->icalRaw = componentText(component);
331 95 event->resourceIcal = text;
332 95 return;
333 191 }
334 285 }
335
336 508 QByteArray buildResource(const CalendarEvent &master, const QList<CalendarEvent> &exceptions,
337 const QString &storedResource) {
338 508 ICal::Component calendar;
339
2/2
✓ Branch 3 → 4 taken 352 times.
✓ Branch 3 → 13 taken 156 times.
508 if (!storedResource.isEmpty())
340
1/1
✓ Branch 7 → 8 taken 352 times.
352 calendar = ICal::parseFirst(storedResource, QStringLiteral("VCALENDAR"));
341
2/2
✓ Branch 14 → 15 taken 156 times.
✓ Branch 14 → 50 taken 352 times.
508 if (calendar.name.isEmpty()) {
342 156 calendar = {};
343 156 calendar.name = QStringLiteral("VCALENDAR");
344
1/1
✓ Branch 30 → 31 taken 156 times.
312 calendar.appendProperty(makeProperty(QStringLiteral("VERSION"), QStringLiteral("2.0")));
345
1/1
✓ Branch 43 → 44 taken 156 times.
156 calendar.appendProperty(makeProperty(QStringLiteral("PRODID"),
346 312 QStringLiteral("-//MailJD//CalDAV Client//EN")));
347 }
348
1/1
✓ Branch 53 → 54 taken 508 times.
508 calendar.removeProperties(QStringLiteral("METHOD"));
349
1/1
✓ Branch 56 → 57 taken 508 times.
508 const ICalTime::ZoneResolver resolver(calendar);
350
1/1
✓ Branch 60 → 61 taken 508 times.
508 calendar.removeComponents(QStringLiteral("VEVENT"));
351
2/2
✓ Branch 63 → 64 taken 508 times.
✓ Branch 64 → 65 taken 508 times.
508 calendar.components.append(eventComponent(master, resolver));
352
2/2
✓ Branch 74 → 68 taken 18 times.
✓ Branch 74 → 75 taken 508 times.
526 for (const CalendarEvent &exception : exceptions)
353
2/2
✓ Branch 69 → 70 taken 18 times.
✓ Branch 70 → 71 taken 18 times.
18 calendar.components.append(eventComponent(exception, resolver));
354 // A TZID needs its VTIMEZONE (RFC 5545 §3.2.19). Keep the resource's own
355 // definitions; generate the missing ones before the first VEVENT.
356 508 QSet<QString> defined;
357
5/5
✓ Branch 79 → 80 taken 508 times.
✓ Branch 82 → 83 taken 508 times.
✓ Branch 83 → 84 taken 508 times.
✓ Branch 100 → 85 taken 267 times.
✓ Branch 100 → 101 taken 508 times.
775 for (const ICal::Component &component : calendar.componentsNamed(QStringLiteral("VTIMEZONE")))
358
4/4
✓ Branch 89 → 90 taken 267 times.
✓ Branch 90 → 91 taken 267 times.
✓ Branch 91 → 92 taken 267 times.
✓ Branch 92 → 93 taken 267 times.
775 defined.insert(ICal::unescapeText(component.value(QStringLiteral("TZID"))).trimmed());
359 508 int insertAt = 0;
360
1/2
✓ Branch 105 → 106 taken 777 times.
✗ Branch 105 → 111 not taken.
1554 while (insertAt < calendar.components.size() &&
361
4/4
✓ Branch 109 → 110 taken 269 times.
✓ Branch 109 → 111 taken 508 times.
✓ Branch 112 → 103 taken 269 times.
✓ Branch 112 → 113 taken 508 times.
1554 calendar.components.at(insertAt).name != QLatin1String("VEVENT"))
362 269 ++insertAt;
363
6/6
✓ Branch 113 → 114 taken 508 times.
✓ Branch 114 → 115 taken 506 times.
✓ Branch 114 → 116 taken 2 times.
✓ Branch 115 → 117 taken 506 times.
✓ Branch 116 → 117 taken 2 times.
✓ Branch 117 → 118 taken 508 times.
508 const int year = (master.dtStart.isValid() ? master.dtStart.date() : QDate::currentDate()).year();
364
3/3
✓ Branch 118 → 119 taken 508 times.
✓ Branch 119 → 120 taken 508 times.
✓ Branch 120 → 121 taken 508 times.
508 QStringList missing = referencedTzids(calendar).subtract(defined).values();
365
1/1
✓ Branch 122 → 123 taken 508 times.
508 missing.sort();
366
4/4
✓ Branch 123 → 124 taken 508 times.
✓ Branch 124 → 125 taken 508 times.
✓ Branch 161 → 126 taken 127 times.
✓ Branch 161 → 162 taken 508 times.
635 for (const QString &tzid : missing) {
367
1/1
✓ Branch 127 → 128 taken 127 times.
127 const QTimeZone zone = resolver.resolve(tzid);
368
2/3
✓ Branch 128 → 129 taken 127 times.
✗ Branch 129 → 130 not taken.
✓ Branch 129 → 142 taken 127 times.
127 if (!zone.isValid()) {
369 ✗ qCWarning(lcEventCodec) << "Cannot generate VTIMEZONE for" << tzid;
370 ✗ continue;
371 ✗ }
372
1/1
✓ Branch 142 → 143 taken 127 times.
127 ICal::Component timezone = ICalTime::vtimezoneFor(zone, year);
373
2/2
✓ Branch 143 → 144 taken 127 times.
✓ Branch 147 → 148 taken 127 times.
254 timezone.setPropertyValue(QStringLiteral("TZID"), ICal::escapeText(tzid));
374
1/1
✓ Branch 151 → 152 taken 127 times.
127 calendar.components.insert(insertAt++, timezone);
375 127 }
376
2/2
✓ Branch 162 → 163 taken 508 times.
✓ Branch 163 → 164 taken 508 times.
1016 return ICal::serialize(calendar).toUtf8();
377 508 }
378
379 } // namespace ICalEventCodec
380