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: 97.1% 201 / 0 / 207
Functions: 100.0% 13 / 0 / 13
Branches: 78.4% 330 / 0 / 421

data/MergeValidation.cpp
Line Branch Exec Source
1 #include "MergeValidation.h"
2
3 #include "ICalMergePlan.h"
4 #include "ICalTime.h"
5 #include "ICalendar.h"
6 #include "SettingsMergePlan.h"
7 #include "SieveMergePlan.h"
8 #include "SieveRuleCodec.h"
9 #include "VCardCodec.h"
10 #include "VCardMergePlan.h"
11
12 #include <QSet>
13
14 namespace MergeValidation {
15
16 namespace {
17
18 using Problems = QList<Merge::ValidationProblem>;
19
20 17 void add(Problems *problems, const Merge::Plan &plan, const QString &rule, const QStringList &candidates,
21 const QString &message) {
22 17 QStringList items;
23
2/2
✓ Branch 15 → 4 taken 59 times.
✓ Branch 15 → 16 taken 17 times.
76 for (const QString &id : candidates)
24
7/7
✓ Branch 5 → 6 taken 59 times.
✓ Branch 6 → 7 taken 47 times.
✓ Branch 6 → 10 taken 12 times.
✓ Branch 8 → 9 taken 29 times.
✓ Branch 8 → 10 taken 18 times.
✓ Branch 11 → 12 taken 29 times.
✓ Branch 11 → 13 taken 30 times.
59 if (plan.indexOf(id) >= 0 && !items.contains(id))
25
1/1
✓ Branch 12 → 13 taken 29 times.
29 items.append(id);
26 17 problems->append({rule, items, message});
27 34 }
28
29 111 QString keyOf(const ICal::Component &component) {
30
1/1
✓ Branch 5 → 6 taken 111 times.
111 const ICal::Property *recurrence = component.property(QStringLiteral("RECURRENCE-ID"));
31
4/4
✓ Branch 16 → 17 taken 111 times.
✓ Branch 19 → 20 taken 111 times.
✓ Branch 20 → 21 taken 111 times.
✓ Branch 21 → 22 taken 111 times.
333 return component.name + QLatin1Char(':') + component.value(QStringLiteral("UID")) + QLatin1Char(':') +
32
4/4
✓ Branch 8 → 9 taken 3 times.
✓ Branch 8 → 10 taken 108 times.
✓ Branch 9 → 11 taken 3 times.
✓ Branch 22 → 23 taken 111 times.
333 (recurrence ? recurrence->value.trimmed() : QString());
33 }
34
35 // The items of `names` in component `key`: one per property, or one per
36 // value of a D-8 list; a one-sided component carries the whole value.
37 9 QStringList itemsOf(const Merge::Plan &plan, const QString &key, const QStringList &names) {
38 9 QStringList ids;
39
2/2
✓ Branch 17 → 4 taken 20 times.
✓ Branch 17 → 18 taken 9 times.
29 for (const QString &name : names)
40
3/3
✓ Branch 8 → 9 taken 20 times.
✓ Branch 9 → 10 taken 20 times.
✓ Branch 10 → 11 taken 20 times.
20 ids.append(key + QStringLiteral("::") + name);
41
2/2
✓ Branch 31 → 20 taken 41 times.
✓ Branch 31 → 32 taken 9 times.
50 for (const Merge::Item &item : plan.items)
42
5/6
✓ Branch 22 → 23 taken 41 times.
✗ Branch 22 → 26 not taken.
✓ Branch 24 → 25 taken 19 times.
✓ Branch 24 → 26 taken 22 times.
✓ Branch 27 → 28 taken 19 times.
✓ Branch 27 → 29 taken 22 times.
41 if (item.parent == key && names.contains(item.name))
43
1/1
✓ Branch 28 → 29 taken 19 times.
19 ids.append(item.id);
44
1/1
✓ Branch 32 → 33 taken 9 times.
9 ids.append(key);
45 9 return ids;
46 ✗ }
47
48 // The EXDATE value items of a master that exclude `instant`.
49 1 QStringList exdateItems(const Merge::Plan &plan, const QString &masterKey, const QDateTime &instant,
50 const ICalTime::ZoneResolver &zones) {
51 1 QStringList ids;
52
2/2
✓ Branch 68 → 4 taken 7 times.
✓ Branch 68 → 69 taken 1 time.
8 for (const Merge::Item &item : plan.items) {
53
6/6
✓ Branch 6 → 7 taken 6 times.
✓ Branch 6 → 10 taken 1 time.
✓ Branch 9 → 10 taken 5 times.
✓ Branch 9 → 11 taken 1 time.
✓ Branch 12 → 13 taken 6 times.
✓ Branch 12 → 14 taken 1 time.
7 if (item.parent != masterKey || item.name != QLatin1String("EXDATE"))
54 6 continue;
55
2/2
✓ Branch 64 → 16 taken 2 times.
✓ Branch 64 → 65 taken 1 time.
3 for (const Merge::Value *value : {&item.local, &item.server}) {
56
2/2
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 18 taken 1 time.
2 if (!value->present)
57 1 continue;
58
1/1
✓ Branch 27 → 28 taken 1 time.
3 const ICal::Component parsed = ICal::parseFirst(QStringLiteral("BEGIN:X\r\n") + value->text +
59
1/1
✓ Branch 28 → 29 taken 1 time.
3 QStringLiteral("\r\nEND:X\r\n"),
60
1/1
✓ Branch 29 → 30 taken 1 time.
3 QStringLiteral("X"));
61
2/2
✓ Branch 60 → 40 taken 1 time.
✓ Branch 60 → 61 taken 1 time.
2 for (const ICal::Property &property : parsed.properties)
62
5/5
✓ Branch 41 → 42 taken 1 time.
✓ Branch 42 → 43 taken 1 time.
✓ Branch 43 → 44 taken 1 time.
✓ Branch 56 → 45 taken 1 time.
✓ Branch 56 → 57 taken 1 time.
2 for (const ICalTime::Value &excluded : ICalTime::parseList(property, zones))
63
4/7
✓ Branch 46 → 47 taken 1 time.
✓ Branch 47 → 48 taken 1 time.
✗ Branch 47 → 51 not taken.
✓ Branch 49 → 50 taken 1 time.
✗ Branch 49 → 51 not taken.
✓ Branch 52 → 53 taken 1 time.
✗ Branch 52 → 54 not taken.
1 if (excluded.dateTime == instant && !ids.contains(item.id))
64
1/1
✓ Branch 53 → 54 taken 1 time.
2 ids.append(item.id);
65 1 }
66 }
67 1 return ids;
68 ✗ }
69
70 8 QSet<QString> uidsOf(const QString &text, bool vcard) {
71 8 QSet<QString> uids;
72
2/2
✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 18 taken 4 times.
8 if (vcard) {
73 4 ICal::Component card;
74 4 QString error;
75 4 int skipped = 0;
76
2/3
✓ Branch 4 → 5 taken 4 times.
✓ Branch 5 → 6 taken 4 times.
✗ Branch 5 → 15 not taken.
4 if (VCardCodec::parseTree(text, &card, &error, &skipped))
77
2/2
✓ Branch 9 → 10 taken 4 times.
✓ Branch 10 → 11 taken 4 times.
4 uids.insert(card.value(QStringLiteral("UID")));
78 4 return uids;
79 4 }
80
5/5
✓ Branch 21 → 22 taken 4 times.
✓ Branch 24 → 25 taken 4 times.
✓ Branch 25 → 26 taken 4 times.
✓ Branch 48 → 27 taken 7 times.
✓ Branch 48 → 49 taken 4 times.
11 for (const ICal::Component &component : ICal::parseFirst(text).components)
81
5/6
✓ Branch 30 → 31 taken 3 times.
✓ Branch 30 → 34 taken 4 times.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 3 times.
✓ Branch 36 → 37 taken 4 times.
✓ Branch 36 → 46 taken 3 times.
7 if (component.name == QLatin1String("VEVENT") || component.name == QLatin1String("VTODO"))
82
2/2
✓ Branch 40 → 41 taken 4 times.
✓ Branch 41 → 42 taken 4 times.
8 uids.insert(component.value(QStringLiteral("UID")));
83 4 return uids;
84 ✗ }
85
86 56 void checkComponent(Problems *problems, const Merge::Plan &plan, const ICal::Component &component,
87 const ICalTime::ZoneResolver &zones) {
88
1/1
✓ Branch 2 → 3 taken 56 times.
56 const QString key = keyOf(component);
89 56 const bool todo = component.name == QLatin1String("VTODO");
90 112 const auto time = [&](const QString &name) {
91 112 const ICal::Property *property = component.property(name);
92
3/7
✓ Branch 3 → 4 taken 96 times.
✓ Branch 3 → 5 taken 16 times.
✓ Branch 4 → 7 taken 96 times.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 12 not taken.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
224 return property ? ICalTime::parseProperty(*property, zones) : ICalTime::Value{};
93 56 };
94
1/1
✓ Branch 8 → 9 taken 56 times.
56 const ICalTime::Value start = time(QStringLiteral("DTSTART"));
95
6/6
✓ Branch 11 → 12 taken 8 times.
✓ Branch 11 → 15 taken 48 times.
✓ Branch 18 → 19 taken 48 times.
✓ Branch 18 → 20 taken 8 times.
✓ Branch 20 → 21 taken 8 times.
✓ Branch 20 → 22 taken 48 times.
112 const QString endName = todo ? QStringLiteral("DUE") : QStringLiteral("DTEND");
96
1/1
✓ Branch 22 → 23 taken 56 times.
56 const ICalTime::Value end = time(endName);
97
11/11
✓ Branch 23 → 24 taken 56 times.
✓ Branch 24 → 25 taken 48 times.
✓ Branch 24 → 31 taken 8 times.
✓ Branch 25 → 26 taken 48 times.
✓ Branch 26 → 27 taken 46 times.
✓ Branch 26 → 31 taken 2 times.
✓ Branch 27 → 28 taken 46 times.
✓ Branch 29 → 30 taken 6 times.
✓ Branch 29 → 31 taken 40 times.
✓ Branch 32 → 33 taken 6 times.
✓ Branch 32 → 71 taken 50 times.
56 if (start.valid() && end.valid() && end.dateTime < start.dateTime)
98
4/11
✗ Branch 46 → 47 not taken.
✓ Branch 46 → 50 taken 6 times.
✓ Branch 53 → 54 taken 6 times.
✓ Branch 55 → 56 taken 6 times.
✗ Branch 55 → 57 not taken.
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 59 taken 6 times.
✗ Branch 203 → 204 not taken.
✗ Branch 203 → 205 not taken.
✗ Branch 207 → 208 not taken.
✗ Branch 207 → 209 not taken.
12 add(problems, plan, todo ? QStringLiteral("due-before-start") : QStringLiteral("end-before-start"),
99
3/5
✓ Branch 45 → 46 taken 6 times.
✓ Branch 62 → 63 taken 12 times.
✓ Branch 62 → 64 taken 6 times.
✗ Branch 217 → 218 not taken.
✗ Branch 217 → 219 not taken.
24 itemsOf(plan, key, {QStringLiteral("DTSTART"), endName}),
100
3/10
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 37 taken 6 times.
✓ Branch 66 → 67 taken 6 times.
✗ Branch 66 → 68 not taken.
✗ Branch 68 → 69 not taken.
✓ Branch 68 → 70 taken 6 times.
✗ Branch 233 → 234 not taken.
✗ Branch 233 → 235 not taken.
✗ Branch 237 → 238 not taken.
✗ Branch 237 → 239 not taken.
18 todo ? QStringLiteral("The task is due before it starts") : QStringLiteral("The end is before the start"));
101
12/16
✓ Branch 71 → 72 taken 56 times.
✓ Branch 72 → 73 taken 48 times.
✓ Branch 72 → 79 taken 8 times.
✓ Branch 76 → 77 taken 48 times.
✓ Branch 77 → 78 taken 1 time.
✓ Branch 77 → 79 taken 47 times.
✓ Branch 80 → 81 taken 48 times.
✓ Branch 80 → 82 taken 8 times.
✓ Branch 82 → 83 taken 48 times.
✓ Branch 82 → 84 taken 8 times.
✓ Branch 84 → 85 taken 1 time.
✓ Branch 84 → 109 taken 55 times.
✗ Branch 242 → 243 not taken.
✗ Branch 242 → 244 not taken.
✗ Branch 246 → 247 not taken.
✗ Branch 246 → 248 not taken.
104 if (component.hasProperty(endName) && component.hasProperty(QStringLiteral("DURATION")))
102
4/6
✓ Branch 93 → 94 taken 1 time.
✓ Branch 97 → 98 taken 1 time.
✓ Branch 103 → 104 taken 2 times.
✓ Branch 103 → 105 taken 1 time.
✗ Branch 261 → 262 not taken.
✗ Branch 261 → 263 not taken.
4 add(problems, plan, QStringLiteral("end-and-duration"), itemsOf(plan, key, {endName, QStringLiteral("DURATION")}),
103 2 QStringLiteral("An end and a duration were combined"));
104
2/2
✓ Branch 109 → 110 taken 8 times.
✓ Branch 109 → 190 taken 48 times.
56 if (todo) {
105
3/3
✓ Branch 113 → 114 taken 8 times.
✓ Branch 114 → 115 taken 8 times.
✓ Branch 115 → 116 taken 8 times.
8 const QString status = component.value(QStringLiteral("STATUS")).trimmed().toUpper();
106
1/1
✓ Branch 123 → 124 taken 8 times.
8 const bool completedStamp = component.hasProperty(QStringLiteral("COMPLETED"));
107
1/1
✓ Branch 129 → 130 taken 8 times.
8 const bool hasPercent = component.hasProperty(QStringLiteral("PERCENT-COMPLETE"));
108
3/3
✓ Branch 135 → 136 taken 8 times.
✓ Branch 136 → 137 taken 8 times.
✓ Branch 137 → 138 taken 8 times.
8 const int percent = component.value(QStringLiteral("PERCENT-COMPLETE")).trimmed().toInt();
109 8 const bool done = status == QLatin1String("COMPLETED");
110
12/14
✓ Branch 144 → 145 taken 3 times.
✓ Branch 144 → 146 taken 5 times.
✓ Branch 145 → 146 taken 2 times.
✓ Branch 145 → 154 taken 1 time.
✓ Branch 146 → 147 taken 2 times.
✓ Branch 146 → 149 taken 5 times.
✓ Branch 147 → 148 taken 2 times.
✗ Branch 147 → 149 not taken.
✓ Branch 148 → 149 taken 2 times.
✗ Branch 148 → 154 not taken.
✓ Branch 149 → 150 taken 2 times.
✓ Branch 149 → 155 taken 5 times.
✓ Branch 156 → 157 taken 1 time.
✓ Branch 156 → 188 taken 7 times.
10 if ((completedStamp && !done) || (done && hasPercent && percent != 100) ||
111
3/6
✓ Branch 150 → 151 taken 2 times.
✗ Branch 150 → 155 not taken.
✓ Branch 152 → 153 taken 2 times.
✗ Branch 152 → 155 not taken.
✗ Branch 153 → 154 not taken.
✓ Branch 153 → 155 taken 2 times.
2 (hasPercent && percent == 100 && !status.isEmpty() && !done))
112
1/1
✓ Branch 174 → 175 taken 1 time.
1 add(problems, plan, QStringLiteral("task-completion"),
113
3/5
✓ Branch 170 → 171 taken 1 time.
✓ Branch 180 → 181 taken 3 times.
✓ Branch 180 → 182 taken 1 time.
✗ Branch 328 → 329 not taken.
✗ Branch 328 → 330 not taken.
5 itemsOf(plan, key, {QStringLiteral("STATUS"), QStringLiteral("PERCENT-COMPLETE"), QStringLiteral("COMPLETED")}),
114 2 QStringLiteral("Done, progress and completion date do not agree"));
115 8 }
116 74 }
117
118 55 Problems validateIcal(const Merge::Plan &plan) {
119 55 Problems problems;
120
1/1
✓ Branch 2 → 3 taken 55 times.
55 const ICalMergePlan::Applied applied = ICalMergePlan::apply(plan);
121
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 55 times.
55 if (!applied.ok)
122 ✗ return problems; // open conflicts: nothing to validate yet
123 55 QList<ICal::Component> parsed;
124 55 int skipped = 0;
125
7/9
✓ Branch 5 → 6 taken 55 times.
✓ Branch 6 → 7 taken 55 times.
✗ Branch 6 → 10 not taken.
✓ Branch 7 → 8 taken 54 times.
✓ Branch 7 → 10 taken 1 time.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 54 times.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 31 taken 54 times.
55 if (!ICal::parse(applied.text, &parsed, nullptr, &skipped) || skipped > 0 || parsed.isEmpty()) {
126
3/5
✓ Branch 21 → 22 taken 1 time.
✓ Branch 26 → 27 taken 1 time.
✓ Branch 26 → 28 taken 1 time.
✗ Branch 250 → 251 not taken.
✗ Branch 250 → 252 not taken.
2 add(&problems, plan, QStringLiteral("unreadable-result"), {ICalMergePlan::kRawItem},
127 2 QStringLiteral("The result cannot be read"));
128 1 return problems;
129 }
130
12/16
✓ Branch 31 → 32 taken 2 times.
✓ Branch 31 → 37 taken 52 times.
✓ Branch 32 → 33 taken 2 times.
✓ Branch 33 → 34 taken 2 times.
✓ Branch 35 → 36 taken 1 time.
✓ Branch 35 → 37 taken 1 time.
✓ Branch 38 → 39 taken 2 times.
✓ Branch 38 → 40 taken 52 times.
✓ Branch 40 → 41 taken 2 times.
✓ Branch 40 → 42 taken 52 times.
✓ Branch 42 → 43 taken 1 time.
✓ Branch 42 → 61 taken 53 times.
✗ Branch 266 → 267 not taken.
✗ Branch 266 → 268 not taken.
✗ Branch 270 → 271 not taken.
✗ Branch 270 → 272 not taken.
54 if (!plan.comparable && uidsOf(applied.text, false) != uidsOf(plan.localText, false))
131
3/5
✓ Branch 51 → 52 taken 1 time.
✓ Branch 56 → 57 taken 1 time.
✓ Branch 56 → 58 taken 1 time.
✗ Branch 283 → 284 not taken.
✗ Branch 283 → 285 not taken.
2 add(&problems, plan, QStringLiteral("uid-changed"), {ICalMergePlan::kRawItem},
132 2 QStringLiteral("The result must keep the UID"));
133
1/1
✓ Branch 61 → 62 taken 54 times.
54 const ICal::Component calendar = parsed.first();
134
1/1
✓ Branch 63 → 64 taken 54 times.
54 const ICalTime::ZoneResolver zones(calendar);
135 54 QHash<QString, QList<QDateTime>> excluded; // UID -> EXDATE instants of the master
136 54 QHash<QString, QString> masterKey;
137
2/2
✓ Branch 178 → 66 taken 97 times.
✓ Branch 178 → 179 taken 54 times.
151 for (const ICal::Component &component : calendar.components) {
138
6/6
✓ Branch 69 → 70 taken 49 times.
✓ Branch 69 → 74 taken 48 times.
✓ Branch 72 → 73 taken 41 times.
✓ Branch 72 → 74 taken 8 times.
✓ Branch 75 → 76 taken 41 times.
✓ Branch 75 → 77 taken 56 times.
97 if (component.name != QLatin1String("VEVENT") && component.name != QLatin1String("VTODO"))
139 43 continue;
140
1/1
✓ Branch 77 → 78 taken 56 times.
56 checkComponent(&problems, plan, component, zones);
141
3/3
✓ Branch 81 → 82 taken 56 times.
✓ Branch 84 → 85 taken 2 times.
✓ Branch 84 → 86 taken 54 times.
56 if (component.hasProperty(QStringLiteral("RECURRENCE-ID")))
142 2 continue;
143
1/1
✓ Branch 86 → 87 taken 54 times.
54 const QString key = keyOf(component);
144
1/1
✓ Branch 90 → 91 taken 54 times.
54 const QString uid = component.value(QStringLiteral("UID"));
145
1/1
✓ Branch 93 → 94 taken 54 times.
54 masterKey.insert(uid, key);
146
1/1
✓ Branch 97 → 98 taken 54 times.
54 const QList<ICal::Property> exdates = component.propertiesNamed(QStringLiteral("EXDATE"));
147
9/13
✓ Branch 101 → 102 taken 2 times.
✓ Branch 101 → 113 taken 52 times.
✓ Branch 105 → 106 taken 2 times.
✓ Branch 106 → 107 taken 1 time.
✓ Branch 106 → 113 taken 1 time.
✓ Branch 120 → 121 taken 2 times.
✓ Branch 120 → 122 taken 52 times.
✓ Branch 122 → 123 taken 1 time.
✓ Branch 122 → 154 taken 53 times.
✗ Branch 325 → 326 not taken.
✗ Branch 325 → 327 not taken.
✗ Branch 329 → 330 not taken.
✗ Branch 329 → 331 not taken.
111 if (!exdates.isEmpty() && !component.hasProperty(QStringLiteral("RRULE")) &&
148
8/13
✓ Branch 110 → 111 taken 1 time.
✓ Branch 111 → 112 taken 1 time.
✗ Branch 111 → 113 not taken.
✓ Branch 114 → 115 taken 1 time.
✓ Branch 114 → 116 taken 53 times.
✓ Branch 116 → 117 taken 1 time.
✓ Branch 116 → 118 taken 53 times.
✓ Branch 118 → 119 taken 2 times.
✓ Branch 118 → 120 taken 52 times.
✗ Branch 317 → 318 not taken.
✗ Branch 317 → 319 not taken.
✗ Branch 321 → 322 not taken.
✗ Branch 321 → 323 not taken.
55 !component.hasProperty(QStringLiteral("RDATE")))
149
1/1
✓ Branch 140 → 141 taken 1 time.
1 add(&problems, plan, QStringLiteral("exdate-without-recurrence"),
150
3/5
✓ Branch 136 → 137 taken 1 time.
✓ Branch 146 → 147 taken 3 times.
✓ Branch 146 → 148 taken 1 time.
✗ Branch 344 → 345 not taken.
✗ Branch 344 → 346 not taken.
5 itemsOf(plan, key, {QStringLiteral("EXDATE"), QStringLiteral("RRULE"), QStringLiteral("RDATE")}),
151 2 QStringLiteral("Excluded dates without a repetition"));
152
2/2
✓ Branch 170 → 156 taken 2 times.
✓ Branch 170 → 171 taken 54 times.
56 for (const ICal::Property &exdate : exdates)
153
5/5
✓ Branch 157 → 158 taken 2 times.
✓ Branch 158 → 159 taken 2 times.
✓ Branch 159 → 160 taken 2 times.
✓ Branch 166 → 161 taken 2 times.
✓ Branch 166 → 167 taken 2 times.
4 for (const ICalTime::Value &value : ICalTime::parseList(exdate, zones))
154
2/2
✓ Branch 162 → 163 taken 2 times.
✓ Branch 163 → 164 taken 2 times.
4 excluded[uid].append(value.dateTime);
155 54 }
156
2/2
✓ Branch 232 → 181 taken 97 times.
✓ Branch 232 → 233 taken 54 times.
151 for (const ICal::Component &component : calendar.components) {
157
1/1
✓ Branch 185 → 186 taken 97 times.
97 const ICal::Property *recurrence = component.property(QStringLiteral("RECURRENCE-ID"));
158
2/2
✓ Branch 188 → 189 taken 95 times.
✓ Branch 188 → 190 taken 2 times.
97 if (!recurrence)
159 95 continue;
160
1/1
✓ Branch 193 → 194 taken 2 times.
2 const QString uid = component.value(QStringLiteral("UID"));
161
1/1
✓ Branch 196 → 197 taken 2 times.
2 const QDateTime instant = ICalTime::parseProperty(*recurrence, zones).dateTime;
162
2/2
✓ Branch 202 → 203 taken 1 time.
✓ Branch 202 → 227 taken 1 time.
2 if (excluded.value(uid).contains(instant))
163
1/1
✓ Branch 214 → 215 taken 1 time.
1 add(&problems, plan, QStringLiteral("occurrence-moved-and-cancelled"),
164
4/6
✓ Branch 207 → 208 taken 1 time.
✓ Branch 210 → 211 taken 1 time.
✓ Branch 220 → 221 taken 1 time.
✓ Branch 220 → 222 taken 1 time.
✗ Branch 406 → 407 not taken.
✗ Branch 406 → 408 not taken.
4 QStringList{keyOf(component)} + exdateItems(plan, masterKey.value(uid), instant, zones),
165 2 QStringLiteral("An occurrence is both changed and cancelled"));
166 2 }
167 54 return problems;
168 62 }
169
170 7 Problems validateCard(const Merge::Plan &plan) {
171 7 Problems problems;
172
1/1
✓ Branch 5 → 6 taken 7 times.
7 const VCardMergePlan::Applied applied = VCardMergePlan::apply(plan, QStringLiteral("20000101T000000Z"));
173
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 7 times.
7 if (!applied.ok)
174 ✗ return problems;
175 7 ICal::Component card;
176 7 QString error;
177 7 int skipped = 0;
178
6/7
✓ Branch 10 → 11 taken 7 times.
✓ Branch 11 → 12 taken 6 times.
✓ Branch 11 → 13 taken 1 time.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 6 times.
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 34 taken 6 times.
7 if (!VCardCodec::parseTree(applied.text, &card, &error, &skipped) || skipped > 0) {
179
3/5
✓ Branch 24 → 25 taken 1 time.
✓ Branch 29 → 30 taken 1 time.
✓ Branch 29 → 31 taken 1 time.
✗ Branch 123 → 124 not taken.
✗ Branch 123 → 125 not taken.
2 add(&problems, plan, QStringLiteral("unreadable-result"), {VCardMergePlan::kRawItem},
180 2 QStringLiteral("The result cannot be read"));
181 1 return problems;
182 }
183
12/16
✓ Branch 34 → 35 taken 2 times.
✓ Branch 34 → 40 taken 4 times.
✓ Branch 35 → 36 taken 2 times.
✓ Branch 36 → 37 taken 2 times.
✓ Branch 38 → 39 taken 1 time.
✓ Branch 38 → 40 taken 1 time.
✓ Branch 41 → 42 taken 2 times.
✓ Branch 41 → 43 taken 4 times.
✓ Branch 43 → 44 taken 2 times.
✓ Branch 43 → 45 taken 4 times.
✓ Branch 45 → 46 taken 1 time.
✓ Branch 45 → 64 taken 5 times.
✗ Branch 139 → 140 not taken.
✗ Branch 139 → 141 not taken.
✗ Branch 143 → 144 not taken.
✗ Branch 143 → 145 not taken.
6 if (!plan.comparable && uidsOf(applied.text, true) != uidsOf(plan.localText, true))
184
3/5
✓ Branch 54 → 55 taken 1 time.
✓ Branch 59 → 60 taken 1 time.
✓ Branch 59 → 61 taken 1 time.
✗ Branch 156 → 157 not taken.
✗ Branch 156 → 158 not taken.
2 add(&problems, plan, QStringLiteral("uid-changed"), {VCardMergePlan::kRawItem},
185 2 QStringLiteral("The result must keep the UID"));
186
5/5
✓ Branch 67 → 68 taken 6 times.
✓ Branch 68 → 69 taken 6 times.
✓ Branch 69 → 70 taken 6 times.
✓ Branch 76 → 77 taken 1 time.
✓ Branch 76 → 102 taken 5 times.
6 if (ICal::unescapeText(card.value(QStringLiteral("FN"))).trimmed().isEmpty())
187
3/5
✓ Branch 90 → 91 taken 1 time.
✓ Branch 95 → 96 taken 2 times.
✓ Branch 95 → 97 taken 1 time.
✗ Branch 194 → 195 not taken.
✗ Branch 194 → 196 not taken.
3 add(&problems, plan, QStringLiteral("contact-without-name"), {QStringLiteral("FN"), QStringLiteral("N")},
188 2 QStringLiteral("A contact needs a name"));
189 6 return problems;
190 12 }
191
192 6 Problems validateSieve(const Merge::Plan &plan) {
193 6 Problems problems;
194
3/3
✓ Branch 2 → 3 taken 6 times.
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 14 taken 4 times.
6 if (Merge::uniformSide(plan) == Merge::Choice::None) {
195
2/2
✓ Branch 12 → 6 taken 8 times.
✓ Branch 12 → 13 taken 2 times.
10 for (const Merge::Item &item : plan.items)
196
2/3
✓ Branch 7 → 8 taken 8 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 8 times.
8 if (Merge::itemOpen(plan, item))
197 ✗ return problems;
198 }
199
1/1
✓ Branch 14 → 15 taken 6 times.
6 const SieveMergePlan::Applied applied = SieveMergePlan::apply(plan);
200
7/11
✓ Branch 15 → 16 taken 6 times.
✗ Branch 15 → 19 not taken.
✓ Branch 16 → 17 taken 6 times.
✓ Branch 17 → 18 taken 5 times.
✓ Branch 17 → 19 taken 1 time.
✓ Branch 20 → 21 taken 6 times.
✗ Branch 20 → 22 not taken.
✓ Branch 22 → 23 taken 5 times.
✓ Branch 22 → 24 taken 1 time.
✗ Branch 49 → 50 not taken.
✗ Branch 49 → 51 not taken.
6 if (applied.ok && !Sieve::decode(applied.script).readOnly)
201 5 return problems;
202 1 QStringList chosen;
203
2/2
✓ Branch 32 → 26 taken 1 time.
✓ Branch 32 → 33 taken 1 time.
2 for (const Merge::Item &item : plan.items)
204
1/4
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 1 time.
✗ Branch 28 → 29 not taken.
✗ Branch 28 → 30 not taken.
1 if (item.state == Merge::State::Conflict || item.touched)
205
1/1
✓ Branch 29 → 30 taken 1 time.
1 chosen.append(item.id);
206
1/1
✓ Branch 39 → 40 taken 1 time.
2 add(&problems, plan, QStringLiteral("script-does-not-parse"), chosen, QStringLiteral("The combined script does not parse"));
207 1 return problems;
208 6 }
209
210 6 Problems validateSettings(const Merge::Plan &plan) {
211 6 Problems problems;
212
1/1
✓ Branch 2 → 3 taken 6 times.
6 const SyncPayload result = SettingsMergePlan::apply(plan);
213 6 QSet<QString> accounts;
214
2/2
✓ Branch 10 → 6 taken 3 times.
✓ Branch 10 → 11 taken 6 times.
9 for (const SyncPayload::CardDavAccount &account : result.carddavAccounts)
215
1/1
✓ Branch 7 → 8 taken 3 times.
3 accounts.insert(account.id);
216
2/2
✓ Branch 44 → 13 taken 5 times.
✓ Branch 44 → 45 taken 6 times.
11 for (const SyncPayload::CalDavSyncConfig &config : result.caldavConfigs)
217
2/2
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 42 taken 4 times.
5 if (!accounts.contains(config.carddavAccountId))
218
3/5
✓ Branch 29 → 30 taken 1 time.
✓ Branch 34 → 35 taken 2 times.
✓ Branch 34 → 36 taken 1 time.
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 62 not taken.
3 add(&problems, plan, QStringLiteral("calendars-without-account"),
219 2 {SettingsMergePlan::accountItemId(config.carddavAccountId, QStringLiteral("calendars")),
220 1 SettingsMergePlan::accountItemId(config.carddavAccountId)},
221 2 QStringLiteral("Calendars of an account that is not there"));
222 6 return problems;
223 8 }
224
225 } // namespace
226
227 75 QList<Merge::ValidationProblem> validate(const Merge::Plan &plan) {
228
2/2
✓ Branch 4 → 5 taken 55 times.
✓ Branch 4 → 6 taken 20 times.
75 if (plan.domain == QLatin1String("ical"))
229 55 return validateIcal(plan);
230
2/2
✓ Branch 8 → 9 taken 7 times.
✓ Branch 8 → 10 taken 13 times.
20 if (plan.domain == QLatin1String("vcard"))
231 7 return validateCard(plan);
232
2/2
✓ Branch 12 → 13 taken 6 times.
✓ Branch 12 → 14 taken 7 times.
13 if (plan.domain == QLatin1String("sieve"))
233 6 return validateSieve(plan);
234
2/2
✓ Branch 16 → 17 taken 6 times.
✓ Branch 16 → 18 taken 1 time.
7 if (plan.domain == QLatin1String("settings"))
235 6 return validateSettings(plan);
236 1 return {};
237 }
238
239 7 bool fixWithSameSide(Merge::Plan *plan, const Merge::ValidationProblem &problem) {
240
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 6 times.
7 if (problem.itemIds.isEmpty())
241 1 return false;
242 6 Merge::Choice side = Merge::Choice::Local;
243
1/2
✓ Branch 15 → 7 taken 6 times.
✗ Branch 15 → 16 not taken.
6 for (const QString &id : problem.itemIds) {
244
1/1
✓ Branch 8 → 9 taken 6 times.
6 const Merge::Item *item = plan->item(id);
245
4/6
✓ Branch 9 → 10 taken 6 times.
✗ Branch 9 → 13 not taken.
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 5 times.
✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 13 not taken.
6 if (item && (item->choice == Merge::Choice::Local || item->choice == Merge::Choice::Server)) {
246 6 side = item->choice;
247 6 break;
248 }
249 }
250 6 bool changed = false;
251
2/2
✓ Branch 26 → 18 taken 12 times.
✓ Branch 26 → 27 taken 6 times.
18 for (const QString &id : problem.itemIds)
252
2/5
✓ Branch 19 → 20 taken 12 times.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 12 times.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
12 changed = Merge::choose(plan, id, side) || changed;
253 6 return changed;
254 }
255
256 } // namespace MergeValidation
257