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: 96.0% 267 / 0 / 278
Functions: 100.0% 16 / 0 / 16
Branches: 87.0% 289 / 0 / 332

data/SieveMergePlan.cpp
Line Branch Exec Source
1 #include "SieveMergePlan.h"
2
3 #include "SieveRuleCodec.h"
4
5 #include <QCryptographicHash>
6 #include <QHash>
7
8 namespace SieveMergePlan {
9
10 namespace {
11
12 struct Keyed {
13 QString key;
14 QString name;
15 QByteArray bytes;
16 Sieve::BlockKind kind = Sieve::BlockKind::Advanced;
17 QStringList extensions;
18 };
19
20 struct Scripts {
21 Sieve::Document base;
22 Sieve::Document local;
23 Sieve::Document server;
24 QList<Keyed> baseBlocks;
25 QList<Keyed> localBlocks;
26 QList<Keyed> serverBlocks;
27 QHash<QString, const Keyed *> baseMap;
28 QHash<QString, const Keyed *> localMap;
29 QHash<QString, const Keyed *> serverMap;
30 bool readable = true;
31 };
32
33 744 QList<Keyed> keyedBlocks(const Sieve::Document &document) {
34 744 QList<Keyed> result;
35 744 QHash<QString, int> occurrences;
36
2/2
✓ Branch 71 → 4 taken 32452 times.
✓ Branch 71 → 72 taken 744 times.
33196 for (const Sieve::Block &block : document.blocks) {
37 32452 QString base;
38
2/2
✓ Branch 5 → 6 taken 744 times.
✓ Branch 5 → 13 taken 31708 times.
32452 if (block.kind == Sieve::BlockKind::Require)
39 744 base = QStringLiteral("require");
40
2/2
✓ Branch 13 → 14 taken 1671 times.
✓ Branch 13 → 27 taken 30037 times.
31708 else if (block.named)
41
2/2
✓ Branch 18 → 19 taken 1671 times.
✓ Branch 20 → 21 taken 1671 times.
5013 base = QStringLiteral("n:%1:%2").arg(static_cast<int>(block.kind)).arg(block.name);
42 else
43 60074 base = QStringLiteral("b:") +
44
4/4
✓ Branch 28 → 29 taken 30037 times.
✓ Branch 29 → 30 taken 30037 times.
✓ Branch 30 → 31 taken 30037 times.
✓ Branch 34 → 35 taken 30037 times.
90111 QString::fromLatin1(QCryptographicHash::hash(block.original, QCryptographicHash::Sha256).toHex());
45
1/1
✓ Branch 43 → 44 taken 32452 times.
32452 const int occurrence = occurrences[base]++;
46 32452 QString name = block.name;
47
2/2
✓ Branch 46 → 47 taken 30781 times.
✓ Branch 46 → 55 taken 1671 times.
32452 if (name.isEmpty())
48
3/3
✓ Branch 47 → 48 taken 30781 times.
✓ Branch 48 → 49 taken 30781 times.
✓ Branch 49 → 50 taken 30781 times.
30781 name = QString::fromUtf8(block.original.trimmed().left(40));
49
2/2
✓ Branch 55 → 56 taken 32452 times.
✓ Branch 58 → 59 taken 32452 times.
64904 result.append({base + QLatin1Char('#') + QString::number(occurrence), name, block.original, block.kind,
50 32452 block.extensions});
51 32452 }
52 744 return result;
53 33196 }
54
55 744 QHash<QString, const Keyed *> byKey(const QList<Keyed> &blocks) {
56 744 QHash<QString, const Keyed *> map;
57
2/2
✓ Branch 8 → 4 taken 32452 times.
✓ Branch 8 → 9 taken 744 times.
33196 for (const Keyed &block : blocks)
58
1/1
✓ Branch 5 → 6 taken 32452 times.
32452 map.insert(block.key, &block);
59 744 return map;
60 ✗ }
61
62 428 QStringList commonOrder(const QList<Keyed> &blocks, const QHash<QString, const Keyed *> &a,
63 const QHash<QString, const Keyed *> &b) {
64 428 QStringList keys;
65
2/2
✓ Branch 15 → 4 taken 21421 times.
✓ Branch 15 → 16 taken 428 times.
21849 for (const Keyed &block : blocks)
66
6/6
✓ Branch 6 → 7 taken 21264 times.
✓ Branch 6 → 10 taken 157 times.
✓ Branch 8 → 9 taken 21212 times.
✓ Branch 8 → 10 taken 52 times.
✓ Branch 11 → 12 taken 21212 times.
✓ Branch 11 → 13 taken 209 times.
21421 if (a.contains(block.key) && b.contains(block.key))
67
1/1
✓ Branch 12 → 13 taken 21212 times.
21212 keys.append(block.key);
68 428 return keys;
69 ✗ }
70
71 // Keyed lists point into the Scripts object: it is filled in place.
72 259 void parseScripts(Scripts *scripts, const QByteArray &base, const QByteArray &local, const QByteArray &server) {
73
1/1
✓ Branch 2 → 3 taken 259 times.
259 scripts->base = Sieve::decode(base);
74
1/1
✓ Branch 5 → 6 taken 259 times.
259 scripts->local = Sieve::decode(local);
75
1/1
✓ Branch 8 → 9 taken 259 times.
259 scripts->server = Sieve::decode(server);
76
4/6
✓ Branch 11 → 12 taken 259 times.
✗ Branch 11 → 14 not taken.
✓ Branch 12 → 13 taken 259 times.
✗ Branch 12 → 14 not taken.
✓ Branch 13 → 14 taken 11 times.
✓ Branch 13 → 15 taken 248 times.
259 if (scripts->base.readOnly || scripts->local.readOnly || scripts->server.readOnly) {
77 11 scripts->readable = false;
78 11 return;
79 }
80
1/1
✓ Branch 15 → 16 taken 248 times.
248 scripts->baseBlocks = keyedBlocks(scripts->base);
81
1/1
✓ Branch 18 → 19 taken 248 times.
248 scripts->localBlocks = keyedBlocks(scripts->local);
82
1/1
✓ Branch 21 → 22 taken 248 times.
248 scripts->serverBlocks = keyedBlocks(scripts->server);
83
1/1
✓ Branch 24 → 25 taken 248 times.
248 scripts->baseMap = byKey(scripts->baseBlocks);
84
1/1
✓ Branch 27 → 28 taken 248 times.
248 scripts->localMap = byKey(scripts->localBlocks);
85
1/1
✓ Branch 30 → 31 taken 248 times.
248 scripts->serverMap = byKey(scripts->serverBlocks);
86 }
87
88 struct Orders {
89 QStringList base;
90 QStringList local;
91 QStringList server;
92 bool localMoved = false;
93 bool serverMoved = false;
94
4/6
✓ Branch 2 → 3 taken 32 times.
✗ Branch 2 → 7 not taken.
✓ Branch 3 → 4 taken 16 times.
✓ Branch 3 → 7 taken 16 times.
✓ Branch 5 → 6 taken 16 times.
✗ Branch 5 → 7 not taken.
32 bool conflict() const { return localMoved && serverMoved && local != server; }
95 };
96
97 214 Orders ordersOf(const Scripts &s) {
98 214 Orders orders;
99
2/2
✓ Branch 15 → 4 taken 10670 times.
✓ Branch 15 → 16 taken 214 times.
10884 for (const Keyed &block : s.baseBlocks)
100
6/6
✓ Branch 6 → 7 taken 10608 times.
✓ Branch 6 → 10 taken 62 times.
✓ Branch 8 → 9 taken 10606 times.
✓ Branch 8 → 10 taken 2 times.
✓ Branch 11 → 12 taken 10606 times.
✓ Branch 11 → 13 taken 64 times.
10670 if (s.localMap.contains(block.key) && s.serverMap.contains(block.key))
101
1/1
✓ Branch 12 → 13 taken 10606 times.
10606 orders.base.append(block.key);
102
1/1
✓ Branch 16 → 17 taken 214 times.
214 orders.local = commonOrder(s.localBlocks, s.baseMap, s.serverMap);
103
1/1
✓ Branch 19 → 20 taken 214 times.
214 orders.server = commonOrder(s.serverBlocks, s.baseMap, s.localMap);
104
1/1
✓ Branch 22 → 23 taken 214 times.
214 orders.localMoved = orders.local != orders.base;
105
1/1
✓ Branch 23 → 24 taken 214 times.
214 orders.serverMoved = orders.server != orders.base;
106 214 return orders;
107 ✗ }
108
109 // Primary order (the leading side's blocks), the other side's blocks after
110 // their predecessor, then blocks only the base has.
111 214 QStringList sequenceOf(const Scripts &s, bool localLeads) {
112
2/2
✓ Branch 2 → 3 taken 18 times.
✓ Branch 2 → 4 taken 196 times.
214 const QList<Keyed> &primary = localLeads ? s.localBlocks : s.serverBlocks;
113
2/2
✓ Branch 5 → 6 taken 18 times.
✓ Branch 5 → 7 taken 196 times.
214 const QList<Keyed> &secondary = localLeads ? s.serverBlocks : s.localBlocks;
114 214 QStringList sequence;
115
2/2
✓ Branch 14 → 10 taken 10747 times.
✓ Branch 14 → 15 taken 214 times.
10961 for (const Keyed &block : primary)
116
1/1
✓ Branch 11 → 12 taken 10747 times.
10747 sequence.append(block.key);
117 214 QString predecessor;
118
2/2
✓ Branch 29 → 17 taken 10674 times.
✓ Branch 29 → 30 taken 214 times.
10888 for (const Keyed &block : secondary) {
119
2/2
✓ Branch 19 → 20 taken 68 times.
✓ Branch 19 → 26 taken 10606 times.
10674 if (!sequence.contains(block.key)) {
120
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 68 times.
68 const int at = predecessor.isEmpty() ? 0 : sequence.indexOf(predecessor) + 1;
121
1/1
✓ Branch 25 → 26 taken 68 times.
68 sequence.insert(at, block.key);
122 }
123 10674 predecessor = block.key;
124 }
125
2/2
✓ Branch 38 → 32 taken 10670 times.
✓ Branch 38 → 39 taken 214 times.
10884 for (const Keyed &block : s.baseBlocks)
126
2/2
✓ Branch 34 → 35 taken 12 times.
✓ Branch 34 → 36 taken 10658 times.
10670 if (!sequence.contains(block.key))
127
1/1
✓ Branch 35 → 36 taken 12 times.
12 sequence.append(block.key);
128 214 return sequence;
129 214 }
130
131 enum class Decision { Local, Server, Delete, Conflict, MergedRequire };
132
133 // Sieve::merge3's decision for one block identity.
134 10633 Decision decide(const Keyed *base, const Keyed *mine, const Keyed *theirs) {
135
4/4
✓ Branch 2 → 3 taken 10527 times.
✓ Branch 2 → 26 taken 106 times.
✓ Branch 3 → 4 taken 10478 times.
✓ Branch 3 → 26 taken 49 times.
10633 if (mine && theirs) {
136
2/2
✓ Branch 5 → 6 taken 10377 times.
✓ Branch 5 → 7 taken 101 times.
10478 if (mine->bytes == theirs->bytes)
137 10377 return Decision::Local;
138
5/6
✓ Branch 7 → 8 taken 101 times.
✗ Branch 7 → 11 not taken.
✓ Branch 9 → 10 taken 24 times.
✓ Branch 9 → 11 taken 77 times.
✓ Branch 12 → 13 taken 24 times.
✓ Branch 12 → 14 taken 77 times.
101 if (base && mine->bytes == base->bytes)
139 24 return Decision::Server;
140
5/6
✓ Branch 14 → 15 taken 77 times.
✗ Branch 14 → 18 not taken.
✓ Branch 16 → 17 taken 23 times.
✓ Branch 16 → 18 taken 54 times.
✓ Branch 19 → 20 taken 23 times.
✓ Branch 19 → 21 taken 54 times.
77 if (base && theirs->bytes == base->bytes)
141 23 return Decision::Local;
142
1/2
✓ Branch 22 → 23 taken 15 times.
✗ Branch 22 → 24 not taken.
15 return mine->kind == Sieve::BlockKind::Require && theirs->kind == Sieve::BlockKind::Require
143
2/2
✓ Branch 21 → 22 taken 15 times.
✓ Branch 21 → 24 taken 39 times.
69 ? Decision::MergedRequire
144 54 : Decision::Conflict;
145 }
146
2/2
✓ Branch 26 → 27 taken 49 times.
✓ Branch 26 → 34 taken 106 times.
155 if (mine) {
147
2/2
✓ Branch 27 → 28 taken 47 times.
✓ Branch 27 → 29 taken 2 times.
49 if (!base)
148 47 return Decision::Local;
149
2/2
✓ Branch 30 → 31 taken 1 time.
✓ Branch 30 → 32 taken 1 time.
2 return mine->bytes == base->bytes ? Decision::Delete : Decision::Conflict;
150 }
151
2/2
✓ Branch 34 → 35 taken 94 times.
✓ Branch 34 → 42 taken 12 times.
106 if (theirs) {
152
2/2
✓ Branch 35 → 36 taken 59 times.
✓ Branch 35 → 37 taken 35 times.
94 if (!base)
153 59 return Decision::Server;
154
2/2
✓ Branch 38 → 39 taken 10 times.
✓ Branch 38 → 40 taken 25 times.
35 return theirs->bytes == base->bytes ? Decision::Delete : Decision::Conflict;
155 }
156 12 return Decision::Delete;
157 }
158
159 241 void appendBlock(QByteArray *out, const QByteArray &bytes, const QByteArray &lineEnd) {
160
4/6
✓ Branch 3 → 4 taken 174 times.
✓ Branch 3 → 7 taken 67 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 174 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 241 times.
241 if (!out->isEmpty() && !out->endsWith('\n'))
161 ✗ *out += lineEnd;
162 241 *out += bytes;
163 241 }
164
165 31221 Merge::Value valueOf(const Keyed *block) {
166
2/2
✓ Branch 2 → 3 taken 30972 times.
✓ Branch 2 → 4 taken 249 times.
31221 return block ? Merge::Value{true, QString::fromUtf8(block->bytes)} : Merge::Value{};
167 }
168
169 11 Merge::Plan rawPlan(const QByteArray &base, const QByteArray &local, const QByteArray &server) {
170 11 Merge::Plan plan;
171 11 plan.domain = QStringLiteral("sieve");
172 11 plan.comparable = false;
173 11 plan.error = QStringLiteral("A version of the script does not parse");
174
1/1
✓ Branch 14 → 15 taken 11 times.
11 plan.baseText = QString::fromUtf8(base);
175
1/1
✓ Branch 17 → 18 taken 11 times.
11 plan.localText = QString::fromUtf8(local);
176
1/1
✓ Branch 20 → 21 taken 11 times.
11 plan.serverText = QString::fromUtf8(server);
177 11 Merge::Item raw;
178 11 raw.id = kRawItem;
179 11 raw.name = QStringLiteral("Script");
180 11 raw.kind = Merge::Kind::Text;
181 11 raw.base = {true, plan.baseText};
182 11 raw.local = {true, plan.localText};
183 11 raw.server = {true, plan.serverText};
184
2/3
✓ Branch 39 → 40 taken 11 times.
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 42 taken 11 times.
11 raw.state = Merge::stateOf(raw.base, raw.local, raw.server) == Merge::State::Same ? Merge::State::Same
185 : Merge::State::Conflict;
186 11 raw.basePlain = plan.baseText;
187 11 raw.localPlain = plan.localText;
188 11 raw.serverPlain = plan.serverText;
189
1/1
✓ Branch 46 → 47 taken 11 times.
11 raw.hunks = TextMerge::diff3(plan.baseText, plan.localText, plan.serverText);
190
1/1
✓ Branch 49 → 50 taken 11 times.
11 plan.items.append(raw);
191
1/1
✓ Branch 50 → 51 taken 11 times.
11 Merge::applyDefaults(&plan);
192 11 return plan;
193 44 }
194
195 67 bool localLeads(const Merge::Plan &plan, const Orders &orders) {
196
6/6
✓ Branch 3 → 4 taken 13 times.
✓ Branch 3 → 7 taken 54 times.
✓ Branch 5 → 6 taken 6 times.
✓ Branch 5 → 7 taken 7 times.
✓ Branch 8 → 9 taken 6 times.
✓ Branch 8 → 10 taken 61 times.
67 if (const Merge::Item *order = plan.item(kOrderItem); order && orders.conflict())
197 6 return order->choice == Merge::Choice::Local;
198
3/4
✓ Branch 10 → 11 taken 7 times.
✓ Branch 10 → 13 taken 54 times.
✓ Branch 11 → 12 taken 7 times.
✗ Branch 11 → 13 not taken.
61 return orders.localMoved && !orders.serverMoved;
199 }
200
201 } // namespace
202
203 158 Merge::Plan analyze(const QByteArray &base, const QByteArray &local, const QByteArray &server) {
204
1/1
✓ Branch 2 → 3 taken 158 times.
158 Scripts s;
205
1/1
✓ Branch 3 → 4 taken 158 times.
158 parseScripts(&s, base, local, server);
206
2/2
✓ Branch 4 → 5 taken 11 times.
✓ Branch 4 → 7 taken 147 times.
158 if (!s.readable)
207
1/1
✓ Branch 5 → 6 taken 11 times.
11 return rawPlan(base, local, server);
208 147 Merge::Plan plan;
209 147 plan.domain = QStringLiteral("sieve");
210
1/1
✓ Branch 13 → 14 taken 147 times.
147 plan.baseText = QString::fromUtf8(base);
211
1/1
✓ Branch 16 → 17 taken 147 times.
147 plan.localText = QString::fromUtf8(local);
212
1/1
✓ Branch 19 → 20 taken 147 times.
147 plan.serverText = QString::fromUtf8(server);
213
1/1
✓ Branch 22 → 23 taken 147 times.
147 const Orders orders = ordersOf(s);
214
3/4
✓ Branch 23 → 24 taken 128 times.
✓ Branch 23 → 25 taken 19 times.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 60 taken 128 times.
147 if (orders.localMoved || orders.serverMoved) {
215 19 Merge::Item order;
216 19 order.id = kOrderItem;
217 19 order.name = QStringLiteral("Rule order");
218 19 order.kind = Merge::Kind::Order;
219 38 order.base = {true, orders.base.join(QLatin1Char('\n'))};
220 38 order.local = {true, orders.local.join(QLatin1Char('\n'))};
221 38 order.server = {true, orders.server.join(QLatin1Char('\n'))};
222
2/2
✓ Branch 48 → 49 taken 10 times.
✓ Branch 48 → 50 taken 9 times.
28 order.state = orders.conflict() ? Merge::State::Conflict
223
1/2
✗ Branch 50 → 51 not taken.
✓ Branch 50 → 55 taken 9 times.
9 : !orders.serverMoved ? Merge::State::LocalOnly
224 ✗ : !orders.localMoved ? Merge::State::ServerOnly
225 : Merge::State::BothSame;
226
1/1
✓ Branch 57 → 58 taken 19 times.
19 plan.items.append(order);
227 19 }
228
9/9
✓ Branch 60 → 61 taken 19 times.
✓ Branch 60 → 63 taken 128 times.
✓ Branch 61 → 62 taken 9 times.
✓ Branch 61 → 63 taken 10 times.
✓ Branch 64 → 65 taken 147 times.
✓ Branch 65 → 66 taken 147 times.
✓ Branch 66 → 67 taken 147 times.
✓ Branch 132 → 68 taken 10566 times.
✓ Branch 132 → 133 taken 147 times.
10713 for (const QString &key : sequenceOf(s, orders.localMoved && !orders.serverMoved)) {
229 10566 const Keyed *b = s.baseMap.value(key), *l = s.localMap.value(key), *t = s.serverMap.value(key);
230
4/4
✓ Branch 72 → 73 taken 106 times.
✓ Branch 72 → 77 taken 10460 times.
✓ Branch 73 → 74 taken 94 times.
✓ Branch 73 → 75 taken 12 times.
10566 const Keyed *any = l ? l : t ? t : b;
231 10566 const Decision decision = decide(b, l, t);
232
3/4
✓ Branch 79 → 80 taken 147 times.
✓ Branch 79 → 82 taken 10419 times.
✓ Branch 80 → 81 taken 147 times.
✗ Branch 80 → 82 not taken.
10566 if (any->kind == Sieve::BlockKind::Require && decision != Decision::Conflict)
233 159 continue; // computed in apply()
234
4/4
✓ Branch 82 → 83 taken 106 times.
✓ Branch 82 → 85 taken 10313 times.
✓ Branch 83 → 84 taken 12 times.
✓ Branch 83 → 85 taken 94 times.
10419 if (!l && !t)
235 12 continue; // removed on both sides
236 10407 Merge::Item item;
237 10407 item.id = key;
238 10407 item.name = any->name;
239 10407 item.kind = Merge::Kind::Component;
240
2/2
✓ Branch 87 → 88 taken 23 times.
✓ Branch 87 → 95 taken 10384 times.
10407 if (any->kind == Sieve::BlockKind::Vacation)
241 23 item.context = QStringLiteral("vacation");
242
1/1
✓ Branch 95 → 96 taken 10407 times.
10407 item.base = valueOf(b);
243
1/1
✓ Branch 98 → 99 taken 10407 times.
10407 item.local = valueOf(l);
244
1/1
✓ Branch 101 → 102 taken 10407 times.
10407 item.server = valueOf(t);
245
4/5
✓ Branch 104 → 105 taken 10259 times.
✓ Branch 104 → 117 taken 72 times.
✓ Branch 104 → 118 taken 11 times.
✓ Branch 104 → 122 taken 65 times.
✗ Branch 104 → 126 not taken.
10407 switch (decision) {
246 10259 case Decision::Local:
247
4/4
✓ Branch 106 → 107 taken 10212 times.
✓ Branch 106 → 115 taken 47 times.
✓ Branch 108 → 109 taken 10189 times.
✓ Branch 108 → 115 taken 23 times.
10259 item.state = l && t && l->bytes == t->bytes
248
3/6
✓ Branch 105 → 106 taken 10259 times.
✗ Branch 105 → 115 not taken.
✓ Branch 109 → 110 taken 10189 times.
✗ Branch 109 → 113 not taken.
✓ Branch 111 → 112 taken 10189 times.
✗ Branch 111 → 113 not taken.
20518 ? (b && b->bytes == l->bytes ? Merge::State::Same : Merge::State::BothSame)
249 : Merge::State::LocalOnly;
250 10259 break;
251 72 case Decision::Server:
252 72 item.state = Merge::State::ServerOnly;
253 72 break;
254 11 case Decision::Delete:
255
2/2
✓ Branch 118 → 119 taken 1 time.
✓ Branch 118 → 120 taken 10 times.
11 item.state = l ? Merge::State::ServerOnly : Merge::State::LocalOnly; // removed on the other side
256 11 break;
257 65 case Decision::Conflict:
258 case Decision::MergedRequire:
259 65 item.state = Merge::State::Conflict;
260
4/4
✓ Branch 122 → 123 taken 40 times.
✓ Branch 122 → 124 taken 25 times.
✓ Branch 123 → 124 taken 1 time.
✓ Branch 123 → 125 taken 39 times.
65 if (!l || !t)
261 26 item.kind = Merge::Kind::Deletion;
262 65 break;
263 }
264
1/1
✓ Branch 126 → 127 taken 10407 times.
10407 plan.items.append(item);
265 10554 }
266
1/1
✓ Branch 134 → 135 taken 147 times.
147 Merge::applyDefaults(&plan);
267 147 return plan;
268 291 }
269
270 78 Applied apply(const Merge::Plan &plan) {
271 78 Applied applied;
272
2/2
✓ Branch 16 → 4 taken 221 times.
✓ Branch 16 → 17 taken 71 times.
292 for (const Merge::Item &item : plan.items)
273
3/3
✓ Branch 5 → 6 taken 221 times.
✓ Branch 6 → 7 taken 7 times.
✓ Branch 6 → 14 taken 214 times.
221 if (Merge::itemOpen(plan, item)) {
274 7 applied.error = QStringLiteral("Open conflicts remain");
275 7 return applied;
276 }
277
2/2
✓ Branch 17 → 18 taken 4 times.
✓ Branch 17 → 33 taken 67 times.
71 if (!plan.comparable) {
278
1/1
✓ Branch 18 → 19 taken 4 times.
4 const Merge::Item *raw = plan.item(kRawItem);
279 4 applied.ok = raw != nullptr;
280
4/8
✓ Branch 19 → 20 taken 4 times.
✗ Branch 19 → 22 not taken.
✓ Branch 20 → 21 taken 4 times.
✓ Branch 21 → 23 taken 4 times.
✓ Branch 25 → 26 taken 4 times.
✗ Branch 25 → 27 not taken.
✗ Branch 142 → 143 not taken.
✗ Branch 142 → 144 not taken.
4 applied.script = raw ? Merge::resultText(*raw).toUtf8() : QByteArray();
281
1/2
✓ Branch 27 → 28 taken 4 times.
✗ Branch 27 → 29 not taken.
4 applied.error = raw ? QString() : plan.error;
282 4 return applied;
283 }
284
1/1
✓ Branch 33 → 34 taken 67 times.
67 Scripts s;
285
4/4
✓ Branch 34 → 35 taken 67 times.
✓ Branch 35 → 36 taken 67 times.
✓ Branch 36 → 37 taken 67 times.
✓ Branch 37 → 38 taken 67 times.
67 parseScripts(&s, plan.baseText.toUtf8(), plan.localText.toUtf8(), plan.serverText.toUtf8());
286
1/2
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 49 taken 67 times.
67 if (!s.readable) {
287 ✗ applied.error = QStringLiteral("A version of the script does not parse");
288 ✗ return applied;
289 }
290
1/1
✓ Branch 49 → 50 taken 67 times.
67 const Orders orders = ordersOf(s);
291 // A block the user took from this computer against the automatic merge
292 // may need extensions only the local require lists.
293 67 bool localOverride = false;
294
2/2
✓ Branch 60 → 52 taken 207 times.
✓ Branch 60 → 61 taken 67 times.
274 for (const Merge::Item &item : plan.items)
295
4/4
✓ Branch 53 → 54 taken 111 times.
✓ Branch 53 → 58 taken 96 times.
✓ Branch 54 → 55 taken 78 times.
✓ Branch 54 → 58 taken 33 times.
207 if (item.choice == Merge::Choice::Local && item.state != Merge::State::LocalOnly &&
296
3/4
✓ Branch 55 → 56 taken 17 times.
✓ Branch 55 → 58 taken 61 times.
✓ Branch 56 → 57 taken 17 times.
✗ Branch 56 → 58 not taken.
78 item.state != Merge::State::Same && item.state != Merge::State::BothSame)
297 17 localOverride = true;
298 67 QByteArray merged;
299 67 const QByteArray &lineEnd = s.local.style.lineEnd;
300
6/6
✓ Branch 61 → 62 taken 67 times.
✓ Branch 62 → 63 taken 67 times.
✓ Branch 63 → 64 taken 67 times.
✓ Branch 64 → 65 taken 67 times.
✓ Branch 122 → 66 taken 261 times.
✓ Branch 122 → 123 taken 67 times.
328 for (const QString &key : sequenceOf(s, localLeads(plan, orders))) {
301 261 const Keyed *b = s.baseMap.value(key), *l = s.localMap.value(key), *t = s.serverMap.value(key);
302
1/1
✓ Branch 70 → 71 taken 261 times.
261 const Merge::Item *item = plan.item(key);
303
3/4
✓ Branch 71 → 72 taken 47 times.
✓ Branch 71 → 76 taken 214 times.
✓ Branch 72 → 73 taken 47 times.
✗ Branch 72 → 74 not taken.
261 const Keyed *any = l ? l : t ? t : b;
304
2/2
✓ Branch 77 → 78 taken 67 times.
✓ Branch 77 → 103 taken 194 times.
261 if (!item) {
305 67 const Decision decision = decide(b, l, t);
306
2/2
✓ Branch 79 → 80 taken 58 times.
✓ Branch 79 → 81 taken 9 times.
67 if (decision == Decision::Local)
307
1/1
✓ Branch 80 → 102 taken 58 times.
58 appendBlock(&merged, l->bytes, lineEnd);
308
2/2
✓ Branch 81 → 82 taken 3 times.
✓ Branch 81 → 97 taken 6 times.
9 else if (decision == Decision::Server) {
309
1/5
✗ Branch 83 → 84 not taken.
✗ Branch 83 → 87 not taken.
✗ Branch 84 → 85 not taken.
✗ Branch 84 → 87 not taken.
✓ Branch 88 → 89 taken 3 times.
3 appendBlock(&merged, localOverride && l && any->kind == Sieve::BlockKind::Require
310
1/3
✗ Branch 82 → 83 not taken.
✓ Branch 82 → 87 taken 3 times.
✗ Branch 85 → 86 not taken.
6 ? Sieve::addRequirements(t->bytes, l->extensions)
311 3 : t->bytes,
312 lineEnd);
313
3/6
✓ Branch 90 → 91 taken 3 times.
✗ Branch 90 → 93 not taken.
✓ Branch 92 → 93 taken 3 times.
✗ Branch 92 → 94 not taken.
✓ Branch 95 → 96 taken 3 times.
✗ Branch 95 → 102 not taken.
3 if (!b || t->bytes != b->bytes)
314
1/1
✓ Branch 96 → 102 taken 3 times.
3 applied.takenFromServer.append(t->name);
315 }
316
1/2
✓ Branch 97 → 98 taken 6 times.
✗ Branch 97 → 102 not taken.
6 else if (decision == Decision::MergedRequire)
317
2/2
✓ Branch 98 → 99 taken 6 times.
✓ Branch 99 → 100 taken 6 times.
6 appendBlock(&merged, Sieve::addRequirements(t->bytes, l->extensions), lineEnd);
318 67 continue;
319 67 }
320
3/4
✓ Branch 103 → 104 taken 105 times.
✓ Branch 103 → 108 taken 89 times.
✗ Branch 104 → 105 not taken.
✓ Branch 104 → 106 taken 105 times.
194 const Keyed *chosen = item->choice == Merge::Choice::Server ? t : item->choice == Merge::Choice::Base ? b : l;
321
2/2
✓ Branch 109 → 110 taken 20 times.
✓ Branch 109 → 111 taken 174 times.
194 if (!chosen)
322 20 continue; // that side has no such block
323
1/1
✓ Branch 111 → 112 taken 174 times.
174 appendBlock(&merged, chosen->bytes, lineEnd);
324
8/8
✓ Branch 112 → 113 taken 81 times.
✓ Branch 112 → 117 taken 93 times.
✓ Branch 113 → 114 taken 53 times.
✓ Branch 113 → 116 taken 28 times.
✓ Branch 115 → 116 taken 19 times.
✓ Branch 115 → 117 taken 34 times.
✓ Branch 118 → 119 taken 47 times.
✓ Branch 118 → 120 taken 127 times.
174 if (chosen == t && (!b || t->bytes != b->bytes))
325
1/1
✓ Branch 119 → 120 taken 47 times.
47 applied.takenFromServer.append(t->name);
326 67 }
327
2/3
✓ Branch 124 → 125 taken 67 times.
✗ Branch 126 → 127 not taken.
✓ Branch 126 → 134 taken 67 times.
67 if (Sieve::decode(merged).readOnly) {
328 ✗ applied.error = QStringLiteral("The combined script does not parse");
329 ✗ return applied;
330 }
331 67 applied.ok = true;
332 67 applied.script = merged;
333 67 return applied;
334 67 }
335
336 38 Sieve::MergeResult mergeResult(const Merge::Plan &plan) {
337 38 Sieve::MergeResult result;
338
1/1
✓ Branch 2 → 3 taken 38 times.
38 const QByteArray base = plan.baseText.toUtf8();
339
1/1
✓ Branch 3 → 4 taken 38 times.
38 const QByteArray mine = plan.localText.toUtf8();
340
1/1
✓ Branch 4 → 5 taken 38 times.
38 const QByteArray theirs = plan.serverText.toUtf8();
341
2/2
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 16 taken 36 times.
38 if (!plan.comparable) {
342 4 result.conflicts.append({QStringLiteral("Script"), base, mine, theirs});
343 2 return result;
344 }
345
5/5
✓ Branch 16 → 17 taken 36 times.
✓ Branch 17 → 18 taken 4 times.
✓ Branch 17 → 29 taken 32 times.
✓ Branch 18 → 19 taken 2 times.
✓ Branch 18 → 29 taken 2 times.
36 if (const Merge::Item *order = plan.item(kOrderItem); order && order->state == Merge::State::Conflict) {
346 4 result.conflicts.append({QStringLiteral("Rule order"), base, mine, theirs});
347 2 return result;
348 }
349
1/1
✓ Branch 29 → 30 taken 34 times.
34 Scripts s;
350
1/1
✓ Branch 30 → 31 taken 34 times.
34 parseScripts(&s, base, mine, theirs);
351
2/2
✓ Branch 2 → 3 taken 44 times.
✓ Branch 2 → 4 taken 4 times.
48 const auto bytesOf = [](const Keyed *block) { return block ? block->bytes : QByteArray(); };
352
2/2
✓ Branch 48 → 33 taken 104 times.
✓ Branch 48 → 49 taken 34 times.
138 for (const Merge::Item &item : plan.items)
353
3/4
✓ Branch 34 → 35 taken 16 times.
✓ Branch 34 → 46 taken 88 times.
✓ Branch 35 → 36 taken 16 times.
✗ Branch 35 → 46 not taken.
104 if (item.state == Merge::State::Conflict && item.kind != Merge::Kind::Order)
354 16 result.conflicts.append({item.name, bytesOf(s.baseMap.value(item.id)), bytesOf(s.localMap.value(item.id)),
355 16 bytesOf(s.serverMap.value(item.id))});
356
2/2
✓ Branch 50 → 51 taken 16 times.
✓ Branch 50 → 52 taken 18 times.
34 if (!result.conflicts.isEmpty())
357 16 return result;
358
1/1
✓ Branch 52 → 53 taken 18 times.
18 const Applied applied = apply(plan);
359
1/2
✗ Branch 53 → 54 not taken.
✓ Branch 53 → 64 taken 18 times.
18 if (!applied.ok) {
360 ✗ result.conflicts.append({QStringLiteral("Script"), base, mine, theirs});
361 ✗ return result;
362 }
363 18 result.clean = true;
364 18 result.merged = applied.script;
365 18 result.takenFromTheirs = applied.takenFromServer;
366 18 return result;
367 58 }
368
369 } // namespace SieveMergePlan
370