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.2% 176 / 0 / 183
Functions: 95.0% 19 / 0 / 20
Branches: 82.5% 184 / 0 / 223

performance/StartupPerformanceTracker.cpp
Line Branch Exec Source
1 #include "performance/StartupPerformanceTracker.h"
2
3 #include <QApplication>
4 #include <QEvent>
5 #include <QTimer>
6 #include <QWidget>
7
8 #include <algorithm>
9
10 namespace MailJD::Performance {
11
12 namespace {
13
14 constexpr auto WindowVisibleMetric = "startup.process_to_window_visible.v1";
15 constexpr auto InputReadyMetric = "startup.process_to_input_ready.v1";
16 constexpr auto CachedMailboxMetric = "startup.process_to_cached_mailbox.v1";
17 constexpr auto OnlineReadyMetric = "startup.process_to_online_ready.v1";
18 constexpr auto MainThreadStallMetric = "startup.main_thread_stall_max.v1";
19
20 } // namespace
21
22 3281 StartupPerformanceTracker &StartupPerformanceTracker::instance() {
23
4/7
✓ Branch 2 → 3 taken 32 times.
✓ Branch 2 → 8 taken 3249 times.
✓ Branch 4 → 5 taken 32 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 32 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
3281 static StartupPerformanceTracker tracker;
24 3281 return tracker;
25 }
26
27 32 StartupPerformanceTracker::StartupPerformanceTracker(QObject *parent)
28
3/6
✓ Branch 10 → 11 taken 32 times.
✓ Branch 11 → 12 taken 32 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 32 times.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
32 : QObject(parent), m_stallTimer(new QTimer(this)),
29
3/6
✓ Branch 14 → 15 taken 32 times.
✓ Branch 15 → 16 taken 32 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 32 times.
✗ Branch 28 → 29 not taken.
✗ Branch 28 → 30 not taken.
64 m_stallDeadline(new QTimer(this)) {
30
1/1
✓ Branch 18 → 19 taken 32 times.
32 m_stallTimer->setTimerType(Qt::PreciseTimer);
31
1/1
✓ Branch 19 → 20 taken 32 times.
32 m_stallDeadline->setSingleShot(true);
32
1/1
✓ Branch 20 → 21 taken 32 times.
32 connect(m_stallTimer, &QTimer::timeout, this, [this]() {
33 ✗ const qint64 now = PerformanceTrace::monotonicNowNs();
34 ✗ const qint64 expected =
35 ✗ m_previousHeartbeatNs +
36 ✗ static_cast<qint64>(m_heartbeatIntervalMs) * 1000000;
37 ✗ m_maxStallNs = std::max<qint64>(0, std::max(m_maxStallNs, now - expected));
38 ✗ m_previousHeartbeatNs = now;
39 ✗ });
40 32 connect(m_stallDeadline, &QTimer::timeout, this,
41
1/1
✓ Branch 22 → 23 taken 32 times.
32 &StartupPerformanceTracker::finishStallObservation);
42 32 }
43
44 9 void StartupPerformanceTracker::resetOperations() {
45 9 m_windowVisible = {};
46 9 m_inputReady = {};
47 9 m_cachedMailbox = {};
48 9 m_onlineReady = {};
49 9 }
50
51 9 void StartupPerformanceTracker::configure(qint64 processStartNs,
52 int stallObservationMs) {
53
1/2
✓ Branch 3 → 4 taken 9 times.
✗ Branch 3 → 6 not taken.
9 if (qApp)
54 9 qApp->removeEventFilter(this);
55 9 m_stallTimer->stop();
56 9 m_stallDeadline->stop();
57 9 resetOperations();
58
2/2
✓ Branch 9 → 10 taken 9 times.
✓ Branch 10 → 11 taken 9 times.
9 m_scenario = PerformanceTrace::instance().selectedScenario();
59 9 m_processStartNs = processStartNs;
60 9 m_window = nullptr;
61 9 m_mailboxViewport = nullptr;
62 9 m_firstPaint = false;
63 9 m_inputPending = false;
64 9 m_cachedPending = false;
65 9 m_onlinePending = false;
66 9 m_accountClassified = false;
67 9 m_cachedFolderId = -1;
68 9 m_onlineFolderId = -1;
69 9 m_cachedRows = -1;
70 9 m_onlineRows = -1;
71 9 m_authenticationStartNs = 0;
72 9 m_folderListStartNs = 0;
73 9 m_authoritativeStartNs = 0;
74 9 m_maxStallNs = 0;
75
2/2
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 18 taken 8 times.
9 if (!isActive())
76 1 return;
77
78
1/1
✓ Branch 18 → 19 taken 8 times.
16 m_windowVisible = PerformanceTrace::instance().startMetric(
79
2/2
✓ Branch 20 → 21 taken 8 times.
✓ Branch 21 → 22 taken 8 times.
16 m_scenario, QString::fromLatin1(WindowVisibleMetric), processStartNs);
80
1/1
✓ Branch 25 → 26 taken 8 times.
16 m_inputReady = PerformanceTrace::instance().startMetric(
81
2/2
✓ Branch 27 → 28 taken 8 times.
✓ Branch 28 → 29 taken 8 times.
16 m_scenario, QString::fromLatin1(InputReadyMetric), processStartNs);
82 8 m_previousHeartbeatNs = PerformanceTrace::monotonicNowNs();
83 8 m_stallTimer->setInterval(m_heartbeatIntervalMs);
84 8 m_stallTimer->start();
85
1/1
✓ Branch 36 → 37 taken 8 times.
8 m_stallDeadline->start(std::max(1, stallObservationMs));
86
1/2
✓ Branch 38 → 39 taken 8 times.
✗ Branch 38 → 41 not taken.
8 if (qApp)
87 8 qApp->installEventFilter(this);
88 }
89
90 3421 bool StartupPerformanceTracker::isActive() const {
91
4/4
✓ Branch 2 → 3 taken 3421 times.
✓ Branch 3 → 4 taken 3421 times.
✓ Branch 4 → 5 taken 118 times.
✓ Branch 4 → 11 taken 3303 times.
3539 return PerformanceTrace::instance().isEnabled() &&
92
7/11
✓ Branch 8 → 9 taken 118 times.
✓ Branch 9 → 10 taken 109 times.
✓ Branch 9 → 11 taken 9 times.
✓ Branch 12 → 13 taken 118 times.
✓ Branch 12 → 14 taken 3303 times.
✓ Branch 14 → 15 taken 118 times.
✓ Branch 14 → 16 taken 3303 times.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 20 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
3657 m_scenario.startsWith(QStringLiteral("START."));
93 }
94
95 6 void StartupPerformanceTracker::watchWindow(QWidget *window,
96 QWidget *mailboxViewport) {
97
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 5 times.
6 if (!isActive())
98 1 return;
99 5 m_window = window;
100 5 m_mailboxViewport = mailboxViewport;
101 }
102
103 2418 void StartupPerformanceTracker::recordPhase(const QString &phase,
104 qint64 startNs, qint64 endNs) {
105
6/6
✓ Branch 3 → 4 taken 10 times.
✓ Branch 3 → 5 taken 2408 times.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 8 times.
✓ Branch 7 → 8 taken 2410 times.
✓ Branch 7 → 9 taken 8 times.
2418 if (!isActive() || endNs < startNs)
106 2410 return;
107 8 PerformanceTrace::instance().markProcessEvent(m_scenario, phase, endNs,
108 endNs - startNs);
109 }
110
111 858 void StartupPerformanceTracker::markConfiguredAccount() {
112
6/6
✓ Branch 3 → 4 taken 6 times.
✓ Branch 3 → 5 taken 852 times.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 4 times.
✓ Branch 7 → 8 taken 854 times.
✓ Branch 7 → 9 taken 4 times.
858 if (!isActive() || m_accountClassified)
113 854 return;
114 4 m_accountClassified = true;
115
1/1
✓ Branch 9 → 10 taken 4 times.
8 m_cachedMailbox = PerformanceTrace::instance().startMetric(
116
2/2
✓ Branch 11 → 12 taken 4 times.
✓ Branch 12 → 13 taken 4 times.
8 m_scenario, QString::fromLatin1(CachedMailboxMetric), m_processStartNs);
117
6/11
✓ Branch 19 → 20 taken 4 times.
✓ Branch 20 → 21 taken 3 times.
✓ Branch 20 → 27 taken 1 time.
✓ Branch 34 → 35 taken 4 times.
✗ Branch 34 → 36 not taken.
✓ Branch 36 → 37 taken 2 times.
✓ Branch 36 → 45 taken 2 times.
✗ Branch 72 → 73 not taken.
✗ Branch 72 → 74 not taken.
✗ Branch 76 → 77 not taken.
✗ Branch 76 → 78 not taken.
15 if (!m_scenario.contains(QStringLiteral(".offline_")) &&
118
8/13
✓ Branch 24 → 25 taken 3 times.
✓ Branch 25 → 26 taken 2 times.
✓ Branch 25 → 27 taken 1 time.
✓ Branch 28 → 29 taken 3 times.
✓ Branch 28 → 30 taken 1 time.
✓ Branch 30 → 31 taken 3 times.
✓ Branch 30 → 32 taken 1 time.
✓ Branch 32 → 33 taken 4 times.
✗ Branch 32 → 34 not taken.
✗ Branch 64 → 65 not taken.
✗ Branch 64 → 66 not taken.
✗ Branch 68 → 69 not taken.
✗ Branch 68 → 70 not taken.
10 !m_scenario.contains(QStringLiteral(".offline."))) {
119
1/1
✓ Branch 37 → 38 taken 2 times.
4 m_onlineReady = PerformanceTrace::instance().startMetric(
120
2/2
✓ Branch 39 → 40 taken 2 times.
✓ Branch 40 → 41 taken 2 times.
4 m_scenario, QString::fromLatin1(OnlineReadyMetric), m_processStartNs);
121 } else {
122 2 PerformanceTrace::instance().markProcessEvent(
123
2/2
✓ Branch 50 → 51 taken 2 times.
✓ Branch 52 → 53 taken 2 times.
4 m_scenario, QString::fromLatin1(OnlineReadyMetric) +
124
1/1
✓ Branch 51 → 52 taken 2 times.
6 QStringLiteral(".inapplicable"));
125 }
126 }
127
128 19 void StartupPerformanceTracker::markNoAccount() {
129
6/6
✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 5 taken 15 times.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 2 times.
✓ Branch 7 → 8 taken 17 times.
✓ Branch 7 → 9 taken 2 times.
19 if (!isActive() || m_accountClassified)
130 17 return;
131 2 m_accountClassified = true;
132 2 PerformanceTrace::instance().markProcessEvent(
133
2/2
✓ Branch 14 → 15 taken 2 times.
✓ Branch 16 → 17 taken 2 times.
4 m_scenario, QString::fromLatin1(CachedMailboxMetric) +
134
1/1
✓ Branch 15 → 16 taken 2 times.
6 QStringLiteral(".inapplicable"));
135 2 PerformanceTrace::instance().markProcessEvent(
136
1/1
✓ Branch 28 → 29 taken 2 times.
2 m_scenario,
137
2/2
✓ Branch 26 → 27 taken 2 times.
✓ Branch 27 → 28 taken 2 times.
4 QString::fromLatin1(OnlineReadyMetric) + QStringLiteral(".inapplicable"));
138 }
139
140 16 void StartupPerformanceTracker::markAuthenticationStarted() {
141
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 6 taken 14 times.
16 if (isActive())
142 2 m_authenticationStartNs = PerformanceTrace::monotonicNowNs();
143 16 }
144
145 5 void StartupPerformanceTracker::markAuthenticationReady() {
146
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 4 times.
5 if (!isActive())
147 1 return;
148 4 const qint64 now = PerformanceTrace::monotonicNowNs();
149
2/2
✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 14 taken 2 times.
4 if (m_authenticationStartNs > 0)
150
1/1
✓ Branch 10 → 11 taken 2 times.
4 recordPhase(QStringLiteral("authentication"), m_authenticationStartNs, now);
151 4 m_folderListStartNs = now;
152 }
153
154 3 void StartupPerformanceTracker::markFolderListReady() {
155
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 2 times.
3 if (!isActive())
156 1 return;
157 2 const qint64 now = PerformanceTrace::monotonicNowNs();
158
1/2
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 14 not taken.
2 if (m_folderListStartNs > 0)
159
1/1
✓ Branch 10 → 11 taken 2 times.
4 recordPhase(QStringLiteral("folder_list"), m_folderListStartNs, now);
160 }
161
162 7 void StartupPerformanceTracker::expectCachedMailbox(qint64 folderId,
163 int rowCount) {
164
8/8
✓ Branch 3 → 4 taken 6 times.
✓ Branch 3 → 6 taken 1 time.
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 6 taken 2 times.
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 7 taken 2 times.
✓ Branch 8 → 9 taken 5 times.
✓ Branch 8 → 10 taken 2 times.
7 if (!m_cachedMailbox.isValid() || folderId <= 0 || rowCount < 0)
165 5 return;
166 2 m_cachedFolderId = folderId;
167 2 m_cachedRows = rowCount;
168 2 m_cachedPending = true;
169
1/2
✓ Branch 10 → 11 taken 2 times.
✗ Branch 10 → 13 not taken.
2 if (m_authoritativeStartNs == 0)
170 2 m_authoritativeStartNs = PerformanceTrace::monotonicNowNs();
171
1/2
✓ Branch 14 → 15 taken 2 times.
✗ Branch 14 → 17 not taken.
2 if (m_mailboxViewport)
172 2 m_mailboxViewport->update();
173 }
174
175 6 void StartupPerformanceTracker::expectAuthoritativeMailbox(qint64 folderId,
176 int rowCount) {
177
6/8
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 6 taken 3 times.
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 6 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 3 times.
✓ Branch 8 → 9 taken 3 times.
✓ Branch 8 → 10 taken 3 times.
6 if (!m_onlineReady.isValid() || folderId <= 0 || rowCount < 0)
178 3 return;
179 3 m_onlineFolderId = folderId;
180 3 m_onlineRows = rowCount;
181 3 m_onlinePending = true;
182 3 const qint64 now = PerformanceTrace::monotonicNowNs();
183
2/2
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 19 taken 1 time.
3 if (m_authoritativeStartNs > 0) {
184
1/1
✓ Branch 15 → 16 taken 2 times.
4 recordPhase(QStringLiteral("authoritative_sync_push"),
185 m_authoritativeStartNs, now);
186 2 m_authoritativeStartNs = 0;
187 }
188
1/2
✓ Branch 20 → 21 taken 3 times.
✗ Branch 20 → 23 not taken.
3 if (m_mailboxViewport)
189 3 m_mailboxViewport->update();
190 }
191
192 9 void StartupPerformanceTracker::inputProbeAcknowledged() {
193
2/2
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 5 taken 6 times.
9 if (!m_inputReady.isValid())
194 3 return;
195 6 m_inputPending = true;
196 6 finishInputIfReady();
197 }
198
199 10 void StartupPerformanceTracker::finishInputIfReady() {
200
7/8
✓ Branch 2 → 3 taken 6 times.
✓ Branch 2 → 7 taken 4 times.
✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 7 taken 2 times.
✓ Branch 5 → 6 taken 4 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 4 times.
✓ Branch 8 → 14 taken 6 times.
10 if (m_firstPaint && m_inputPending && m_inputReady.isValid()) {
201 4 m_inputReady.finish(Outcome::Success);
202 4 m_inputReady = {};
203 4 m_inputPending = false;
204 }
205 10 }
206
207 4 void StartupPerformanceTracker::finishStallObservation() {
208 // S106-217: the heartbeat stops even when the trace ended before the
209 // observation deadline — it used to keep waking the GUI thread for the
210 // rest of the session.
211 4 m_stallTimer->stop();
212
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 3 times.
4 if (!isActive())
213 1 return;
214 3 PerformanceTrace::instance().markProcessEvent(
215
2/2
✓ Branch 9 → 10 taken 3 times.
✓ Branch 10 → 11 taken 3 times.
3 m_scenario, QString::fromLatin1(MainThreadStallMetric),
216 PerformanceTrace::monotonicNowNs(), m_maxStallNs);
217 }
218
219 35 bool StartupPerformanceTracker::belongsToWindow(QObject *object) const {
220 // Window-owned QAction/QShortcut objects receive shortcut events instead
221 // of a widget KeyPress when the shortcut map consumes the key.
222
5/6
✓ Branch 8 → 9 taken 45 times.
✓ Branch 8 → 12 taken 4 times.
✓ Branch 10 → 11 taken 45 times.
✗ Branch 10 → 12 not taken.
✓ Branch 13 → 3 taken 45 times.
✓ Branch 13 → 14 taken 4 times.
49 while (object && m_window) {
223
2/2
✓ Branch 4 → 5 taken 31 times.
✓ Branch 4 → 6 taken 14 times.
45 if (object == m_window)
224 31 return true;
225 14 object = object->parent();
226 }
227 4 return false;
228 }
229
230 82 bool StartupPerformanceTracker::eventFilter(QObject *watched, QEvent *event) {
231
6/6
✓ Branch 3 → 4 taken 65 times.
✓ Branch 3 → 6 taken 17 times.
✓ Branch 5 → 6 taken 18 times.
✓ Branch 5 → 7 taken 47 times.
✓ Branch 8 → 9 taken 35 times.
✓ Branch 8 → 11 taken 47 times.
82 if (!isActive() || !m_window)
232 35 return QObject::eventFilter(watched, event);
233
234 47 auto *paintedWidget = qobject_cast<QWidget *>(watched);
235
6/6
✓ Branch 14 → 15 taken 28 times.
✓ Branch 14 → 21 taken 12 times.
✓ Branch 16 → 17 taken 25 times.
✓ Branch 16 → 21 taken 3 times.
✓ Branch 18 → 19 taken 4 times.
✓ Branch 18 → 21 taken 21 times.
65 if (paintedWidget && paintedWidget->isWindow() && belongsToWindow(watched) &&
236
5/6
✓ Branch 12 → 13 taken 40 times.
✓ Branch 12 → 21 taken 7 times.
✓ Branch 19 → 20 taken 4 times.
✗ Branch 19 → 21 not taken.
✓ Branch 22 → 23 taken 4 times.
✓ Branch 22 → 31 taken 43 times.
112 event->type() == QEvent::Paint && !m_firstPaint) {
237 4 m_firstPaint = true;
238
1/2
✓ Branch 24 → 25 taken 4 times.
✗ Branch 24 → 30 not taken.
4 if (m_windowVisible.isValid()) {
239 4 m_windowVisible.finish(Outcome::Success);
240 4 m_windowVisible = {};
241 }
242 4 finishInputIfReady();
243 }
244
245
6/6
✓ Branch 32 → 33 taken 12 times.
✓ Branch 32 → 36 taken 35 times.
✓ Branch 34 → 35 taken 3 times.
✓ Branch 34 → 36 taken 9 times.
✓ Branch 37 → 38 taken 3 times.
✓ Branch 37 → 62 taken 44 times.
47 if (watched == m_mailboxViewport && event->type() == QEvent::Paint) {
246
5/6
✓ Branch 38 → 39 taken 2 times.
✓ Branch 38 → 42 taken 1 time.
✓ Branch 40 → 41 taken 2 times.
✗ Branch 40 → 42 not taken.
✓ Branch 43 → 44 taken 2 times.
✓ Branch 43 → 49 taken 1 time.
3 if (m_cachedPending && m_cachedMailbox.isValid()) {
247 2 m_cachedMailbox.finish(Outcome::Success, m_cachedRows);
248 2 m_cachedMailbox = {};
249 2 m_cachedPending = false;
250 }
251
1/2
✓ Branch 51 → 52 taken 3 times.
✗ Branch 51 → 55 not taken.
3 if (m_onlinePending && m_onlineReady.isValid() &&
252
6/8
✓ Branch 49 → 50 taken 3 times.
✗ Branch 49 → 55 not taken.
✓ Branch 52 → 53 taken 2 times.
✓ Branch 52 → 55 taken 1 time.
✓ Branch 53 → 54 taken 2 times.
✗ Branch 53 → 55 not taken.
✓ Branch 56 → 57 taken 2 times.
✓ Branch 56 → 62 taken 1 time.
6 m_onlineFolderId == m_cachedFolderId && m_onlineRows >= 0) {
253 2 m_onlineReady.finish(Outcome::Success, m_onlineRows);
254 2 m_onlineReady = {};
255 2 m_onlinePending = false;
256 }
257 }
258
259
4/4
✓ Branch 65 → 66 taken 42 times.
✓ Branch 65 → 68 taken 2 times.
✓ Branch 67 → 68 taken 2 times.
✓ Branch 67 → 71 taken 40 times.
133 if ((event->type() == QEvent::KeyPress || event->type() == QEvent::Shortcut ||
260
6/6
✓ Branch 63 → 64 taken 44 times.
✓ Branch 63 → 68 taken 3 times.
✓ Branch 69 → 70 taken 6 times.
✓ Branch 69 → 71 taken 1 time.
✓ Branch 72 → 73 taken 6 times.
✓ Branch 72 → 74 taken 41 times.
136 event->type() == QEvent::MouseButtonPress) &&
261 7 belongsToWindow(watched)) {
262 6 inputProbeAcknowledged();
263 }
264 47 return QObject::eventFilter(watched, event);
265 }
266
267 } // namespace MailJD::Performance
268