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.2% 104 / 0 / 107
Functions: 100.0% 6 / 0 / 6
Branches: 82.3% 121 / 0 / 147

data/MailSelectionSnapshot.cpp
Line Branch Exec Source
1 #include "MailSelectionSnapshot.h"
2 #include <QFile>
3 #include <QDir>
4 #include <QtEndian>
5 #include <limits>
6
7 namespace {
8 248 bool open(QFile &file, const std::shared_ptr<const MailSelectionSnapshot> &snapshot,
9 MailSelectionResult &result) {
10
7/8
✓ Branch 3 → 4 taken 245 times.
✓ Branch 3 → 12 taken 3 times.
✓ Branch 6 → 7 taken 245 times.
✗ Branch 6 → 12 not taken.
✓ Branch 8 → 9 taken 244 times.
✓ Branch 8 → 12 taken 1 time.
✓ Branch 14 → 15 taken 5 times.
✓ Branch 14 → 22 taken 243 times.
492 if (!snapshot || !snapshot->storage || snapshot->count < 0 ||
11
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 243 times.
244 snapshot->count > std::numeric_limits<qint64>::max() / 8) {
12 5 result.error = QStringLiteral("Invalid selection snapshot");
13 5 return false;
14 }
15
2/2
✓ Branch 23 → 24 taken 243 times.
✓ Branch 24 → 25 taken 243 times.
243 file.setFileName(snapshot->path());
16
7/8
✓ Branch 27 → 28 taken 243 times.
✓ Branch 28 → 29 taken 243 times.
✗ Branch 28 → 32 not taken.
✓ Branch 29 → 30 taken 243 times.
✓ Branch 31 → 32 taken 4 times.
✓ Branch 31 → 33 taken 239 times.
✓ Branch 34 → 35 taken 4 times.
✓ Branch 34 → 42 taken 239 times.
243 if (!file.open(QIODevice::ReadOnly) || file.size() != snapshot->count * 8) {
17 4 result.error = QStringLiteral("Selection snapshot is unavailable or truncated");
18 4 return false;
19 }
20 239 result.snapshot = snapshot;
21 239 return true;
22 }
23 11686 bool readUid(QFile &file, qint64 index, qint64 &uid) {
24 uchar bytes[8];
25
5/8
✓ Branch 2 → 3 taken 11686 times.
✓ Branch 3 → 4 taken 11686 times.
✗ Branch 3 → 6 not taken.
✓ Branch 4 → 5 taken 11686 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 11686 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 11686 times.
11686 if (!file.seek(index * 8) || file.read(reinterpret_cast<char *>(bytes), 8) != 8) return false;
26
1/1
✓ Branch 10 → 11 taken 11686 times.
11686 uid = qFromLittleEndian<qint64>(bytes);
27 11686 return uid >= 0;
28 }
29 }
30
31 154 MailSelectionResult MailSelectionReader::page(std::shared_ptr<const MailSelectionSnapshot> snapshot,
32 qint64 offset, int limit) {
33 154 MailSelectionResult result;
34
6/6
✓ Branch 2 → 3 taken 152 times.
✓ Branch 2 → 5 taken 2 times.
✓ Branch 3 → 4 taken 151 times.
✓ Branch 3 → 5 taken 1 time.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 12 taken 149 times.
154 if (offset < 0 || limit < 1 || limit > MaxBatch) {
35 5 result.error = QStringLiteral("Invalid selection page bounds");
36 5 return result;
37 }
38
1/1
✓ Branch 12 → 13 taken 149 times.
149 QFile file;
39
3/3
✓ Branch 13 → 14 taken 149 times.
✓ Branch 14 → 15 taken 5 times.
✓ Branch 14 → 16 taken 144 times.
149 if (!open(file, snapshot, result)) return result;
40
2/2
✓ Branch 17 → 18 taken 142 times.
✓ Branch 17 → 21 taken 2 times.
144 const auto end = offset >= snapshot->count ? offset : offset + qMin<qint64>(limit, snapshot->count - offset);
41
2/2
✓ Branch 35 → 23 taken 1726 times.
✓ Branch 35 → 36 taken 143 times.
1869 for (qint64 row = offset; row < end; ++row) {
42 1726 qint64 uid = -1;
43
3/3
✓ Branch 23 → 24 taken 1726 times.
✓ Branch 24 → 25 taken 1 time.
✓ Branch 24 → 33 taken 1725 times.
1726 if (!readUid(file, row, uid)) {
44
1/1
✓ Branch 25 → 26 taken 1 time.
1 result.uids.clear();
45 1 result.error = QStringLiteral("Could not read selection identity");
46 1 return result;
47 }
48
1/1
✓ Branch 33 → 34 taken 1725 times.
1725 result.uids.append(uid);
49 }
50 143 result.outcome = MailSelectionResult::Outcome::Ready;
51 143 return result;
52 149 }
53
54 76 MailSelectionResult MailSelectionReader::lookup(std::shared_ptr<const MailSelectionSnapshot> snapshot,
55 const QList<qint64> &uids) {
56 76 MailSelectionResult result;
57
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 11 taken 74 times.
76 if (uids.size() > MaxBatch) {
58 2 result.error = QStringLiteral("Selection membership batch exceeds limit");
59 2 return result;
60 }
61
1/1
✓ Branch 11 → 12 taken 74 times.
74 QFile file;
62
3/3
✓ Branch 12 → 13 taken 74 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 72 times.
74 if (!open(file, snapshot, result)) return result;
63
2/2
✓ Branch 41 → 17 taken 2267 times.
✓ Branch 41 → 42 taken 71 times.
2338 for (const auto uid : uids) {
64 2267 qint64 low = 0, high = snapshot->count;
65 2267 bool found = false;
66
2/2
✓ Branch 37 → 20 taken 9960 times.
✓ Branch 37 → 38 taken 1308 times.
11268 while (low < high) {
67 9960 const qint64 middle = low + (high - low) / 2;
68 9960 qint64 candidate = -1;
69
3/3
✓ Branch 20 → 21 taken 9960 times.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 21 → 30 taken 9959 times.
9960 if (!readUid(file, middle, candidate)) {
70 1 result.membership.clear();
71 1 result.error = QStringLiteral("Could not read selection membership");
72 1 return result;
73 }
74
2/2
✓ Branch 30 → 31 taken 958 times.
✓ Branch 30 → 32 taken 9001 times.
9959 if (candidate == uid) { found = true; break; }
75
2/2
✓ Branch 32 → 33 taken 5687 times.
✓ Branch 32 → 34 taken 3314 times.
9001 if (candidate < uid) low = middle + 1;
76 3314 else high = middle;
77 }
78
1/1
✓ Branch 38 → 39 taken 2266 times.
2266 result.membership.insert(uid, found);
79 }
80 71 result.outcome = MailSelectionResult::Outcome::Ready;
81 71 return result;
82 74 }
83
84 29 MailSelectionResult MailSelectionReader::toggle(std::shared_ptr<const MailSelectionSnapshot> snapshot,
85 qint64 uid, const std::atomic_bool &cancelled) {
86 29 MailSelectionResult result;
87
2/2
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 10 taken 27 times.
31 if (uid < 0) { result.error = QStringLiteral("Invalid selection identity"); return result; }
88
2/2
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 13 taken 25 times.
27 if (cancelled.load()) { result.outcome = MailSelectionResult::Outcome::Cancelled; return result; }
89
1/1
✓ Branch 13 → 14 taken 25 times.
25 QFile input;
90
3/3
✓ Branch 14 → 15 taken 25 times.
✓ Branch 15 → 16 taken 2 times.
✓ Branch 15 → 17 taken 23 times.
25 if (!open(input, snapshot, result)) return result;
91 23 result.snapshot.reset();
92
1/1
✓ Branch 19 → 20 taken 23 times.
23 auto outputSnapshot = std::make_shared<MailSelectionSnapshot>(*snapshot);
93 23 outputSnapshot->count = 0;
94
3/3
✓ Branch 21 → 22 taken 23 times.
✓ Branch 22 → 23 taken 23 times.
✓ Branch 23 → 24 taken 23 times.
23 outputSnapshot->storage = std::make_shared<QTemporaryDir>(QDir::tempPath() + "/mailjd-selection-XXXXXX");
95
2/4
✓ Branch 31 → 32 taken 23 times.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 39 taken 23 times.
✗ Branch 35 → 36 not taken.
23 if (!outputSnapshot->storage->isValid()) { result.error = outputSnapshot->storage->errorString(); return result; }
96
2/2
✓ Branch 40 → 41 taken 23 times.
✓ Branch 41 → 42 taken 23 times.
23 QFile output(outputSnapshot->path());
97
5/8
✓ Branch 44 → 45 taken 23 times.
✓ Branch 45 → 46 taken 23 times.
✗ Branch 45 → 49 not taken.
✓ Branch 47 → 48 taken 23 times.
✗ Branch 48 → 49 not taken.
✓ Branch 48 → 50 taken 23 times.
✗ Branch 51 → 52 not taken.
✓ Branch 51 → 56 taken 23 times.
23 if (!output.open(QIODevice::WriteOnly) || !output.setPermissions(QFile::ReadOwner | QFile::WriteOwner)) {
98 ✗ result.error = output.errorString(); return result;
99 }
100 23 QByteArray buffer;
101
1/1
✓ Branch 56 → 57 taken 23 times.
23 buffer.reserve(64 * 1024);
102 36355 auto append = [&](qint64 value) {
103 36355 const auto encoded = qToLittleEndian(value);
104
1/1
✓ Branch 3 → 4 taken 36355 times.
36355 buffer.append(reinterpret_cast<const char *>(&encoded), 8);
105 36355 ++outputSnapshot->count;
106
2/2
✓ Branch 6 → 7 taken 36351 times.
✓ Branch 6 → 8 taken 4 times.
36355 if (buffer.size() < 64 * 1024) return true;
107
2/3
✓ Branch 8 → 9 taken 4 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 4 times.
4 if (output.write(buffer) != buffer.size()) return false;
108
1/1
✓ Branch 12 → 13 taken 4 times.
4 buffer.clear();
109 4 return true;
110 23 };
111 23 bool handled = false;
112 23 qint64 read = 0, previous = -1;
113
2/2
✓ Branch 116 → 58 taken 26 times.
✓ Branch 116 → 117 taken 22 times.
48 while (read < snapshot->count) {
114
1/2
✗ Branch 59 → 60 not taken.
✓ Branch 59 → 61 taken 26 times.
27 if (cancelled.load()) { result.outcome = MailSelectionResult::Outcome::Cancelled; return result; }
115
1/1
✓ Branch 63 → 64 taken 26 times.
26 const auto bytes = input.read(qMin<qint64>(8192, snapshot->count - read) * 8);
116
3/6
✓ Branch 65 → 66 taken 26 times.
✗ Branch 65 → 68 not taken.
✗ Branch 67 → 68 not taken.
✓ Branch 67 → 69 taken 26 times.
✗ Branch 70 → 71 not taken.
✓ Branch 70 → 78 taken 26 times.
26 if (bytes.isEmpty() || bytes.size() % 8 != 0) {
117 ✗ result.error = QStringLiteral("Could not read selection identity"); return result;
118 }
119
2/2
✓ Branch 108 → 79 taken 36345 times.
✓ Branch 108 → 109 taken 25 times.
36370 for (qsizetype offset = 0; offset < bytes.size(); offset += 8) {
120
1/1
✓ Branch 80 → 81 taken 36345 times.
36345 const auto value = qFromLittleEndian<qint64>(bytes.constData() + offset);
121
2/2
✓ Branch 81 → 82 taken 1 time.
✓ Branch 81 → 89 taken 36344 times.
36346 if (value <= previous) { result.error = QStringLiteral("Invalid selection ordering"); return result; }
122 36344 previous = value;
123 36344 ++read;
124
2/2
✓ Branch 89 → 90 taken 6 times.
✓ Branch 89 → 91 taken 36338 times.
36344 if (value == uid) { handled = true; continue; }
125
4/4
✓ Branch 91 → 92 taken 25518 times.
✓ Branch 91 → 100 taken 10820 times.
✓ Branch 92 → 93 taken 12 times.
✓ Branch 92 → 100 taken 25506 times.
36338 if (!handled && value > uid) {
126
2/4
✓ Branch 93 → 94 taken 12 times.
✗ Branch 94 → 95 not taken.
✓ Branch 94 → 99 taken 12 times.
✗ Branch 95 → 96 not taken.
12 if (!append(uid)) { result.error = output.errorString(); return result; }
127 12 handled = true;
128 }
129
2/4
✓ Branch 100 → 101 taken 36338 times.
✗ Branch 101 → 102 not taken.
✓ Branch 101 → 106 taken 36338 times.
✗ Branch 102 → 103 not taken.
36338 if (!append(value)) { result.error = output.errorString(); return result; }
130 }
131 26 }
132
9/13
✓ Branch 117 → 118 taken 5 times.
✓ Branch 117 → 120 taken 17 times.
✓ Branch 118 → 119 taken 5 times.
✓ Branch 119 → 120 taken 5 times.
✗ Branch 119 → 125 not taken.
✓ Branch 120 → 121 taken 22 times.
✓ Branch 122 → 123 taken 22 times.
✗ Branch 122 → 125 not taken.
✓ Branch 123 → 124 taken 22 times.
✗ Branch 124 → 125 not taken.
✓ Branch 124 → 126 taken 22 times.
✗ Branch 127 → 128 not taken.
✓ Branch 127 → 132 taken 22 times.
22 if ((!handled && !append(uid)) || output.write(buffer) != buffer.size() || !output.flush()) {
133 ✗ result.error = output.errorString(); return result;
134 }
135
1/2
✗ Branch 133 → 134 not taken.
✓ Branch 133 → 135 taken 22 times.
22 if (cancelled.load()) { result.outcome = MailSelectionResult::Outcome::Cancelled; return result; }
136 22 result.snapshot = std::move(outputSnapshot);
137 22 result.outcome = MailSelectionResult::Outcome::Ready;
138 22 return result;
139 25 }
140