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.7% 130 / 0 / 133
Functions: 100.0% 14 / 0 / 14
Branches: 88.2% 142 / 0 / 161

data/TextMerge3.cpp
Line Branch Exec Source
1 #include "TextMerge3.h"
2
3 #include "LineDiff.h"
4
5 #include <QRegularExpression>
6
7 #include <algorithm>
8 #include <vector>
9
10 namespace TextMerge {
11
12 namespace {
13
14 constexpr int kWordLimit = 400;
15
16 1117 HunkKind classify(const QStringList &base, const QStringList &local, const QStringList &server) {
17
4/6
✓ Branch 3 → 4 taken 461 times.
✓ Branch 3 → 7 taken 656 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 461 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 1117 times.
1117 if (local == base && server == base)
18 ✗ return HunkKind::Same;
19
2/2
✓ Branch 11 → 12 taken 461 times.
✓ Branch 11 → 13 taken 656 times.
1117 if (local == base)
20 461 return HunkKind::ServerOnly;
21
2/2
✓ Branch 14 → 15 taken 427 times.
✓ Branch 14 → 16 taken 229 times.
656 if (server == base)
22 427 return HunkKind::LocalOnly;
23
2/2
✓ Branch 17 → 18 taken 15 times.
✓ Branch 17 → 19 taken 214 times.
229 if (local == server)
24 15 return HunkKind::BothSame;
25 214 return HunkKind::Conflict;
26 }
27
28 76 QStringList words(const QString &line) {
29 6 static const QRegularExpression token(QStringLiteral("\\w+|\\s+|[^\\w\\s]"),
30
4/7
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 15 taken 73 times.
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 15 not taken.
✓ Branch 9 → 10 taken 3 times.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 38 not taken.
82 QRegularExpression::UseUnicodePropertiesOption);
31 76 QStringList result;
32
1/1
✓ Branch 16 → 17 taken 76 times.
76 auto it = token.globalMatch(line);
33
3/3
✓ Branch 24 → 25 taken 395 times.
✓ Branch 25 → 18 taken 319 times.
✓ Branch 25 → 26 taken 76 times.
395 while (it.hasNext())
34
3/3
✓ Branch 18 → 19 taken 319 times.
✓ Branch 19 → 20 taken 319 times.
✓ Branch 20 → 21 taken 319 times.
319 result.append(it.next().captured());
35 76 return result;
36 76 }
37
38 } // namespace
39
40 10 int Result::conflicts() const {
41 10 return int(std::count_if(hunks.cbegin(), hunks.cend(),
42 39 [](const Hunk &hunk) { return hunk.kind == HunkKind::Conflict; }));
43 }
44
45 1 int Result::changes() const {
46 1 return int(std::count_if(hunks.cbegin(), hunks.cend(),
47 5 [](const Hunk &hunk) { return hunk.kind != HunkKind::Same; }));
48 }
49
50
1/1
✓ Branch 5 → 6 taken 4735 times.
4735 QStringList splitText(const QString &text) { return text.split(QLatin1Char('\n')); }
51
52
1/1
✓ Branch 4 → 5 taken 2142 times.
2142 QString joinText(const QStringList &lines) { return lines.join(QLatin1Char('\n')); }
53
54 745 Result diff3(const QString &baseText, const QString &localText, const QString &serverText) {
55
1/1
✓ Branch 2 → 3 taken 745 times.
745 const QStringList base = splitText(baseText);
56
1/1
✓ Branch 3 → 4 taken 745 times.
745 const QStringList local = splitText(localText);
57
1/1
✓ Branch 4 → 5 taken 745 times.
745 const QStringList server = splitText(serverText);
58
1/1
✓ Branch 5 → 6 taken 745 times.
745 const QList<int> toLocal = LineDiff::match(base, local);
59
1/1
✓ Branch 6 → 7 taken 745 times.
745 const QList<int> toServer = LineDiff::match(base, server);
60 745 Result result;
61 745 int i = 0, j = 0, k = 0;
62 745 const int nb = int(base.size()), nl = int(local.size()), ns = int(server.size());
63
6/6
✓ Branch 79 → 11 taken 2216 times.
✓ Branch 79 → 80 taken 826 times.
✓ Branch 80 → 11 taken 28 times.
✓ Branch 80 → 81 taken 798 times.
✓ Branch 81 → 11 taken 53 times.
✓ Branch 81 → 82 taken 745 times.
3042 while (i < nb || j < nl || k < ns) {
64
8/8
✓ Branch 11 → 12 taken 2216 times.
✓ Branch 11 → 17 taken 81 times.
✓ Branch 13 → 14 taken 1627 times.
✓ Branch 13 → 17 taken 589 times.
✓ Branch 15 → 16 taken 1180 times.
✓ Branch 15 → 17 taken 447 times.
✓ Branch 18 → 19 taken 1180 times.
✓ Branch 18 → 39 taken 1117 times.
2297 if (i < nb && toLocal.at(i) == j && toServer.at(i) == k) {
65 1180 Hunk same;
66
8/8
✓ Branch 27 → 28 taken 23775 times.
✓ Branch 27 → 33 taken 466 times.
✓ Branch 29 → 30 taken 23387 times.
✓ Branch 29 → 33 taken 388 times.
✓ Branch 31 → 32 taken 23061 times.
✓ Branch 31 → 33 taken 326 times.
✓ Branch 34 → 20 taken 23061 times.
✓ Branch 34 → 35 taken 1180 times.
24241 while (i < nb && toLocal.at(i) == j && toServer.at(i) == k) {
67
1/1
✓ Branch 21 → 22 taken 23061 times.
23061 same.base.append(base.at(i));
68
1/1
✓ Branch 23 → 24 taken 23061 times.
23061 same.local.append(local.at(j));
69
1/1
✓ Branch 25 → 26 taken 23061 times.
23061 same.server.append(server.at(k));
70 23061 ++i, ++j, ++k;
71 }
72
1/1
✓ Branch 35 → 36 taken 1180 times.
1180 result.hunks.append(same);
73 1180 continue;
74 1180 }
75 // The next base line matched on both sides ends the changed run.
76 1117 int sync = i;
77
8/8
✓ Branch 41 → 42 taken 4267 times.
✓ Branch 41 → 47 taken 360 times.
✓ Branch 43 → 44 taken 1190 times.
✓ Branch 43 → 46 taken 3077 times.
✓ Branch 45 → 46 taken 433 times.
✓ Branch 45 → 47 taken 757 times.
✓ Branch 48 → 40 taken 3510 times.
✓ Branch 48 → 49 taken 1117 times.
4627 while (sync < nb && (toLocal.at(sync) < 0 || toServer.at(sync) < 0))
78 3510 ++sync;
79
2/2
✓ Branch 49 → 50 taken 757 times.
✓ Branch 49 → 52 taken 360 times.
1117 const int localEnd = sync < nb ? toLocal.at(sync) : nl;
80
2/2
✓ Branch 53 → 54 taken 757 times.
✓ Branch 53 → 56 taken 360 times.
1117 const int serverEnd = sync < nb ? toServer.at(sync) : ns;
81 1117 Hunk changed;
82
1/1
✓ Branch 57 → 58 taken 1117 times.
1117 changed.base = base.mid(i, sync - i);
83
1/1
✓ Branch 60 → 61 taken 1117 times.
1117 changed.local = local.mid(j, localEnd - j);
84
1/1
✓ Branch 63 → 64 taken 1117 times.
1117 changed.server = server.mid(k, serverEnd - k);
85
1/1
✓ Branch 66 → 67 taken 1117 times.
1117 changed.kind = classify(changed.base, changed.local, changed.server);
86
6/8
✓ Branch 68 → 69 taken 355 times.
✓ Branch 68 → 73 taken 762 times.
✓ Branch 70 → 71 taken 204 times.
✓ Branch 70 → 73 taken 151 times.
✓ Branch 72 → 73 taken 204 times.
✗ Branch 72 → 74 not taken.
✓ Branch 75 → 76 taken 1117 times.
✗ Branch 75 → 77 not taken.
1117 if (!changed.base.isEmpty() || !changed.local.isEmpty() || !changed.server.isEmpty())
87
1/1
✓ Branch 76 → 77 taken 1117 times.
1117 result.hunks.append(changed);
88 1117 i = sync;
89 1117 j = localEnd;
90 1117 k = serverEnd;
91 1117 }
92 745 return result;
93 745 }
94
95 806 QList<Pick> defaultPicks(const Result &result) {
96 806 QList<Pick> picks;
97
2/2
✓ Branch 14 → 4 taken 2484 times.
✓ Branch 14 → 15 taken 806 times.
3290 for (const Hunk &hunk : result.hunks) {
98
3/3
✓ Branch 5 → 6 taken 514 times.
✓ Branch 5 → 8 taken 215 times.
✓ Branch 5 → 10 taken 1755 times.
2484 switch (hunk.kind) {
99 514 case HunkKind::ServerOnly:
100
1/1
✓ Branch 6 → 7 taken 514 times.
514 picks.append(Pick::Server);
101 514 break;
102 215 case HunkKind::Conflict:
103
1/1
✓ Branch 8 → 9 taken 215 times.
215 picks.append(Pick::Open);
104 215 break;
105 1755 default:
106
1/1
✓ Branch 10 → 11 taken 1755 times.
1755 picks.append(Pick::Local);
107 1755 break;
108 }
109 }
110 806 return picks;
111 ✗ }
112
113 7236 QStringList hunkLines(const Hunk &hunk, Pick pick) {
114
4/5
✓ Branch 2 → 3 taken 1269 times.
✓ Branch 2 → 4 taken 2 times.
✓ Branch 2 → 5 taken 809 times.
✓ Branch 2 → 6 taken 5156 times.
✗ Branch 2 → 7 not taken.
7236 switch (pick) {
115 1269 case Pick::Server:
116 1269 return hunk.server;
117 2 case Pick::Both:
118 2 return hunk.local + hunk.server;
119 809 case Pick::Base:
120 809 return hunk.base;
121 5156 case Pick::Local:
122 case Pick::Open:
123 5156 break;
124 }
125 5156 return hunk.local;
126 }
127
128 2142 QString apply(const Result &result, const QList<Pick> &picks, bool *open) {
129
2/2
✓ Branch 2 → 3 taken 96 times.
✓ Branch 2 → 4 taken 2046 times.
2142 if (open)
130 96 *open = false;
131 2142 QStringList lines;
132
2/2
✓ Branch 23 → 5 taken 7236 times.
✓ Branch 23 → 24 taken 2142 times.
9378 for (int index = 0; index < result.hunks.size(); ++index) {
133 7236 const Hunk &hunk = result.hunks.at(index);
134
1/2
✓ Branch 7 → 8 taken 7236 times.
✗ Branch 7 → 10 not taken.
7236 const Pick pick = index < picks.size() ? picks.at(index) : Pick::Open;
135
5/6
✓ Branch 11 → 12 taken 162 times.
✓ Branch 11 → 15 taken 7074 times.
✓ Branch 12 → 13 taken 162 times.
✗ Branch 12 → 15 not taken.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 15 taken 161 times.
7236 if (pick == Pick::Open && hunk.kind == HunkKind::Conflict && open)
136 1 *open = true;
137
4/4
✓ Branch 15 → 16 taken 3505 times.
✓ Branch 15 → 17 taken 3731 times.
✓ Branch 18 → 19 taken 7236 times.
✓ Branch 19 → 20 taken 7236 times.
7236 lines += hunkLines(hunk, hunk.kind == HunkKind::Same ? Pick::Local : pick);
138 }
139
1/1
✓ Branch 24 → 25 taken 2142 times.
4284 return joinText(lines);
140 2142 }
141
142 38 QList<Segment> wordSegments(const QString &from, const QString &to) {
143
1/1
✓ Branch 2 → 3 taken 38 times.
38 const QStringList a = words(from);
144
1/1
✓ Branch 3 → 4 taken 38 times.
38 const QStringList b = words(to);
145
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 37 times.
38 if (to.isEmpty())
146 1 return {};
147
3/6
✓ Branch 8 → 9 taken 37 times.
✗ Branch 8 → 11 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 37 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 21 taken 37 times.
37 if (a.size() > kWordLimit || b.size() > kWordLimit)
148 ✗ return {{to, from != to}};
149 37 const int n = int(a.size()), m = int(b.size());
150
2/2
✓ Branch 27 → 28 taken 37 times.
✓ Branch 28 → 29 taken 37 times.
37 std::vector<std::vector<int>> table(size_t(n) + 1, std::vector<int>(size_t(m) + 1, 0));
151
2/2
✓ Branch 52 → 33 taken 157 times.
✓ Branch 52 → 53 taken 37 times.
194 for (int i = n - 1; i >= 0; --i)
152
2/2
✓ Branch 50 → 34 taken 769 times.
✓ Branch 50 → 51 taken 157 times.
926 for (int j = m - 1; j >= 0; --j)
153
2/2
✓ Branch 37 → 38 taken 175 times.
✓ Branch 37 → 41 taken 594 times.
1363 table[size_t(i)][size_t(j)] = a.at(i) == b.at(j) ? table[size_t(i) + 1][size_t(j) + 1] + 1
154 594 : std::max(table[size_t(i) + 1][size_t(j)], table[size_t(i)][size_t(j) + 1]);
155 37 QList<Segment> segments;
156 161 const auto add = [&segments](const QString &text, bool changed) {
157
6/6
✓ Branch 3 → 4 taken 124 times.
✓ Branch 3 → 7 taken 37 times.
✓ Branch 5 → 6 taken 63 times.
✓ Branch 5 → 7 taken 61 times.
✓ Branch 8 → 9 taken 63 times.
✓ Branch 8 → 11 taken 98 times.
161 if (!segments.isEmpty() && segments.last().changed == changed)
158 63 segments.last().text += text;
159 else
160 98 segments.append({text, changed});
161
1/3
✓ Branch 12 → 13 taken 98 times.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 20 not taken.
259 };
162 37 int i = 0, j = 0;
163
2/2
✓ Branch 78 → 54 taken 203 times.
✓ Branch 78 → 79 taken 37 times.
240 while (j < m) {
164
6/6
✓ Branch 54 → 55 taken 183 times.
✓ Branch 54 → 60 taken 20 times.
✓ Branch 58 → 59 taken 115 times.
✓ Branch 58 → 60 taken 68 times.
✓ Branch 61 → 62 taken 115 times.
✓ Branch 61 → 65 taken 88 times.
203 if (i < n && a.at(i) == b.at(j)) {
165
1/1
✓ Branch 63 → 64 taken 115 times.
115 add(b.at(j), false);
166 115 ++i, ++j;
167
6/6
✓ Branch 65 → 66 taken 68 times.
✓ Branch 65 → 72 taken 20 times.
✓ Branch 70 → 71 taken 42 times.
✓ Branch 70 → 72 taken 26 times.
✓ Branch 73 → 74 taken 42 times.
✓ Branch 73 → 75 taken 46 times.
88 } else if (i < n && table[size_t(i) + 1][size_t(j)] >= table[size_t(i)][size_t(j) + 1]) {
168 42 ++i;
169 } else {
170
1/1
✓ Branch 76 → 77 taken 46 times.
46 add(b.at(j), true);
171 46 ++j;
172 }
173 }
174 37 return segments;
175 38 }
176
177 } // namespace TextMerge
178