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.0% 209 / 0 / 220
Functions: 100.0% 26 / 0 / 26
Branches: 80.3% 232 / 0 / 289

service/ImapCommandTable.cpp
Line Branch Exec Source
1 #include "ImapCommandTable.h"
2
3 #include <QLoggingCategory>
4 #include <QSet>
5 #include <QSslSocket>
6 #include <QTimer>
7
8 Q_DECLARE_LOGGING_CATEGORY(lcImap)
9
10 1401 ImapCommandTable::ImapCommandTable(QObject *owner, QSslSocket *socket,
11 1401 const QString &selectedFolder, Deps deps)
12 1401 : m_socket(socket), m_selectedFolder(selectedFolder), m_deps(std::move(deps)),
13
3/6
✓ Branch 4 → 5 taken 1401 times.
✓ Branch 5 → 6 taken 1401 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1401 times.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
1401 m_commandTimeoutTimer(new QTimer(owner)),
14
3/6
✓ Branch 8 → 9 taken 1401 times.
✓ Branch 9 → 10 taken 1401 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 1401 times.
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
1401 m_commandDeadlineTimer(new QTimer(owner)) {}
15
16 1274 void ImapCommandTable::executeAfterIdle(std::function<void()> command) {
17
2/2
✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 5 taken 1270 times.
1274 if (m_deps.isNotifying()) {
18 // T-320: NOTIFY doesn't block — execute immediately, no stop needed
19 4 command();
20
2/2
✓ Branch 6 → 7 taken 12 times.
✓ Branch 6 → 10 taken 1258 times.
1270 } else if (m_deps.isIdling()) {
21 // Queue command and stop IDLE – command runs on IDLE OK
22 12 m_deferredCommands.enqueue(std::move(command));
23 12 m_deps.stopIdle();
24 } else {
25 // Not idling/notifying – execute immediately
26 1258 command();
27 }
28 1274 }
29
30 31 void ImapCommandTable::runNextDeferredCommand() {
31
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 31 times.
31 if (m_deferredCommands.isEmpty()) return;
32 // A deferred command waits for IDLE to end. A completion observer of the
33 // tagged response being handled may already have started IDLE again (the
34 // settings sync starts its IDLE watch on SELECT, then queues an APPEND);
35 // running the command now would send it inside IDLE and fail ("Cannot
36 // APPEND: not authenticated"). The IDLE tagged OK drains the queue.
37
2/3
✓ Branch 5 → 6 taken 31 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 21 taken 31 times.
31 if (m_deps.isIdling()) {
38 ✗ qCInfo(lcImap) << "Deferred command waits for the IDLE response ("
39 ✗ << m_deferredCommands.size() << "queued)";
40 ✗ return;
41 }
42
1/1
✓ Branch 21 → 22 taken 31 times.
31 auto command = m_deferredCommands.dequeue();
43
1/1
✓ Branch 22 → 23 taken 31 times.
31 command();
44 31 }
45
46 28 void ImapCommandTable::clearDeferredCommands() {
47
2/2
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 18 taken 25 times.
28 if (!m_deferredCommands.isEmpty()) {
48
6/6
✓ Branch 4 → 5 taken 3 times.
✓ Branch 8 → 9 taken 3 times.
✓ Branch 9 → 10 taken 3 times.
✓ Branch 11 → 12 taken 3 times.
✓ Branch 16 → 6 taken 3 times.
✓ Branch 16 → 17 taken 3 times.
6 qCInfo(lcImap) << "Clearing" << m_deferredCommands.size()
49
1/1
✓ Branch 12 → 13 taken 3 times.
3 << "deferred commands (folder switch)";
50 3 m_deferredCommands.clear();
51 }
52 28 }
53
54 23 QString ImapCommandTable::sendTaggedCommand(const QString &type,
55 const QString &command,
56 ImapPendingCommand record) {
57 // Thin wrapper around sendCommand() that exposes the generated tag, so
58 // the probe can match its response in handleTagged().
59 23 QString tag;
60 // Inline copy of sendCommand() but return tag (cannot refactor callers
61 // without disturbing the existing command-timeout/reflow behaviour).
62 const bool startsCommandBudget =
63
2/12
✓ Branch 6 → 7 taken 23 times.
✗ Branch 6 → 10 not taken.
✓ Branch 13 → 14 taken 23 times.
✗ Branch 13 → 15 not taken.
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 53 not taken.
✗ Branch 55 → 56 not taken.
✗ Branch 55 → 57 not taken.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
✗ Branch 66 → 67 not taken.
✗ Branch 66 → 68 not taken.
92 type != QStringLiteral("IDLE") &&
64
4/5
✓ Branch 7 → 8 taken 23 times.
✓ Branch 8 → 9 taken 21 times.
✓ Branch 8 → 10 taken 2 times.
✓ Branch 11 → 12 taken 23 times.
✗ Branch 11 → 13 not taken.
46 !hasTimeoutTrackedCommandInFlight();
65
2/2
✓ Branch 15 → 16 taken 21 times.
✓ Branch 15 → 17 taken 2 times.
23 if (startsCommandBudget)
66 21 resetCommandAccumulators();
67
68
1/1
✓ Branch 17 → 18 taken 23 times.
23 tag = nextTag();
69 23 record.type = type;
70 23 QElapsedTimer timer;
71 23 timer.start();
72 23 record.timer = timer;
73
1/1
✓ Branch 24 → 25 taken 23 times.
23 [[maybe_unused]] const auto inserted = m_pending.insert(tag, std::move(record));
74
3/3
✓ Branch 25 → 26 taken 23 times.
✓ Branch 26 → 27 taken 23 times.
✓ Branch 27 → 28 taken 23 times.
23 auto fullCommand = tag + " " + command + "\r\n";
75
7/7
✓ Branch 30 → 31 taken 23 times.
✓ Branch 34 → 35 taken 23 times.
✓ Branch 35 → 36 taken 23 times.
✓ Branch 36 → 37 taken 23 times.
✓ Branch 37 → 38 taken 23 times.
✓ Branch 42 → 32 taken 23 times.
✓ Branch 42 → 43 taken 23 times.
46 qCDebug(lcImap) << ">>>" << fullCommand.trimmed();
76 #ifdef MAILJD_E2E_TESTING
77
3/3
✓ Branch 44 → 45 taken 19 times.
✓ Branch 49 → 50 taken 19 times.
✓ Branch 50 → 51 taken 19 times.
57 m_deps.reportE2eTransportPhase(QStringLiteral("command-write:%1").arg(type),
78 19 m_deps.e2eRequestTokenFor(*inserted));
79 #endif
80
4/4
✓ Branch 43 → 44 taken 4 times.
✓ Branch 44 → 45 taken 4 times.
✓ Branch 54 → 55 taken 19 times.
✓ Branch 55 → 56 taken 19 times.
23 m_socket->write(fullCommand.toUtf8());
81
2/2
✓ Branch 46 → 47 taken 4 times.
✓ Branch 57 → 58 taken 19 times.
23 refreshCommandTimeout();
82
2/2
✓ Branch 47 → 48 taken 4 times.
✓ Branch 58 → 59 taken 19 times.
23 refreshCommandDeadline();
83 23 return tag;
84 23 }
85
86 1458 bool ImapCommandTable::hasStatefulCommandInFlight() const {
87 static const QSet<QString> statefulTypes = {
88 14 QStringLiteral("LIST"), QStringLiteral("SELECT"),
89 14 QStringLiteral("FETCH_HEADERS"), QStringLiteral("FETCH_BODY"),
90 14 QStringLiteral("FETCH_FLAGS"), QStringLiteral("FETCH_SEQNO"),
91 14 QStringLiteral("FETCH_FLAGS_CHANGED"), QStringLiteral("SEARCH_MEMBERSHIP"),
92 14 QStringLiteral("SEARCH"), QStringLiteral("STATUS"),
93 14 QStringLiteral("STORE"), QStringLiteral("MOVE"),
94 14 QStringLiteral("COPY"), QStringLiteral("STORE_DELETE"),
95 14 QStringLiteral("EXPUNGE_MOVE"), QStringLiteral("COPY_ONLY"),
96 14 QStringLiteral("EXPUNGE"), QStringLiteral("APPEND"),
97 14 QStringLiteral("CREATE"), QStringLiteral("DELETE"),
98
5/10
✓ Branch 2 → 3 taken 14 times.
✓ Branch 2 → 100 taken 1444 times.
✓ Branch 4 → 5 taken 14 times.
✗ Branch 4 → 100 not taken.
✓ Branch 75 → 76 taken 308 times.
✓ Branch 75 → 77 taken 14 times.
✗ Branch 113 → 114 not taken.
✗ Branch 113 → 115 not taken.
✗ Branch 188 → 189 not taken.
✗ Branch 188 → 190 not taken.
1794 QStringLiteral("RENAME"), QStringLiteral("NOOP")};
99
100
4/4
✓ Branch 100 → 101 taken 1458 times.
✓ Branch 101 → 102 taken 1458 times.
✓ Branch 109 → 103 taken 498 times.
✓ Branch 109 → 110 taken 1024 times.
1522 for (const ImapPendingCommand &record : m_pending) {
101
2/2
✓ Branch 105 → 106 taken 434 times.
✓ Branch 105 → 107 taken 64 times.
498 if (statefulTypes.contains(record.type))
102 434 return true;
103 }
104 1024 return false;
105 322 }
106
107 108974 bool ImapCommandTable::hasTimeoutTrackedCommandInFlight() const {
108
4/4
✓ Branch 2 → 3 taken 108974 times.
✓ Branch 3 → 4 taken 108974 times.
✓ Branch 16 → 5 taken 7046 times.
✓ Branch 16 → 17 taken 101944 times.
108990 for (const ImapPendingCommand &record : m_pending) {
109
2/2
✓ Branch 12 → 13 taken 7030 times.
✓ Branch 12 → 14 taken 16 times.
7046 if (record.type != QStringLiteral("IDLE"))
110 7030 return true;
111 }
112 101944 return false;
113 }
114
115 // A queued UID command must run in the mailbox that was selected when it was
116 // issued. A SELECT queued ahead of it would otherwise retarget the UID: the
117 // server answers OK for a UID STORE/MOVE of a UID that does not exist there
118 // (or acts on a different message that happens to share the UID). Journal
119 // tokens are rejected as "Mailbox changed before dispatch"; the controller
120 // requeues them without a user-visible notice.
121 15 bool ImapCommandTable::queuedMailboxStillSelected(const QString &issuedFolder,
122 const QList<qint64> &mutationTokens,
123 const char *command) {
124
2/2
✓ Branch 3 → 4 taken 11 times.
✓ Branch 3 → 5 taken 4 times.
15 if (m_selectedFolder == issuedFolder)
125 11 return true;
126
6/6
✓ Branch 5 → 6 taken 4 times.
✓ Branch 9 → 10 taken 4 times.
✓ Branch 10 → 11 taken 4 times.
✓ Branch 11 → 12 taken 4 times.
✓ Branch 19 → 7 taken 4 times.
✓ Branch 19 → 20 taken 4 times.
8 qCWarning(lcImap) << "Dropping queued" << command
127
1/1
✓ Branch 12 → 13 taken 4 times.
4 << "because the selected mailbox changed from"
128
3/3
✓ Branch 13 → 14 taken 4 times.
✓ Branch 14 → 15 taken 4 times.
✓ Branch 15 → 16 taken 4 times.
4 << issuedFolder << "to" << m_selectedFolder;
129
2/2
✓ Branch 31 → 22 taken 4 times.
✓ Branch 31 → 32 taken 4 times.
8 for (qint64 token : mutationTokens)
130
1/1
✓ Branch 26 → 27 taken 4 times.
8 m_deps.completeMutation(token, ImapService::MutationOutcome::Rejected,
131 8 QStringLiteral("Mailbox changed before dispatch"));
132 4 return false;
133 }
134
135 105 void ImapCommandTable::enqueueSerializedCommand(std::function<void()> command) {
136 105 m_serializedCommands.enqueue(std::move(command));
137
5/5
✓ Branch 4 → 5 taken 105 times.
✓ Branch 8 → 9 taken 105 times.
✓ Branch 9 → 10 taken 105 times.
✓ Branch 15 → 6 taken 105 times.
✓ Branch 15 → 16 taken 105 times.
210 qCDebug(lcImap) << "Queued stateful IMAP command; queue size:"
138
1/1
✓ Branch 11 → 12 taken 105 times.
105 << m_serializedCommands.size();
139 105 }
140
141 482 void ImapCommandTable::runNextSerializedCommand() {
142
6/6
✓ Branch 18 → 19 taken 366 times.
✓ Branch 18 → 22 taken 154 times.
✓ Branch 20 → 21 taken 38 times.
✓ Branch 20 → 22 taken 328 times.
✓ Branch 23 → 3 taken 38 times.
✓ Branch 23 → 24 taken 482 times.
520 while (!hasStatefulCommandInFlight() && !m_serializedCommands.isEmpty()) {
143
1/1
✓ Branch 3 → 4 taken 38 times.
38 auto command = m_serializedCommands.dequeue();
144
1/1
✓ Branch 6 → 7 taken 38 times.
38 executeAfterIdle(std::move(command));
145
2/3
✓ Branch 8 → 9 taken 38 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 38 times.
38 if (m_deps.isIdling())
146 ✗ return;
147 38 }
148
3/4
✓ Branch 27 → 28 taken 2 times.
✓ Branch 27 → 33 taken 2 times.
✓ Branch 29 → 30 taken 2 times.
✗ Branch 29 → 33 not taken.
488 if (m_deps.idleStartPending() && !hasStatefulCommandInFlight() &&
149
5/6
✓ Branch 25 → 26 taken 4 times.
✓ Branch 25 → 33 taken 478 times.
✓ Branch 31 → 32 taken 2 times.
✗ Branch 31 → 33 not taken.
✓ Branch 34 → 35 taken 2 times.
✓ Branch 34 → 36 taken 480 times.
488 m_serializedCommands.isEmpty() && m_deferredCommands.isEmpty())
150 2 m_deps.startIdle();
151 }
152
153 3922 void ImapCommandTable::refreshCommandTimeout() {
154
2/2
✓ Branch 3 → 4 taken 516 times.
✓ Branch 3 → 6 taken 3406 times.
3922 if (!hasTimeoutTrackedCommandInFlight()) {
155 516 m_commandTimeoutTimer->stop();
156 516 return;
157 }
158 3406 m_commandTimeoutTimer->start(COMMAND_TIMEOUT_MS);
159 }
160
161 953 void ImapCommandTable::refreshCommandDeadline() {
162
3/3
✓ Branch 2 → 3 taken 953 times.
✓ Branch 3 → 4 taken 456 times.
✓ Branch 3 → 6 taken 497 times.
953 if (!hasTimeoutTrackedCommandInFlight()) {
163
1/1
✓ Branch 4 → 5 taken 456 times.
456 m_commandDeadlineTimer->stop();
164 456 return;
165 }
166 // Absolute deadline: incoming progress may cause this helper to be called,
167 // but it can only shorten the remaining timer. It never grants a fresh
168 // window to a slow-drip server.
169 497 qint64 shortestRemaining = COMMAND_DEADLINE_MS;
170
4/4
✓ Branch 6 → 7 taken 497 times.
✓ Branch 7 → 8 taken 497 times.
✓ Branch 33 → 9 taken 534 times.
✓ Branch 33 → 34 taken 497 times.
1031 for (const ImapPendingCommand &record : m_pending) {
171
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 534 times.
534 if (record.type == QStringLiteral("IDLE"))
172 ✗ continue;
173
5/6
✓ Branch 19 → 20 taken 524 times.
✓ Branch 19 → 23 taken 10 times.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 524 times.
✓ Branch 25 → 26 taken 10 times.
✓ Branch 25 → 27 taken 524 times.
534 if (!record.timer || !record.timer->isValid())
174 10 continue;
175 524 shortestRemaining =
176 1048 qMin(shortestRemaining,
177 524 static_cast<qint64>(COMMAND_DEADLINE_MS) - record.timer->elapsed());
178 }
179 994 m_commandDeadlineTimer->start(
180
1/1
✓ Branch 35 → 36 taken 497 times.
497 static_cast<int>(qBound<qint64>(qint64(1), shortestRemaining,
181 994 qint64(COMMAND_DEADLINE_MS))));
182 }
183
184 3388 void ImapCommandTable::resetCommandAccumulators() {
185 3388 m_commandResponseBytes = 0;
186 3388 m_commandLiteralCount = 0;
187 3388 m_commandResultItems = 0;
188 3388 }
189
190 2965 bool ImapCommandTable::accountCommandResponseBytes(qint64 bytes) {
191
6/6
✓ Branch 2 → 3 taken 2935 times.
✓ Branch 2 → 5 taken 30 times.
✓ Branch 4 → 5 taken 60 times.
✓ Branch 4 → 6 taken 2875 times.
✓ Branch 7 → 8 taken 90 times.
✓ Branch 7 → 9 taken 2875 times.
2965 if (bytes <= 0 || !hasTimeoutTrackedCommandInFlight())
192 90 return true;
193
2/2
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 17 taken 2874 times.
2875 if (bytes > ImapService::MAX_COMMAND_RESPONSE_SIZE - m_commandResponseBytes) {
194
1/1
✓ Branch 13 → 14 taken 1 time.
2 m_deps.failConnection(QStringLiteral("IMAP command response byte budget exceeded"),
195 ImapService::DisconnectReason::ProtocolError);
196 1 return false;
197 }
198 2874 m_commandResponseBytes += bytes;
199 2874 return true;
200 }
201
202 100045 bool ImapCommandTable::accountCommandLiteral() {
203
2/2
✓ Branch 3 → 4 taken 100003 times.
✓ Branch 3 → 5 taken 42 times.
100045 if (!hasTimeoutTrackedCommandInFlight())
204 100003 return true;
205
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 13 taken 41 times.
42 if (m_commandLiteralCount >= ImapService::MAX_LITERALS_PER_COMMAND) {
206
1/1
✓ Branch 9 → 10 taken 1 time.
2 m_deps.failConnection(QStringLiteral("IMAP command literal budget exceeded"),
207 ImapService::DisconnectReason::ProtocolError);
208 1 return false;
209 }
210 41 ++m_commandLiteralCount;
211 41 return true;
212 }
213
214 160 bool ImapCommandTable::accountCommandResultItems(qint64 items) {
215
3/6
✓ Branch 2 → 3 taken 160 times.
✗ Branch 2 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 160 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 160 times.
160 if (items <= 0 || !hasTimeoutTrackedCommandInFlight())
216 ✗ return true;
217
2/2
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 17 taken 159 times.
160 if (items > ImapService::MAX_RESULT_ITEMS_PER_COMMAND - m_commandResultItems) {
218
1/1
✓ Branch 13 → 14 taken 1 time.
2 m_deps.failConnection(QStringLiteral("IMAP command result item budget exceeded"),
219 ImapService::DisconnectReason::ProtocolError);
220 1 return false;
221 }
222 159 m_commandResultItems += items;
223 159 return true;
224 }
225
226 451 void ImapCommandTable::sendCommand(const QString &type, const QString &command,
227 ImapPendingCommand record) {
228 const bool startsCommandBudget =
229
2/12
✓ Branch 6 → 7 taken 451 times.
✗ Branch 6 → 10 not taken.
✓ Branch 13 → 14 taken 451 times.
✗ Branch 13 → 15 not taken.
✗ Branch 70 → 71 not taken.
✗ Branch 70 → 72 not taken.
✗ Branch 74 → 75 not taken.
✗ Branch 74 → 76 not taken.
✗ Branch 81 → 82 not taken.
✗ Branch 81 → 83 not taken.
✗ Branch 85 → 86 not taken.
✗ Branch 85 → 87 not taken.
1804 type != QStringLiteral("IDLE") &&
230
4/5
✓ Branch 7 → 8 taken 451 times.
✓ Branch 8 → 9 taken 429 times.
✓ Branch 8 → 10 taken 22 times.
✓ Branch 11 → 12 taken 451 times.
✗ Branch 11 → 13 not taken.
902 !hasTimeoutTrackedCommandInFlight();
231
2/2
✓ Branch 15 → 16 taken 429 times.
✓ Branch 15 → 17 taken 22 times.
451 if (startsCommandBudget)
232 429 resetCommandAccumulators();
233
234
1/1
✓ Branch 17 → 18 taken 451 times.
451 auto tag = nextTag();
235 451 record.type = type;
236
237 // T-210: Start timing for this command
238 451 QElapsedTimer timer;
239 451 timer.start();
240 451 record.timer = timer;
241
1/1
✓ Branch 22 → 23 taken 451 times.
451 [[maybe_unused]] const auto inserted = m_pending.insert(tag, std::move(record));
242
243
3/3
✓ Branch 23 → 24 taken 451 times.
✓ Branch 24 → 25 taken 451 times.
✓ Branch 25 → 26 taken 451 times.
451 auto fullCommand = tag + " " + command + "\r\n";
244 // T-400/Bug 6: Mask credentials in LOGIN commands
245
2/2
✓ Branch 34 → 35 taken 49 times.
✓ Branch 34 → 48 taken 402 times.
451 if (type == QStringLiteral("LOGIN")) {
246
7/7
✓ Branch 35 → 36 taken 49 times.
✓ Branch 39 → 40 taken 49 times.
✓ Branch 40 → 41 taken 49 times.
✓ Branch 41 → 42 taken 49 times.
✓ Branch 42 → 43 taken 49 times.
✓ Branch 46 → 37 taken 49 times.
✓ Branch 46 → 47 taken 49 times.
98 qCDebug(lcImap) << ">>>" << tag << "LOGIN <user> <***>";
247 } else {
248
7/7
✓ Branch 48 → 49 taken 402 times.
✓ Branch 52 → 53 taken 402 times.
✓ Branch 53 → 54 taken 402 times.
✓ Branch 54 → 55 taken 402 times.
✓ Branch 55 → 56 taken 402 times.
✓ Branch 60 → 50 taken 402 times.
✓ Branch 60 → 61 taken 402 times.
804 qCDebug(lcImap) << ">>>" << fullCommand.trimmed();
249 }
250 #ifdef MAILJD_E2E_TESTING
251
3/3
✓ Branch 63 → 64 taken 374 times.
✓ Branch 68 → 69 taken 374 times.
✓ Branch 69 → 70 taken 374 times.
1122 m_deps.reportE2eTransportPhase(QStringLiteral("command-write:%1").arg(type),
252 374 m_deps.e2eRequestTokenFor(*inserted));
253 #endif
254
4/4
✓ Branch 62 → 63 taken 77 times.
✓ Branch 63 → 64 taken 77 times.
✓ Branch 73 → 74 taken 374 times.
✓ Branch 74 → 75 taken 374 times.
451 m_socket->write(fullCommand.toUtf8());
255
2/2
✓ Branch 65 → 66 taken 77 times.
✓ Branch 76 → 77 taken 374 times.
451 refreshCommandTimeout();
256
2/2
✓ Branch 66 → 67 taken 77 times.
✓ Branch 77 → 78 taken 374 times.
451 refreshCommandDeadline();
257 451 }
258
259 544 QString ImapCommandTable::nextTag() {
260
2/2
✓ Branch 2 → 3 taken 544 times.
✓ Branch 4 → 5 taken 544 times.
544 return QString("A%1").arg(++m_tagCounter, 3, 10, QChar('0'));
261 }
262
263 2 void ImapCommandTable::onCommandTimeout() {
264
2/3
✓ Branch 2 → 3 taken 2 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 2 times.
2 if (!hasTimeoutTrackedCommandInFlight()) {
265 ✗ m_commandTimeoutTimer->stop();
266 ✗ return;
267 }
268
269 2 QStringList pending;
270
4/4
✓ Branch 6 → 7 taken 2 times.
✓ Branch 28 → 29 taken 4 times.
✓ Branch 30 → 8 taken 2 times.
✓ Branch 30 → 31 taken 2 times.
4 for (auto it = m_pending.cbegin(); it != m_pending.cend(); ++it) {
271
1/2
✓ Branch 15 → 16 taken 2 times.
✗ Branch 15 → 27 not taken.
2 if (it->type != QStringLiteral("IDLE"))
272
2/2
✓ Branch 21 → 22 taken 2 times.
✓ Branch 22 → 23 taken 2 times.
2 pending.append(QStringLiteral("%1:%2").arg(it.key(), it->type));
273 }
274
275
1/1
✓ Branch 38 → 39 taken 2 times.
4 m_deps.failConnection(
276 4 QStringLiteral("IMAP command timeout (inactivity): %1")
277
2/2
✓ Branch 36 → 37 taken 2 times.
✓ Branch 37 → 38 taken 2 times.
4 .arg(pending.join(',')),
278 ImapService::DisconnectReason::CommandTimeout);
279 2 }
280
281 2 void ImapCommandTable::onCommandDeadline() {
282
2/3
✓ Branch 2 → 3 taken 2 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 2 times.
2 if (!hasTimeoutTrackedCommandInFlight()) {
283 ✗ m_commandDeadlineTimer->stop();
284 ✗ return;
285 }
286
287 2 QStringList pending;
288
4/4
✓ Branch 6 → 7 taken 2 times.
✓ Branch 28 → 29 taken 4 times.
✓ Branch 30 → 8 taken 2 times.
✓ Branch 30 → 31 taken 2 times.
4 for (auto it = m_pending.cbegin(); it != m_pending.cend(); ++it) {
289
1/2
✓ Branch 15 → 16 taken 2 times.
✗ Branch 15 → 27 not taken.
2 if (it->type != QStringLiteral("IDLE"))
290
2/2
✓ Branch 21 → 22 taken 2 times.
✓ Branch 22 → 23 taken 2 times.
2 pending.append(QStringLiteral("%1:%2").arg(it.key(), it->type));
291 }
292
293
1/1
✓ Branch 38 → 39 taken 2 times.
4 m_deps.failConnection(
294 4 QStringLiteral("IMAP command absolute deadline exceeded: %1")
295
2/2
✓ Branch 36 → 37 taken 2 times.
✓ Branch 37 → 38 taken 2 times.
4 .arg(pending.join(',')),
296 ImapService::DisconnectReason::CommandTimeout);
297 2 }
298
299 2482 void ImapCommandTable::clearCommands() {
300 2482 m_pending.clear();
301 2482 m_deferredCommands.clear();
302 2482 m_serializedCommands.clear();
303 2482 resetCommandAccumulators();
304 2482 }
305
306 100346 bool ImapCommandTable::hasCommandType(const QString &type) const {
307
4/4
✓ Branch 2 → 3 taken 100346 times.
✓ Branch 3 → 4 taken 100346 times.
✓ Branch 11 → 5 taken 315 times.
✓ Branch 11 → 12 taken 100078 times.
100393 for (const ImapPendingCommand &record : m_pending) {
308
2/2
✓ Branch 7 → 8 taken 268 times.
✓ Branch 7 → 9 taken 47 times.
315 if (record.type == type)
309 268 return true;
310 }
311 100078 return false;
312 }
313
314 5045 ImapPendingCommand *ImapCommandTable::recordOfType(const QString &type) {
315
4/4
✓ Branch 2 → 3 taken 5045 times.
✓ Branch 3 → 4 taken 5045 times.
✓ Branch 11 → 5 taken 285 times.
✓ Branch 11 → 12 taken 4947 times.
5232 for (ImapPendingCommand &record : m_pending) {
316
2/2
✓ Branch 7 → 8 taken 98 times.
✓ Branch 7 → 9 taken 187 times.
285 if (record.type == type)
317 98 return &record;
318 }
319 4947 return nullptr;
320 }
321
322 475 ImapPendingCommand ImapCommandTable::takeRecord(const QString &tag) {
323 475 return m_pending.take(tag);
324 }
325
326 13 void ImapCommandTable::insertRecord(const QString &tag, ImapPendingCommand record) {
327 13 m_pending.insert(tag, std::move(record));
328 13 }
329
330 2 void ImapCommandTable::eraseType(const QString &type) {
331
4/4
✓ Branch 2 → 3 taken 2 times.
✓ Branch 11 → 12 taken 4 times.
✓ Branch 13 → 4 taken 2 times.
✓ Branch 13 → 14 taken 2 times.
4 for (auto it = m_pending.begin(); it != m_pending.end();) {
332
1/2
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 10 not taken.
2 if (it->type == type)
333
1/1
✓ Branch 8 → 9 taken 2 times.
2 it = m_pending.erase(it);
334 else
335 ✗ ++it;
336 }
337 2 }
338