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: 94.2% 323 / 0 / 343
Functions: 97.0% 32 / 0 / 33
Branches: 83.2% 420 / 5 / 510

service/ImapMutationCommands.cpp
Line Branch Exec Source
1 #include "ImapMutationCommands.h"
2
3 #include "ImapCommandTable.h"
4 #include "ImapPushSession.h"
5
6 #include <QLoggingCategory>
7 #include <QRegularExpression>
8
9 #include <algorithm>
10 #include <limits>
11 #include <utility>
12
13 Q_DECLARE_LOGGING_CATEGORY(lcImap)
14
15 1401 ImapMutationCommands::ImapMutationCommands(ImapCommandTable &commands, ImapPushSession &push,
16 const State &state, const QString &selectedFolder,
17 const quint32 &selectedUidValidity,
18 1401 const QStringList &capabilities, Deps deps)
19 1401 : m_commands(commands), m_push(push), m_state(state), m_selectedFolder(selectedFolder),
20 1401 m_selectedUidValidity(selectedUidValidity), m_capabilities(capabilities),
21 1401 m_deps(std::move(deps)) {}
22
23 1 void ImapMutationCommands::markSeen(qint64 uid) {
24
1/1
✓ Branch 5 → 6 taken 1 time.
1 storeFlag(uid, QStringLiteral("\\Seen"), true);
25 1 }
26
27 1 void ImapMutationCommands::markUnseen(qint64 uid) {
28
1/1
✓ Branch 5 → 6 taken 1 time.
1 storeFlag(uid, QStringLiteral("\\Seen"), false);
29 1 }
30
31 40 void ImapMutationCommands::storeFlag(qint64 uid, const QString &flag, bool add,
32 qint64 mutationToken) {
33
1/1
✓ Branch 2 → 3 taken 40 times.
40 trackMutation(mutationToken);
34 // A folderSelected observer may start IDLE before a later observer dispatches
35 // its durable mutation. IDLE still owns the selected mailbox; pause it and
36 // execute the STORE after the tagged DONE response instead of rejecting a
37 // valid, folder-scoped operation.
38
5/6
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 7 taken 37 times.
✓ Branch 5 → 6 taken 3 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 3 times.
✓ Branch 8 → 16 taken 37 times.
40 if (m_state == State::Idling && m_push.isIdling()) {
39
2/4
✓ Branch 11 → 12 taken 3 times.
✓ Branch 12 → 13 taken 3 times.
✗ Branch 114 → 115 not taken.
✗ Branch 114 → 116 not taken.
3 m_commands.executeAfterIdle([this, uid, flag, add, mutationToken,
40 issuedFolder = m_selectedFolder]() {
41
3/4
✓ Branch 2 → 3 taken 2 times.
✓ Branch 3 → 4 taken 2 times.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
2 if (m_commands.queuedMailboxStillSelected(issuedFolder, {mutationToken}, "storeFlag"))
42 2 storeFlag(uid, flag, add, mutationToken);
43 2 });
44 20 return;
45 }
46
2/2
✓ Branch 16 → 17 taken 3 times.
✓ Branch 16 → 43 taken 34 times.
37 if (m_state != State::Selected) {
47
5/5
✓ Branch 17 → 18 taken 3 times.
✓ Branch 21 → 22 taken 3 times.
✓ Branch 22 → 23 taken 3 times.
✓ Branch 33 → 19 taken 3 times.
✓ Branch 33 → 34 taken 3 times.
6 qCWarning(lcImap) << "Cannot store flag: no folder selected, state:"
48
3/3
✓ Branch 23 → 24 taken 3 times.
✓ Branch 24 → 25 taken 3 times.
✓ Branch 25 → 26 taken 3 times.
3 << static_cast<int>(m_state) << "UID:" << uid
49
4/4
✓ Branch 26 → 27 taken 3 times.
✓ Branch 27 → 28 taken 3 times.
✓ Branch 28 → 29 taken 3 times.
✓ Branch 29 → 30 taken 3 times.
3 << "flag:" << flag << "add:" << add;
50
1/1
✓ Branch 38 → 39 taken 3 times.
6 completeMutation(mutationToken, MutationOutcome::Rejected,
51 6 QStringLiteral("No folder selected"));
52 3 return;
53 }
54
2/3
✓ Branch 43 → 44 taken 34 times.
✗ Branch 44 → 45 not taken.
✓ Branch 44 → 46 taken 34 times.
34 if (!m_push.probeBeforeUse("STORE"))
55 ✗ return; // failConnection() cancelled the token before dispatch
56
3/3
✓ Branch 46 → 47 taken 34 times.
✓ Branch 47 → 48 taken 10 times.
✓ Branch 47 → 55 taken 24 times.
34 if (m_commands.hasStatefulCommandInFlight()) {
57
2/4
✓ Branch 50 → 51 taken 10 times.
✓ Branch 51 → 52 taken 10 times.
✗ Branch 137 → 138 not taken.
✗ Branch 137 → 139 not taken.
10 m_commands.enqueueSerializedCommand([this, uid, flag, add, mutationToken,
58 issuedFolder = m_selectedFolder]() {
59
4/4
✓ Branch 2 → 3 taken 5 times.
✓ Branch 3 → 4 taken 5 times.
✓ Branch 5 → 6 taken 3 times.
✓ Branch 5 → 7 taken 2 times.
5 if (m_commands.queuedMailboxStillSelected(issuedFolder, {mutationToken}, "storeFlag"))
60 3 storeFlag(uid, flag, add, mutationToken);
61 5 });
62 10 return;
63 }
64
65 // Reject malformed tokens instead of rewriting them into a different flag.
66 // In particular, this blocks a server-provided keyword from escaping the
67 // parenthesized STORE flag list.
68
3/3
✓ Branch 55 → 56 taken 24 times.
✓ Branch 56 → 57 taken 4 times.
✓ Branch 56 → 76 taken 20 times.
24 if (!ImapService::isValidImapFlag(flag)) {
69
5/5
✓ Branch 57 → 58 taken 4 times.
✓ Branch 61 → 62 taken 4 times.
✓ Branch 62 → 63 taken 4 times.
✓ Branch 66 → 59 taken 4 times.
✓ Branch 66 → 67 taken 4 times.
8 qCWarning(lcImap) << "Refusing malformed IMAP flag token";
70
1/1
✓ Branch 71 → 72 taken 4 times.
8 completeMutation(mutationToken, MutationOutcome::Rejected,
71 8 QStringLiteral("Invalid IMAP flag"));
72 4 return;
73 }
74
75
6/6
✓ Branch 76 → 77 taken 17 times.
✓ Branch 76 → 80 taken 3 times.
✓ Branch 83 → 84 taken 3 times.
✓ Branch 83 → 85 taken 17 times.
✓ Branch 85 → 86 taken 17 times.
✓ Branch 85 → 87 taken 3 times.
40 QString op = add ? QStringLiteral("+FLAGS") : QStringLiteral("-FLAGS");
76 20 ImapPendingCommand store;
77 20 store.storeToken = mutationToken;
78
2/2
✓ Branch 96 → 97 taken 20 times.
✓ Branch 97 → 98 taken 20 times.
40 m_commands.sendCommand("STORE",
79
4/4
✓ Branch 89 → 90 taken 20 times.
✓ Branch 91 → 92 taken 20 times.
✓ Branch 93 → 94 taken 20 times.
✓ Branch 95 → 96 taken 20 times.
100 QString("UID STORE %1 %2 (%3)").arg(uid).arg(op).arg(flag),
80 20 std::move(store));
81
1/1
✓ Branch 104 → 105 taken 20 times.
20 markMutationDispatched(mutationToken);
82 20 }
83
84 7 void ImapMutationCommands::moveMessage(qint64 uid, const QString &targetFolder,
85 qint64 mutationToken) {
86
2/2
✓ Branch 2 → 3 taken 7 times.
✓ Branch 3 → 4 taken 7 times.
7 moveMessages({uid}, targetFolder, mutationToken);
87 7 }
88
89 25 void ImapMutationCommands::moveMessages(const QList<qint64> &uids,
90 const QString &targetFolder,
91 qint64 mutationToken) {
92
1/1
✓ Branch 2 → 3 taken 25 times.
25 trackMutation(mutationToken);
93
5/6
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 7 taken 24 times.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 17 taken 24 times.
25 if (m_state == State::Idling && m_push.isIdling()) {
94
2/6
✓ Branch 12 → 13 taken 1 time.
✓ Branch 13 → 14 taken 1 time.
✗ Branch 114 → 115 not taken.
✗ Branch 114 → 116 not taken.
✗ Branch 117 → 118 not taken.
✗ Branch 117 → 119 not taken.
1 m_commands.executeAfterIdle([this, uids, targetFolder, mutationToken,
95 issuedFolder = m_selectedFolder]() {
96
3/4
✓ Branch 2 → 3 taken 1 time.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
1 if (m_commands.queuedMailboxStillSelected(issuedFolder, {mutationToken}, "moveMessages"))
97 1 moveMessages(uids, targetFolder, mutationToken);
98 else
99 ✗ m_deps.moveError(QStringLiteral("No folder selected"));
100 1 });
101 13 return;
102 }
103
2/2
✓ Branch 17 → 18 taken 6 times.
✓ Branch 17 → 43 taken 18 times.
24 if (m_state != State::Selected) {
104
5/5
✓ Branch 18 → 19 taken 6 times.
✓ Branch 22 → 23 taken 6 times.
✓ Branch 23 → 24 taken 6 times.
✓ Branch 27 → 20 taken 6 times.
✓ Branch 27 → 28 taken 6 times.
12 qCWarning(lcImap) << "Cannot move messages: no folder selected";
105
1/1
✓ Branch 31 → 32 taken 6 times.
12 m_deps.moveError(QStringLiteral("No folder selected"));
106
1/1
✓ Branch 38 → 39 taken 6 times.
12 completeMutation(mutationToken, MutationOutcome::Rejected,
107 12 QStringLiteral("No folder selected"));
108 6 return;
109 }
110
3/3
✓ Branch 43 → 44 taken 18 times.
✓ Branch 44 → 45 taken 1 time.
✓ Branch 44 → 46 taken 17 times.
18 if (!m_push.probeBeforeUse("MOVE"))
111 1 return; // failConnection() cancelled the token before dispatch
112
3/3
✓ Branch 46 → 47 taken 17 times.
✓ Branch 47 → 48 taken 5 times.
✓ Branch 47 → 56 taken 12 times.
17 if (m_commands.hasStatefulCommandInFlight()) {
113
2/6
✓ Branch 51 → 52 taken 5 times.
✓ Branch 52 → 53 taken 5 times.
✗ Branch 146 → 147 not taken.
✗ Branch 146 → 148 not taken.
✗ Branch 149 → 150 not taken.
✗ Branch 149 → 151 not taken.
5 m_commands.enqueueSerializedCommand([this, uids, targetFolder, mutationToken,
114 issuedFolder = m_selectedFolder]() {
115
4/4
✓ Branch 2 → 3 taken 3 times.
✓ Branch 3 → 4 taken 3 times.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 2 times.
3 if (m_commands.queuedMailboxStillSelected(issuedFolder, {mutationToken}, "moveMessages"))
116 1 moveMessages(uids, targetFolder, mutationToken);
117 else
118
1/1
✓ Branch 10 → 11 taken 2 times.
4 m_deps.moveError(QStringLiteral("No folder selected"));
119 3 });
120 5 return;
121 }
122
123 12 ImapPendingCommand move;
124 12 move.moveUids = uids;
125 12 move.moveTarget = targetFolder;
126
1/1
✓ Branch 58 → 59 taken 12 times.
12 move.mutationTokens = {mutationToken};
127
128 // Build UID set string: "100,200,300"
129 12 QStringList uidStrs;
130
2/2
✓ Branch 67 → 61 taken 16 times.
✓ Branch 67 → 68 taken 12 times.
28 for (qint64 u : uids)
131
2/2
✓ Branch 62 → 63 taken 16 times.
✓ Branch 63 → 64 taken 16 times.
16 uidStrs.append(QString::number(u));
132
1/1
✓ Branch 69 → 70 taken 12 times.
12 auto uidSet = uidStrs.join(',');
133
134
2/2
✓ Branch 76 → 77 taken 6 times.
✓ Branch 76 → 90 taken 6 times.
24 if (m_capabilities.contains(QStringLiteral("MOVE"), Qt::CaseInsensitive)) {
135
2/2
✓ Branch 82 → 83 taken 6 times.
✓ Branch 83 → 84 taken 6 times.
18 m_commands.sendCommand("MOVE",
136
3/3
✓ Branch 79 → 80 taken 6 times.
✓ Branch 80 → 81 taken 6 times.
✓ Branch 81 → 82 taken 6 times.
12 QString("UID MOVE %1 %2").arg(uidSet, ImapService::quoteMailboxName(targetFolder)),
137 6 std::move(move));
138 } else {
139
2/2
✓ Branch 95 → 96 taken 6 times.
✓ Branch 96 → 97 taken 6 times.
18 m_commands.sendCommand("COPY",
140
3/3
✓ Branch 92 → 93 taken 6 times.
✓ Branch 93 → 94 taken 6 times.
✓ Branch 94 → 95 taken 6 times.
12 QString("UID COPY %1 %2").arg(uidSet, ImapService::quoteMailboxName(targetFolder)),
141 6 std::move(move));
142 }
143
1/1
✓ Branch 103 → 104 taken 12 times.
12 markMutationDispatched(mutationToken);
144 12 }
145
146 // F4.3: the journal dispatches one UID MOVE uid-set for a batch of records
147 // that share source folder, target and epoch. Every token is tracked and
148 // completes with the single terminal response of the shared command.
149 10 void ImapMutationCommands::moveMessageSet(const QList<qint64> &uids,
150 const QString &targetFolder,
151 const QList<qint64> &mutationTokens) {
152
2/2
✓ Branch 8 → 4 taken 27 times.
✓ Branch 8 → 9 taken 10 times.
37 for (qint64 token : mutationTokens)
153
1/1
✓ Branch 5 → 6 taken 27 times.
27 trackMutation(token);
154
3/6
✓ Branch 11 → 12 taken 10 times.
✗ Branch 11 → 14 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 10 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 32 taken 10 times.
10 if (uids.size() != mutationTokens.size() || uids.isEmpty()) {
155 ✗ for (qint64 token : mutationTokens)
156 ✗ completeMutation(token, MutationOutcome::Rejected,
157 ✗ QStringLiteral("Invalid batched move request"));
158 1 return;
159 }
160
2/6
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 36 taken 10 times.
✗ Branch 34 → 35 not taken.
✗ Branch 34 → 36 not taken.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 47 taken 10 times.
10 if (m_state == State::Idling && m_push.isIdling()) {
161 ✗ m_commands.executeAfterIdle([this, uids, targetFolder, mutationTokens,
162 issuedFolder = m_selectedFolder]() {
163 ✗ if (m_commands.queuedMailboxStillSelected(issuedFolder, mutationTokens, "moveMessageSet"))
164 ✗ moveMessageSet(uids, targetFolder, mutationTokens);
165 else
166 ✗ m_deps.moveError(QStringLiteral("No folder selected"));
167 ✗ });
168 ✗ return;
169 }
170
1/2
✗ Branch 47 → 48 not taken.
✓ Branch 47 → 79 taken 10 times.
10 if (m_state != State::Selected) {
171 ✗ qCWarning(lcImap) << "Cannot move messages: no folder selected";
172 ✗ m_deps.moveError(QStringLiteral("No folder selected"));
173 ✗ for (qint64 token : mutationTokens)
174 ✗ completeMutation(token, MutationOutcome::Rejected,
175 ✗ QStringLiteral("No folder selected"));
176 ✗ return;
177 }
178
2/3
✓ Branch 79 → 80 taken 10 times.
✗ Branch 80 → 81 not taken.
✓ Branch 80 → 82 taken 10 times.
10 if (!m_push.probeBeforeUse("MOVE"))
179 ✗ return; // failConnection() cancelled the token before dispatch
180
3/3
✓ Branch 82 → 83 taken 10 times.
✓ Branch 83 → 84 taken 1 time.
✓ Branch 83 → 93 taken 9 times.
10 if (m_commands.hasStatefulCommandInFlight()) {
181
2/8
✓ Branch 88 → 89 taken 1 time.
✓ Branch 89 → 90 taken 1 time.
✗ Branch 204 → 205 not taken.
✗ Branch 204 → 206 not taken.
✗ Branch 207 → 208 not taken.
✗ Branch 207 → 209 not taken.
✗ Branch 210 → 211 not taken.
✗ Branch 210 → 212 not taken.
1 m_commands.enqueueSerializedCommand([this, uids, targetFolder, mutationTokens,
182 issuedFolder = m_selectedFolder]() {
183
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 5 not taken.
1 if (m_commands.queuedMailboxStillSelected(issuedFolder, mutationTokens, "moveMessageSet"))
184 1 moveMessageSet(uids, targetFolder, mutationTokens);
185 else
186 ✗ m_deps.moveError(QStringLiteral("No folder selected"));
187 1 });
188 1 return;
189 }
190
191 9 ImapPendingCommand move;
192 9 move.moveUids = uids;
193 9 move.moveTarget = targetFolder;
194 9 move.mutationTokens = mutationTokens;
195
196 // Build UID set string: "100,200,300"
197 9 QStringList uidStrs;
198
2/2
✓ Branch 104 → 98 taken 25 times.
✓ Branch 104 → 105 taken 9 times.
34 for (qint64 u : uids)
199
2/2
✓ Branch 99 → 100 taken 25 times.
✓ Branch 100 → 101 taken 25 times.
25 uidStrs.append(QString::number(u));
200
1/1
✓ Branch 106 → 107 taken 9 times.
9 auto uidSet = uidStrs.join(',');
201
202
2/2
✓ Branch 113 → 114 taken 6 times.
✓ Branch 113 → 127 taken 3 times.
18 if (m_capabilities.contains(QStringLiteral("MOVE"), Qt::CaseInsensitive)) {
203
2/2
✓ Branch 119 → 120 taken 6 times.
✓ Branch 120 → 121 taken 6 times.
18 m_commands.sendCommand("MOVE",
204
3/3
✓ Branch 116 → 117 taken 6 times.
✓ Branch 117 → 118 taken 6 times.
✓ Branch 118 → 119 taken 6 times.
12 QString("UID MOVE %1 %2").arg(uidSet, ImapService::quoteMailboxName(targetFolder)),
205 6 std::move(move));
206 } else {
207
2/2
✓ Branch 132 → 133 taken 3 times.
✓ Branch 133 → 134 taken 3 times.
9 m_commands.sendCommand("COPY",
208
3/3
✓ Branch 129 → 130 taken 3 times.
✓ Branch 130 → 131 taken 3 times.
✓ Branch 131 → 132 taken 3 times.
6 QString("UID COPY %1 %2").arg(uidSet, ImapService::quoteMailboxName(targetFolder)),
209 3 std::move(move));
210 }
211
2/2
✓ Branch 146 → 142 taken 25 times.
✓ Branch 146 → 147 taken 9 times.
34 for (qint64 token : mutationTokens)
212
1/1
✓ Branch 143 → 144 taken 25 times.
25 markMutationDispatched(token);
213 9 }
214
215 // F4.3: complete every journal token riding the pending MOVE/COPY command
216 // with one shared terminal outcome.
217 28 void ImapMutationCommands::completePendingMoveMutations(const ImapPendingCommand &command,
218 MutationOutcome outcome,
219 const QString &error,
220 const QString &uidPlusEvidence) {
221
2/2
✓ Branch 8 → 4 taken 28 times.
✓ Branch 8 → 9 taken 28 times.
56 for (qint64 token : command.mutationTokens)
222
1/1
✓ Branch 5 → 6 taken 28 times.
28 completeMutation(token, outcome, error, uidPlusEvidence);
223 28 }
224
225 9 void ImapMutationCommands::copyMessage(qint64 uid, const QString &targetFolder,
226 qint64 mutationToken) {
227
1/1
✓ Branch 2 → 3 taken 9 times.
9 trackMutation(mutationToken);
228
5/6
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 7 taken 8 times.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 16 taken 8 times.
9 if (m_state == State::Idling && m_push.isIdling()) {
229
2/4
✓ Branch 11 → 12 taken 1 time.
✓ Branch 12 → 13 taken 1 time.
✗ Branch 76 → 77 not taken.
✗ Branch 76 → 78 not taken.
1 m_commands.executeAfterIdle([this, uid, targetFolder, mutationToken,
230 issuedFolder = m_selectedFolder]() {
231
3/4
✓ Branch 2 → 3 taken 1 time.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
1 if (m_commands.queuedMailboxStillSelected(issuedFolder, {mutationToken}, "copyMessage"))
232 1 copyMessage(uid, targetFolder, mutationToken);
233 1 });
234 5 return;
235 }
236
2/2
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 36 taken 7 times.
8 if (m_state != State::Selected) {
237 2 qCWarning(lcImap) << "Cannot copy message: no folder selected"; // GCOVR_EXCL_BR_LINE — Qt logging-macro cleanup only; folder guard remains measured.
238
1/1
✓ Branch 31 → 32 taken 1 time.
2 completeMutation(mutationToken, MutationOutcome::Rejected,
239 2 QStringLiteral("No folder selected"));
240 1 return;
241 }
242
2/3
✓ Branch 36 → 37 taken 7 times.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 7 times.
7 if (!m_push.probeBeforeUse("COPY"))
243 ✗ return; // failConnection() cancelled the token before dispatch
244
3/3
✓ Branch 39 → 40 taken 7 times.
✓ Branch 40 → 41 taken 3 times.
✓ Branch 40 → 48 taken 4 times.
7 if (m_commands.hasStatefulCommandInFlight()) {
245
2/4
✓ Branch 43 → 44 taken 3 times.
✓ Branch 44 → 45 taken 3 times.
✗ Branch 99 → 100 not taken.
✗ Branch 99 → 101 not taken.
3 m_commands.enqueueSerializedCommand([this, uid, targetFolder, mutationToken,
246 issuedFolder = m_selectedFolder]() {
247
3/4
✓ Branch 2 → 3 taken 2 times.
✓ Branch 3 → 4 taken 2 times.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
2 if (m_commands.queuedMailboxStillSelected(issuedFolder, {mutationToken}, "copyMessage"))
248 2 copyMessage(uid, targetFolder, mutationToken);
249 2 });
250 3 return;
251 }
252
253 4 ImapPendingCommand copy;
254
1/1
✓ Branch 48 → 49 taken 4 times.
4 copy.moveUids = {uid};
255 4 copy.moveTarget = targetFolder;
256
1/1
✓ Branch 50 → 51 taken 4 times.
4 copy.mutationTokens = {mutationToken};
257
2/2
✓ Branch 59 → 60 taken 4 times.
✓ Branch 60 → 61 taken 4 times.
8 m_commands.sendCommand("COPY_ONLY",
258
4/4
✓ Branch 53 → 54 taken 4 times.
✓ Branch 55 → 56 taken 4 times.
✓ Branch 57 → 58 taken 4 times.
✓ Branch 58 → 59 taken 4 times.
16 QString("UID COPY %1 %2").arg(uid).arg(ImapService::quoteMailboxName(targetFolder)),
259 4 std::move(copy));
260
1/1
✓ Branch 67 → 68 taken 4 times.
4 markMutationDispatched(mutationToken);
261 4 }
262
263 24 bool ImapMutationCommands::hasUidExpungeCapability() const {
264
5/6
✓ Branch 6 → 7 taken 9 times.
✓ Branch 6 → 12 taken 15 times.
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 13 taken 7 times.
✓ Branch 20 → 21 taken 24 times.
✗ Branch 20 → 22 not taken.
81 return m_capabilities.contains(QStringLiteral("UIDPLUS"), Qt::CaseInsensitive) ||
265
5/6
✓ Branch 14 → 15 taken 9 times.
✓ Branch 14 → 16 taken 15 times.
✓ Branch 16 → 17 taken 9 times.
✓ Branch 16 → 18 taken 15 times.
✓ Branch 18 → 19 taken 24 times.
✗ Branch 18 → 20 not taken.
42 m_capabilities.contains(QStringLiteral("IMAP4rev2"), Qt::CaseInsensitive);
266 }
267
268 15 void ImapMutationCommands::expungeUids(const QList<qint64> &uids, const QString &folder,
269 quint32 uidValidity) {
270
1/2
✓ Branch 4 → 5 taken 15 times.
✗ Branch 4 → 11 not taken.
15 if (m_state != State::Selected || folder.isEmpty() ||
271
2/4
✓ Branch 6 → 7 taken 15 times.
✗ Branch 6 → 11 not taken.
✓ Branch 7 → 8 taken 15 times.
✗ Branch 7 → 11 not taken.
15 m_selectedFolder != folder || uidValidity == 0 ||
272
8/9
✓ Branch 2 → 3 taken 15 times.
✗ Branch 2 → 11 not taken.
✓ Branch 8 → 9 taken 14 times.
✓ Branch 8 → 11 taken 1 time.
✓ Branch 9 → 10 taken 14 times.
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 13 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 21 taken 13 times.
30 m_selectedUidValidity != uidValidity || !hasUidExpungeCapability()) {
273
1/1
✓ Branch 17 → 18 taken 2 times.
4 m_deps.expungeFailed(QStringLiteral("UID EXPUNGE mailbox identity or capability changed"));
274 9 return;
275 }
276
6/7
✓ Branch 22 → 23 taken 13 times.
✗ Branch 22 → 27 not taken.
✓ Branch 25 → 26 taken 13 times.
✓ Branch 26 → 27 taken 1 time.
✓ Branch 26 → 28 taken 12 times.
✓ Branch 29 → 30 taken 1 time.
✓ Branch 29 → 37 taken 12 times.
13 if (uids.isEmpty() || std::any_of(uids.cbegin(), uids.cend(), [](qint64 uid) {
277
3/4
✓ Branch 2 → 3 taken 16 times.
✓ Branch 2 → 5 taken 1 time.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 16 times.
17 return uid <= 0 || quint64(uid) > std::numeric_limits<quint32>::max();
278 })) {
279
1/1
✓ Branch 33 → 34 taken 1 time.
2 m_deps.expungeFailed(QStringLiteral("Invalid UID EXPUNGE identifiers"));
280 1 return;
281 }
282
3/3
✓ Branch 37 → 38 taken 12 times.
✓ Branch 38 → 39 taken 6 times.
✓ Branch 38 → 46 taken 6 times.
12 if (m_commands.hasStatefulCommandInFlight()) {
283
2/6
✓ Branch 41 → 42 taken 6 times.
✓ Branch 42 → 43 taken 6 times.
✗ Branch 92 → 93 not taken.
✗ Branch 92 → 94 not taken.
✗ Branch 95 → 96 not taken.
✗ Branch 95 → 97 not taken.
6 m_commands.enqueueSerializedCommand([this, uids, folder, uidValidity] {
284 3 expungeUids(uids, folder, uidValidity);
285 3 });
286 6 return;
287 }
288 6 QStringList values;
289
4/4
✓ Branch 49 → 50 taken 9 times.
✓ Branch 50 → 51 taken 9 times.
✓ Branch 54 → 48 taken 9 times.
✓ Branch 54 → 55 taken 6 times.
15 for (qint64 uid : uids) values.append(QString::number(uid));
290
1/1
✓ Branch 55 → 56 taken 6 times.
6 values.removeDuplicates();
291
4/4
✓ Branch 62 → 63 taken 6 times.
✓ Branch 63 → 64 taken 6 times.
✓ Branch 64 → 65 taken 6 times.
✓ Branch 65 → 66 taken 6 times.
18 m_commands.sendCommand("EXPUNGE", QStringLiteral("UID EXPUNGE %1").arg(values.join(QLatin1Char(','))));
292 6 }
293
294 149 void ImapMutationCommands::trackMutation(qint64 token) {
295
2/2
✓ Branch 2 → 3 taken 88 times.
✓ Branch 2 → 5 taken 61 times.
149 if (token > 0)
296
1/1
✓ Branch 3 → 4 taken 88 times.
88 m_knownMutationTokens.insert(token);
297 149 }
298
299 96 void ImapMutationCommands::markMutationDispatched(qint64 token) {
300
2/2
✓ Branch 2 → 3 taken 40 times.
✓ Branch 2 → 4 taken 56 times.
96 if (token <= 0)
301 40 return;
302
1/1
✓ Branch 4 → 5 taken 56 times.
56 m_knownMutationTokens.insert(token);
303
1/1
✓ Branch 5 → 6 taken 56 times.
56 m_dispatchedMutationTokens.insert(token);
304 56 m_deps.mutationDispatched(token);
305 }
306
307 132 void ImapMutationCommands::completeMutation(qint64 token, MutationOutcome outcome,
308 const QString &error,
309 const QString &uidPlusEvidence) {
310
5/6
✓ Branch 2 → 3 taken 79 times.
✓ Branch 2 → 5 taken 53 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 79 times.
✓ Branch 7 → 8 taken 53 times.
✓ Branch 7 → 9 taken 79 times.
132 if (token <= 0 || !m_knownMutationTokens.remove(token))
311 53 return;
312 79 m_dispatchedMutationTokens.remove(token);
313 79 m_deps.mutationCompleted(token, outcome, error, uidPlusEvidence);
314 }
315
316 2484 void ImapMutationCommands::completeOutstandingMutations(const QString &error) {
317 2484 const auto tokens = m_knownMutationTokens;
318
2/2
✓ Branch 15 → 5 taken 23 times.
✓ Branch 15 → 16 taken 2484 times.
2507 for (qint64 token : tokens) {
319 23 const MutationOutcome outcome = m_dispatchedMutationTokens.contains(token)
320
2/2
✓ Branch 7 → 8 taken 18 times.
✓ Branch 7 → 9 taken 5 times.
23 ? MutationOutcome::Ambiguous
321 23 : MutationOutcome::CancelledBeforeDispatch;
322
1/1
✓ Branch 11 → 12 taken 23 times.
23 completeMutation(token, outcome, error);
323 }
324 2484 }
325 15 void ImapMutationCommands::handleTaggedStore(const TaggedResponse &response,
326 ImapPendingCommand &command) {
327 15 const qint64 mutationToken = command.storeToken;
328
2/2
✓ Branch 3 → 4 taken 12 times.
✓ Branch 3 → 11 taken 3 times.
15 if (response.status == "OK") {
329 12 m_deps.storeComplete();
330
1/1
✓ Branch 7 → 8 taken 12 times.
12 completeMutation(mutationToken, MutationOutcome::Confirmed);
331 } else {
332
2/2
✓ Branch 11 → 12 taken 3 times.
✓ Branch 12 → 13 taken 3 times.
3 m_deps.errorOccurred("STORE failed: " + response.message);
333 6 completeMutation(mutationToken, MutationOutcome::Rejected,
334
1/1
✓ Branch 15 → 16 taken 3 times.
3 response.message);
335 }
336 // Drain remaining deferred commands, then restart IDLE/NOTIFY
337
2/2
✓ Branch 20 → 21 taken 2 times.
✓ Branch 20 → 22 taken 13 times.
15 if (!m_commands.deferredCommands().isEmpty()) {
338 2 m_commands.runNextDeferredCommand();
339 } else {
340 13 m_push.restartPush();
341 }
342 15 }
343
344 // The [COPYUID uidvalidity source-set target-set] response code (RFC 4315),
345 // or "" when the server sent none.
346 21 static QString copyUidEvidence(const QString &message) {
347 static const QRegularExpression copyUidRx(
348 6 QStringLiteral(R"(\[COPYUID\s+\d+\s+[^\s\]]+\s+[^\s\]]+\])"),
349
4/7
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 15 taken 18 times.
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 15 not taken.
✓ Branch 9 → 10 taken 3 times.
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 34 not taken.
27 QRegularExpression::CaseInsensitiveOption);
350
1/1
✓ Branch 16 → 17 taken 21 times.
21 const auto match = copyUidRx.match(message);
351
4/4
✓ Branch 17 → 18 taken 21 times.
✓ Branch 18 → 19 taken 12 times.
✓ Branch 18 → 20 taken 9 times.
✓ Branch 19 → 21 taken 12 times.
42 return match.hasMatch() ? match.captured(0) : QString();
352 21 }
353
354 16 void ImapMutationCommands::handleTaggedMove(const TaggedResponse &response,
355 ImapPendingCommand &command) {
356
2/2
✓ Branch 3 → 4 taken 14 times.
✓ Branch 3 → 33 taken 2 times.
16 if (response.status == "OK") {
357
6/6
✓ Branch 4 → 5 taken 14 times.
✓ Branch 8 → 9 taken 14 times.
✓ Branch 9 → 10 taken 14 times.
✓ Branch 11 → 12 taken 14 times.
✓ Branch 17 → 6 taken 14 times.
✓ Branch 17 → 18 taken 14 times.
28 qCInfo(lcImap) << "MOVE complete:" << command.moveUids.size()
358
2/2
✓ Branch 12 → 13 taken 14 times.
✓ Branch 13 → 14 taken 14 times.
14 << "UIDs to" << command.moveTarget;
359
4/4
✓ Branch 18 → 19 taken 14 times.
✓ Branch 19 → 20 taken 14 times.
✓ Branch 25 → 21 taken 25 times.
✓ Branch 25 → 26 taken 14 times.
39 for (qint64 uid : command.moveUids)
360
1/1
✓ Branch 22 → 23 taken 25 times.
25 m_deps.messageMoved(uid, command.moveTarget);
361 14 m_deps.messagesMoved(command.moveUids, command.moveTarget);
362
1/1
✓ Branch 29 → 30 taken 14 times.
14 completePendingMoveMutations(command, MutationOutcome::Confirmed, {},
363
1/1
✓ Branch 27 → 28 taken 14 times.
28 copyUidEvidence(response.message));
364 } else {
365
6/6
✓ Branch 33 → 34 taken 2 times.
✓ Branch 37 → 38 taken 2 times.
✓ Branch 38 → 39 taken 2 times.
✓ Branch 39 → 40 taken 2 times.
✓ Branch 43 → 35 taken 2 times.
✓ Branch 43 → 44 taken 2 times.
4 qCWarning(lcImap) << "MOVE failed:" << response.message;
366 2 m_deps.moveError(response.message);
367 4 completePendingMoveMutations(command, MutationOutcome::Rejected,
368
1/1
✓ Branch 46 → 47 taken 2 times.
2 response.message);
369 }
370
2/2
✓ Branch 51 → 52 taken 2 times.
✓ Branch 51 → 53 taken 14 times.
16 if (!m_commands.deferredCommands().isEmpty()) {
371 2 m_commands.runNextDeferredCommand();
372 } else {
373 14 m_push.restartPush();
374 }
375 16 }
376
377 8 void ImapMutationCommands::handleTaggedCopy(const TaggedResponse &response,
378 ImapPendingCommand &command) {
379
2/2
✓ Branch 3 → 4 taken 7 times.
✓ Branch 3 → 45 taken 1 time.
8 if (response.status == "OK") {
380
5/5
✓ Branch 4 → 5 taken 7 times.
✓ Branch 8 → 9 taken 7 times.
✓ Branch 9 → 10 taken 7 times.
✓ Branch 13 → 6 taken 7 times.
✓ Branch 13 → 14 taken 7 times.
14 qCInfo(lcImap) << "COPY (move-fallback) complete, marking deleted...";
381 // Step 2: mark originals as \Deleted
382 7 QStringList uidStrs;
383
4/4
✓ Branch 14 → 15 taken 7 times.
✓ Branch 15 → 16 taken 7 times.
✓ Branch 23 → 17 taken 11 times.
✓ Branch 23 → 24 taken 7 times.
18 for (qint64 u : command.moveUids)
384
2/2
✓ Branch 18 → 19 taken 11 times.
✓ Branch 19 → 20 taken 11 times.
11 uidStrs.append(QString::number(u));
385
1/1
✓ Branch 24 → 25 taken 7 times.
7 ImapPendingCommand storeDelete = command;
386
1/1
✓ Branch 25 → 26 taken 7 times.
7 storeDelete.copyUidEvidence = copyUidEvidence(response.message);
387
2/2
✓ Branch 35 → 36 taken 7 times.
✓ Branch 36 → 37 taken 7 times.
14 m_commands.sendCommand("STORE_DELETE",
388
3/3
✓ Branch 30 → 31 taken 7 times.
✓ Branch 33 → 34 taken 7 times.
✓ Branch 34 → 35 taken 7 times.
21 QString("UID STORE %1 +FLAGS (\\Deleted)").arg(uidStrs.join(',')),
389 7 std::move(storeDelete));
390 7 } else {
391
6/6
✓ Branch 45 → 46 taken 1 time.
✓ Branch 49 → 50 taken 1 time.
✓ Branch 50 → 51 taken 1 time.
✓ Branch 51 → 52 taken 1 time.
✓ Branch 55 → 47 taken 1 time.
✓ Branch 55 → 56 taken 1 time.
2 qCWarning(lcImap) << "COPY failed:" << response.message;
392 1 m_deps.moveError(response.message);
393 2 completePendingMoveMutations(command, MutationOutcome::Rejected,
394
1/1
✓ Branch 58 → 59 taken 1 time.
1 response.message);
395 1 m_push.restartPush();
396 }
397 8 }
398
399 8 void ImapMutationCommands::handleTaggedStoreDelete(const TaggedResponse &response,
400 ImapPendingCommand &command) {
401
2/2
✓ Branch 3 → 4 taken 6 times.
✓ Branch 3 → 50 taken 2 times.
8 if (response.status == "OK") {
402
5/5
✓ Branch 4 → 5 taken 6 times.
✓ Branch 8 → 9 taken 6 times.
✓ Branch 9 → 10 taken 6 times.
✓ Branch 13 → 6 taken 6 times.
✓ Branch 13 → 14 taken 6 times.
12 qCInfo(lcImap) << "Marked deleted, expunging...";
403 // Sprint 102 S102.5.1: with UIDPLUS remove exactly the moved UIDs; a
404 // plain EXPUNGE also removes messages another client only marked
405 // \Deleted. Servers without UIDPLUS (and without MOVE) have no other way.
406 6 QString expunge = QStringLiteral("EXPUNGE");
407
3/3
✓ Branch 18 → 19 taken 6 times.
✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 43 taken 5 times.
6 if (hasUidExpungeCapability()) {
408 1 QStringList uidStrs;
409
4/4
✓ Branch 20 → 21 taken 1 time.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 29 → 23 taken 2 times.
✓ Branch 29 → 30 taken 1 time.
3 for (qint64 u : command.moveUids)
410
2/2
✓ Branch 24 → 25 taken 2 times.
✓ Branch 25 → 26 taken 2 times.
2 uidStrs.append(QString::number(u));
411
2/2
✓ Branch 31 → 32 taken 1 time.
✓ Branch 35 → 36 taken 1 time.
2 expunge = QStringLiteral("UID EXPUNGE ") + uidStrs.join(',');
412 1 }
413
3/3
✓ Branch 43 → 44 taken 6 times.
✓ Branch 44 → 45 taken 6 times.
✓ Branch 45 → 46 taken 6 times.
6 m_commands.sendCommand("EXPUNGE_MOVE", expunge, command);
414 6 } else {
415 // S106-003: the COPY already succeeded — the message exists in the
416 // target. "Rejected" made the pipeline restore the source row and a
417 // retry duplicated the message; the outcome is ambiguous and goes to
418 // the Message-ID reconciliation with the COPYUID evidence.
419
6/6
✓ Branch 50 → 51 taken 2 times.
✓ Branch 54 → 55 taken 2 times.
✓ Branch 55 → 56 taken 2 times.
✓ Branch 56 → 57 taken 2 times.
✓ Branch 60 → 52 taken 2 times.
✓ Branch 60 → 61 taken 2 times.
4 qCWarning(lcImap) << "STORE DELETE failed after a successful COPY:" << response.message;
420 2 m_deps.moveError(response.message);
421
1/1
✓ Branch 66 → 67 taken 2 times.
2 completePendingMoveMutations(command, MutationOutcome::Ambiguous,
422 4 QStringLiteral("Copied, but the original could not be removed: ") +
423
1/1
✓ Branch 65 → 66 taken 2 times.
2 response.message,
424 2 command.copyUidEvidence);
425 2 m_push.restartPush();
426 }
427 8 }
428
429 9 void ImapMutationCommands::handleTaggedExpungeMove(const TaggedResponse &response,
430 ImapPendingCommand &command) {
431
2/2
✓ Branch 3 → 4 taken 6 times.
✓ Branch 3 → 31 taken 3 times.
9 if (response.status == "OK") {
432
5/5
✓ Branch 4 → 5 taken 6 times.
✓ Branch 8 → 9 taken 6 times.
✓ Branch 9 → 10 taken 6 times.
✓ Branch 17 → 6 taken 6 times.
✓ Branch 17 → 18 taken 6 times.
12 qCInfo(lcImap) << "EXPUNGE complete, move-fallback done:"
433
2/2
✓ Branch 11 → 12 taken 6 times.
✓ Branch 12 → 13 taken 6 times.
6 << command.moveUids.size() << "UIDs to"
434
1/1
✓ Branch 13 → 14 taken 6 times.
6 << command.moveTarget;
435
4/4
✓ Branch 18 → 19 taken 6 times.
✓ Branch 19 → 20 taken 6 times.
✓ Branch 25 → 21 taken 9 times.
✓ Branch 25 → 26 taken 6 times.
15 for (qint64 uid : command.moveUids)
436
1/1
✓ Branch 22 → 23 taken 9 times.
9 m_deps.messageMoved(uid, command.moveTarget);
437 6 m_deps.messagesMoved(command.moveUids, command.moveTarget);
438
1/1
✓ Branch 28 → 29 taken 6 times.
6 completePendingMoveMutations(command, MutationOutcome::Confirmed, {},
439 6 command.copyUidEvidence);
440 } else {
441 // S106-003: copied and marked \Deleted, but not expunged — ambiguous,
442 // never a plain rejection (see handleTaggedStoreDelete).
443
6/6
✓ Branch 31 → 32 taken 3 times.
✓ Branch 35 → 36 taken 3 times.
✓ Branch 36 → 37 taken 3 times.
✓ Branch 37 → 38 taken 3 times.
✓ Branch 41 → 33 taken 3 times.
✓ Branch 41 → 42 taken 3 times.
6 qCWarning(lcImap) << "EXPUNGE failed after a successful COPY:" << response.message;
444 3 m_deps.moveError(response.message);
445
1/1
✓ Branch 47 → 48 taken 3 times.
3 completePendingMoveMutations(command, MutationOutcome::Ambiguous,
446 6 QStringLiteral("Copied, but the original could not be removed: ") +
447
1/1
✓ Branch 46 → 47 taken 3 times.
3 response.message,
448 3 command.copyUidEvidence);
449 }
450
2/2
✓ Branch 54 → 55 taken 2 times.
✓ Branch 54 → 56 taken 7 times.
9 if (!m_commands.deferredCommands().isEmpty()) {
451 2 m_commands.runNextDeferredCommand();
452 } else {
453 7 m_push.restartPush();
454 }
455 9 }
456
457 6 void ImapMutationCommands::handleTaggedCopyOnly(const TaggedResponse &response,
458 ImapPendingCommand &command) {
459 6 const qint64 mutationToken = command.mutationTokens.value(0);
460
2/2
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 49 taken 2 times.
6 if (response.status == "OK") {
461
3/3
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 3 times.
✓ Branch 8 → 9 taken 3 times.
4 auto uid = command.moveUids.isEmpty() ? -1 : command.moveUids.first();
462
6/6
✓ Branch 10 → 11 taken 4 times.
✓ Branch 14 → 15 taken 4 times.
✓ Branch 15 → 16 taken 4 times.
✓ Branch 16 → 17 taken 4 times.
✓ Branch 22 → 12 taken 4 times.
✓ Branch 22 → 23 taken 4 times.
8 qCInfo(lcImap) << "COPY complete: UID" << uid
463
2/2
✓ Branch 17 → 18 taken 4 times.
✓ Branch 18 → 19 taken 4 times.
4 << "to" << command.moveTarget;
464
1/1
✓ Branch 23 → 24 taken 4 times.
4 m_deps.messageCopied(uid, command.moveTarget);
465 static const QRegularExpression copyUidRx(
466 4 QStringLiteral(R"(\[COPYUID\s+\d+\s+[^\s\]]+\s+[^\s\]]+\])"),
467
4/7
✓ Branch 24 → 25 taken 2 times.
✓ Branch 24 → 37 taken 2 times.
✓ Branch 26 → 27 taken 2 times.
✗ Branch 26 → 37 not taken.
✓ Branch 31 → 32 taken 2 times.
✗ Branch 74 → 75 not taken.
✗ Branch 74 → 76 not taken.
8 QRegularExpression::CaseInsensitiveOption);
468
1/1
✓ Branch 38 → 39 taken 4 times.
4 const auto uidPlusMatch = copyUidRx.match(response.message);
469
1/1
✓ Branch 44 → 45 taken 4 times.
4 completeMutation(mutationToken, MutationOutcome::Confirmed, {},
470
4/4
✓ Branch 39 → 40 taken 4 times.
✓ Branch 40 → 41 taken 1 time.
✓ Branch 40 → 42 taken 3 times.
✓ Branch 41 → 43 taken 1 time.
8 uidPlusMatch.hasMatch() ? uidPlusMatch.captured(0)
471 : QString{});
472 4 } else {
473
2/2
✓ Branch 49 → 50 taken 2 times.
✓ Branch 50 → 51 taken 2 times.
2 m_deps.errorOccurred("COPY failed: " + response.message);
474 4 completeMutation(mutationToken, MutationOutcome::Rejected,
475
1/1
✓ Branch 53 → 54 taken 2 times.
2 response.message);
476 }
477
2/2
✓ Branch 58 → 59 taken 2 times.
✓ Branch 58 → 60 taken 4 times.
6 if (!m_commands.deferredCommands().isEmpty()) {
478 2 m_commands.runNextDeferredCommand();
479 } else {
480 4 m_push.restartPush();
481 }
482 6 }
483
484 8 void ImapMutationCommands::handleTaggedExpunge(const TaggedResponse &response) {
485
2/2
✓ Branch 3 → 4 taken 6 times.
✓ Branch 3 → 15 taken 2 times.
8 if (response.status == "OK") {
486
5/5
✓ Branch 4 → 5 taken 6 times.
✓ Branch 8 → 9 taken 6 times.
✓ Branch 9 → 10 taken 6 times.
✓ Branch 13 → 6 taken 6 times.
✓ Branch 13 → 14 taken 6 times.
12 qCInfo(lcImap) << "EXPUNGE complete";
487 6 m_deps.expungeComplete();
488 } else {
489
6/6
✓ Branch 15 → 16 taken 2 times.
✓ Branch 19 → 20 taken 2 times.
✓ Branch 20 → 21 taken 2 times.
✓ Branch 21 → 22 taken 2 times.
✓ Branch 25 → 17 taken 2 times.
✓ Branch 25 → 26 taken 2 times.
4 qCWarning(lcImap) << "EXPUNGE failed:" << response.message;
490 2 m_deps.expungeFailed(response.message);
491 }
492
2/2
✓ Branch 29 → 30 taken 2 times.
✓ Branch 29 → 31 taken 6 times.
8 if (!m_commands.deferredCommands().isEmpty()) {
493 2 m_commands.runNextDeferredCommand();
494 } else {
495 6 m_push.restartPush();
496 }
497 8 }
498