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: 95.2% 99 / 0 / 104
Functions: 100.0% 21 / 0 / 21
Branches: 73.0% 111 / 0 / 152

service/MailSelectionService.cpp
Line Branch Exec Source
1 #include "MailSelectionService.h"
2 #include "data/MailCache.h"
3 #include <QCoreApplication>
4 #include <QFutureWatcher>
5 #include <QtConcurrent>
6
7
1/1
✓ Branch 3 → 4 taken 30 times.
30 MailSelectionService::MailSelectionService(QObject *parent) : QObject(parent) {
8
1/1
✓ Branch 6 → 7 taken 30 times.
30 m_pool.setMaxThreadCount(1);
9
1/1
✓ Branch 7 → 8 taken 30 times.
30 m_pool.setExpiryTimeout(1000);
10
1/1
✓ Branch 9 → 10 taken 30 times.
31 connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, [this] { stop(); });
11 30 }
12 53 MailSelectionService::~MailSelectionService() { stop(); }
13 namespace {
14 QPointer<MailSelectionService> sharedMailSelectionService;
15 // S106-120: runs first in ~QCoreApplication, while Qt's SQL and plugin state
16 // still exists — aboutToQuit misses a process that ends without the event
17 // loop, and the application-child deletion came only after that state was
18 // gone, with a worker possibly inside QSqlDatabase::open (the crash fixed
19 // for MailSelectionMutationService).
20 } // namespace
21
22 22 void MailSelectionService::stopSharedBeforeTeardown() {
23
1/2
✓ Branch 3 → 4 taken 22 times.
✗ Branch 3 → 6 not taken.
22 if (sharedMailSelectionService) sharedMailSelectionService->stop();
24 22 }
25
26 9363 MailSelectionService *MailSelectionService::shared() {
27
2/2
✓ Branch 3 → 4 taken 22 times.
✓ Branch 3 → 11 taken 9341 times.
9363 if (!sharedMailSelectionService) {
28
3/6
✓ Branch 6 → 7 taken 22 times.
✓ Branch 7 → 8 taken 22 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 22 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
22 sharedMailSelectionService = new MailSelectionService(QCoreApplication::instance());
29 22 qAddPostRoutine(stopSharedBeforeTeardown);
30 }
31 9363 return sharedMailSelectionService;
32 }
33 53 void MailSelectionService::stop() {
34 53 m_stopping = true;
35
3/5
✓ Branch 2 → 3 taken 53 times.
✓ Branch 3 → 4 taken 53 times.
✗ Branch 6 → 7 not taken.
✗ Branch 9 → 5 not taken.
✓ Branch 9 → 10 taken 53 times.
53 for (const auto &request : m_pending) disconnect(request.ownerDestroyed);
36 53 m_pending.clear();
37
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 18 taken 53 times.
53 if (m_active) {
38 ✗ disconnect(m_active->ownerDestroyed);
39 ✗ m_active->cancelled->store(true);
40 }
41
1/1
✓ Branch 19 → 20 taken 53 times.
53 m_pool.waitForDone();
42 53 }
43 9504 void MailSelectionService::cancel(QObject *owner) {
44
6/6
✓ Branch 3 → 4 taken 214 times.
✓ Branch 3 → 8 taken 9290 times.
✓ Branch 6 → 7 taken 20 times.
✓ Branch 6 → 8 taken 194 times.
✓ Branch 9 → 10 taken 20 times.
✓ Branch 9 → 13 taken 9484 times.
9504 if (m_active && m_active->owner == owner) m_active->cancelled->store(true);
45 9504 m_pending.removeIf([owner](const Request &request) {
46
5/6
✓ Branch 3 → 4 taken 232 times.
✗ Branch 3 → 7 not taken.
✓ Branch 5 → 6 taken 129 times.
✓ Branch 5 → 7 taken 103 times.
✓ Branch 8 → 9 taken 129 times.
✓ Branch 8 → 10 taken 103 times.
232 if (request.owner && request.owner != owner) return false;
47 103 disconnect(request.ownerDestroyed);
48 103 return true;
49 });
50 9504 }
51 174 void MailSelectionService::capture(QObject *owner, const QString &databasePath,
52 MailSelectionQuery selection, Callback callback) {
53
7/8
✓ Branch 3 → 4 taken 173 times.
✓ Branch 3 → 8 taken 1 time.
✓ Branch 5 → 6 taken 173 times.
✗ Branch 5 → 8 not taken.
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 172 times.
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 20 taken 172 times.
174 if (databasePath.size() > 4096 || selection.accountId.size() > 4096 || selection.text.size() > 1024) {
54 2 cancel(owner);
55
2/3
✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 19 not taken.
✓ Branch 15 → 16 taken 2 times.
2 if (owner) { MailSelectionResult result; result.outcome = MailSelectionResult::Outcome::Limited; callback(std::move(result)); }
56 2 return;
57 }
58
2/4
✓ Branch 25 → 26 taken 172 times.
✓ Branch 26 → 27 taken 172 times.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 44 not taken.
172 submit(owner, [databasePath, selection = std::move(selection)](const std::atomic_bool &cancelled) {
59 54 MailSelectionResult result;
60
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 12 taken 54 times.
54 if (databasePath.size() > 4096) { result.error = QStringLiteral("Selection database path exceeds limit"); return result; }
61
1/1
✓ Branch 12 → 13 taken 54 times.
54 MailCache reader;
62
4/4
✓ Branch 13 → 14 taken 54 times.
✓ Branch 14 → 15 taken 2 times.
✓ Branch 14 → 20 taken 52 times.
✓ Branch 15 → 16 taken 2 times.
54 if (!reader.openReadOnly(databasePath)) { result.error = reader.lastError(); return result; }
63
1/1
✓ Branch 21 → 22 taken 52 times.
52 return reader.snapshots().selectionSnapshot(selection, cancelled);
64 226 }, std::move(callback));
65 }
66 37 void MailSelectionService::page(QObject *owner, std::shared_ptr<const MailSelectionSnapshot> snapshot,
67 qint64 offset, int limit, Callback callback) {
68
2/4
✓ Branch 6 → 7 taken 37 times.
✓ Branch 7 → 8 taken 37 times.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
37 submit(owner, [snapshot = std::move(snapshot), offset, limit](const std::atomic_bool &) {
69
1/1
✓ Branch 3 → 4 taken 36 times.
72 return MailSelectionReader::page(snapshot, offset, limit);
70 37 }, std::move(callback));
71 37 }
72 65 void MailSelectionService::lookup(QObject *owner, std::shared_ptr<const MailSelectionSnapshot> snapshot,
73 QList<qint64> uids, Callback callback) {
74
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 13 taken 64 times.
65 if (uids.size() > MailSelectionReader::MaxBatch) {
75 1 cancel(owner);
76
2/3
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 12 not taken.
✓ Branch 8 → 9 taken 1 time.
1 if (owner) { MailSelectionResult result; result.outcome = MailSelectionResult::Outcome::Limited; callback(std::move(result)); }
77 1 return;
78 }
79
2/4
✓ Branch 19 → 20 taken 64 times.
✓ Branch 20 → 21 taken 64 times.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 38 not taken.
64 submit(owner, [snapshot = std::move(snapshot), uids = std::move(uids)](const std::atomic_bool &) {
80
1/1
✓ Branch 3 → 4 taken 60 times.
60 return MailSelectionReader::lookup(snapshot, uids);
81 64 }, std::move(callback));
82 }
83 14 void MailSelectionService::toggle(QObject *owner, std::shared_ptr<const MailSelectionSnapshot> snapshot,
84 qint64 uid, Callback callback) {
85
2/4
✓ Branch 6 → 7 taken 14 times.
✓ Branch 7 → 8 taken 14 times.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
14 submit(owner, [snapshot = std::move(snapshot), uid](const std::atomic_bool &cancelled) {
86
1/1
✓ Branch 3 → 4 taken 13 times.
26 return MailSelectionReader::toggle(snapshot, uid, cancelled);
87 14 }, std::move(callback));
88 14 }
89 287 void MailSelectionService::submit(QObject *owner, Work work, Callback callback) {
90
1/1
✓ Branch 2 → 3 taken 287 times.
287 cancel(owner);
91
2/2
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 5 taken 284 times.
299 if (!owner) return;
92
5/6
✓ Branch 5 → 6 taken 284 times.
✗ Branch 5 → 8 not taken.
✓ Branch 7 → 8 taken 12 times.
✓ Branch 7 → 9 taken 272 times.
✓ Branch 10 → 11 taken 12 times.
✓ Branch 10 → 17 taken 272 times.
284 if (m_stopping || m_pending.size() >= MaxPending) {
93 12 MailSelectionResult result;
94 12 result.outcome = MailSelectionResult::Outcome::Busy;
95
1/1
✓ Branch 13 → 14 taken 12 times.
12 callback(std::move(result));
96 12 return;
97 12 }
98
3/11
✓ Branch 17 → 18 taken 272 times.
✓ Branch 22 → 23 taken 272 times.
✓ Branch 23 → 24 taken 272 times.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 44 not taken.
✗ Branch 45 → 46 not taken.
✗ Branch 45 → 47 not taken.
✗ Branch 48 → 49 not taken.
✗ Branch 48 → 50 not taken.
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 53 not taken.
272 Request request{owner, std::move(work), std::move(callback)};
99
1/1
✓ Branch 25 → 26 taken 272 times.
544 request.ownerDestroyed = connect(owner, &QObject::destroyed, this,
100 818 [cancelled = request.cancelled] { cancelled->store(true); });
101
1/1
✓ Branch 30 → 31 taken 272 times.
272 m_pending.append(std::move(request));
102
1/1
✓ Branch 31 → 32 taken 272 times.
272 startNext();
103 272 }
104 440 void MailSelectionService::startNext() {
105
5/6
✓ Branch 2 → 3 taken 440 times.
✗ Branch 2 → 5 not taken.
✓ Branch 4 → 5 taken 215 times.
✓ Branch 4 → 6 taken 225 times.
✓ Branch 7 → 8 taken 215 times.
✓ Branch 7 → 9 taken 225 times.
496 if (m_stopping || m_active) return;
106
5/7
✓ Branch 14 → 15 taken 169 times.
✓ Branch 14 → 19 taken 56 times.
✓ Branch 15 → 16 taken 169 times.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 169 times.
✗ Branch 20 → 10 not taken.
✓ Branch 20 → 21 taken 225 times.
225 while (!m_pending.isEmpty() && !m_pending.first().owner) {
107 ✗ disconnect(m_pending.first().ownerDestroyed);
108 ✗ m_pending.removeFirst();
109 }
110
2/2
✓ Branch 22 → 23 taken 56 times.
✓ Branch 22 → 24 taken 169 times.
225 if (m_pending.isEmpty()) return;
111
1/1
✓ Branch 24 → 25 taken 169 times.
169 m_active = m_pending.takeFirst();
112
3/6
✓ Branch 27 → 28 taken 169 times.
✓ Branch 28 → 29 taken 169 times.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 169 times.
✗ Branch 49 → 50 not taken.
✗ Branch 49 → 51 not taken.
169 auto *watcher = new QFutureWatcher<MailSelectionResult>(this);
113
1/1
✓ Branch 31 → 32 taken 169 times.
169 connect(watcher, &QFutureWatcher<MailSelectionResult>::finished, this, [this, watcher] {
114 169 auto request = std::move(*m_active);
115 169 m_active.reset();
116
1/1
✓ Branch 6 → 7 taken 169 times.
169 disconnect(request.ownerDestroyed);
117
1/1
✓ Branch 7 → 8 taken 169 times.
169 auto result = watcher->result();
118
1/1
✓ Branch 8 → 9 taken 169 times.
169 watcher->deleteLater();
119
1/1
✓ Branch 9 → 10 taken 169 times.
169 QPointer<MailSelectionService> guard(this);
120
8/9
✓ Branch 10 → 11 taken 169 times.
✗ Branch 10 → 17 not taken.
✓ Branch 12 → 13 taken 167 times.
✓ Branch 12 → 17 taken 2 times.
✓ Branch 15 → 16 taken 157 times.
✓ Branch 15 → 17 taken 10 times.
✓ Branch 18 → 19 taken 157 times.
✓ Branch 18 → 24 taken 12 times.
✓ Branch 21 → 22 taken 157 times.
169 if (!m_stopping && request.owner && !request.cancelled->load()) request.callback(std::move(result));
121
3/3
✓ Branch 25 → 26 taken 168 times.
✓ Branch 25 → 27 taken 1 time.
✓ Branch 26 → 27 taken 168 times.
169 if (guard) startNext();
122 169 });
123
1/1
✓ Branch 34 → 35 taken 169 times.
169 const auto work = m_active->work;
124 169 const auto cancelled = m_active->cancelled;
125
3/5
✓ Branch 37 → 38 taken 169 times.
✓ Branch 39 → 40 taken 169 times.
✓ Branch 40 → 41 taken 169 times.
✗ Branch 58 → 59 not taken.
✗ Branch 58 → 60 not taken.
169 watcher->setFuture(QtConcurrent::run(&m_pool, [work, cancelled] {
126
2/2
✓ Branch 4 → 5 taken 6 times.
✓ Branch 4 → 8 taken 163 times.
169 if (cancelled->load()) {
127 6 MailSelectionResult result;
128 6 result.outcome = MailSelectionResult::Outcome::Cancelled;
129
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 6 times.
6 return result;
130 ✗ }
131 163 return work(*cancelled);
132 }));
133 169 }
134