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% 408 / 0 / 433
Functions: 98.5% 64 / 0 / 65
Branches: 78.7% 407 / 0 / 517

app/core/SieveSession.cpp
Line Branch Exec Source
1 #include "SieveSession.h"
2
3 #include "util/SecureUtil.h"
4
5 #include <QCryptographicHash>
6 #include <QFutureWatcher>
7 #include <QLoggingCategory>
8 #include <QtConcurrent/QtConcurrentRun>
9
10
4/7
✓ Branch 2 → 3 taken 7 times.
✓ Branch 2 → 8 taken 881 times.
✓ Branch 4 → 5 taken 7 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 7 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
888 Q_LOGGING_CATEGORY(lcSieveSession, "mailjd.sieve.session")
11
12 using Outcome = SieveSession::SaveOutcome;
13 using ErrorKind = ManageSieveClient::ErrorKind;
14
15 namespace {
16
17 332 QString shortHash(const QByteArray &bytes) {
18
4/4
✓ Branch 3 → 4 taken 332 times.
✓ Branch 4 → 5 taken 332 times.
✓ Branch 5 → 6 taken 332 times.
✓ Branch 6 → 7 taken 332 times.
332 return QString::fromLatin1(QCryptographicHash::hash(bytes, QCryptographicHash::Sha256).toHex().left(12));
19 }
20
21 } // namespace
22
23 struct SieveSession::Upload {
24 SaveJob job;
25 QByteArray bytes; // what will be uploaded (mine or merged)
26 QByteArray theirs; // server version replaced by this upload
27 QString scriptName;
28 bool newScript = false;
29 bool merged = false;
30 Sieve::MergeResult merge;
31 QString tempName;
32 };
33
34 234 SieveSession::SieveSession(const Config &config, QObject *parent)
35
3/6
✓ Branch 4 → 5 taken 234 times.
✓ Branch 5 → 6 taken 234 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 234 times.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 22 not taken.
234 : QObject(parent), m_config(config), m_client(new ManageSieveClient(this)),
36
2/2
✓ Branch 8 → 9 taken 234 times.
✓ Branch 14 → 15 taken 234 times.
468 m_history(config.storageDirectory) {}
37
38 466 SieveSession::~SieveSession() = default;
39
40 ✗ QString SieveSession::baseHash() const { return shortHash(m_base); }
41
42 192 QString SieveSession::editedScriptName() const {
43
2/2
✓ Branch 3 → 4 taken 77 times.
✓ Branch 3 → 5 taken 115 times.
192 return m_activeScript.isEmpty() ? newScriptName() : m_activeScript;
44 }
45
46 869 void SieveSession::setStatus(Status status, const QString &message) {
47
5/6
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 6 taken 867 times.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 6 not taken.
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 9 taken 867 times.
869 if (m_status == status && m_statusMessage == message)
48 2 return;
49 867 m_status = status;
50 867 m_statusMessage = message;
51 867 emit statusChanged();
52 }
53
54 588 void SieveSession::withCredentials(std::function<void(bool)> next) {
55
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 8 taken 588 times.
588 if (!m_credentials) {
56 ✗ m_client->setServer(m_config.server);
57 ✗ next(!m_config.server.password.isEmpty());
58 ✗ return;
59 }
60
1/1
✓ Branch 8 → 9 taken 588 times.
588 QPointer<SieveSession> guard(this);
61
2/4
✓ Branch 12 → 13 taken 588 times.
✓ Branch 13 → 14 taken 588 times.
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 25 not taken.
588 m_credentials([this, guard, next = std::move(next)](bool ok, const QByteArray &password) {
62
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 588 times.
588 if (!guard)
63 ✗ return;
64 588 SieveServerConfig server = m_config.server;
65 588 server.password = password;
66
1/1
✓ Branch 7 → 8 taken 588 times.
588 m_client->setServer(server);
67
1/1
✓ Branch 8 → 9 taken 588 times.
588 SecureUtil::zeroMemory(server.password);
68
4/5
✓ Branch 9 → 10 taken 585 times.
✓ Branch 9 → 13 taken 3 times.
✓ Branch 11 → 12 taken 585 times.
✗ Branch 11 → 13 not taken.
✓ Branch 14 → 15 taken 588 times.
588 next(ok && !password.isEmpty());
69 588 });
70 588 }
71
72 249 void SieveSession::load() {
73 249 const quint64 generation = ++m_loadGeneration;
74
1/1
✓ Branch 3 → 4 taken 249 times.
249 setStatus(Status::Loading);
75
1/1
✓ Branch 6 → 7 taken 249 times.
249 withCredentials([this, generation](bool haveCredentials) {
76
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 249 times.
249 if (generation != m_loadGeneration)
77 ✗ return;
78
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 14 taken 248 times.
249 if (!haveCredentials) {
79 1 ManageSieveClient::Result missing;
80 1 missing.error = ErrorKind::NoCredentials;
81 1 missing.message = QStringLiteral("No credentials");
82
1/1
✓ Branch 11 → 12 taken 1 time.
1 loadFailed(missing);
83 1 return;
84 1 }
85
1/1
✓ Branch 15 → 16 taken 248 times.
248 m_client->listScripts([this, generation](const ManageSieveClient::Result &result) {
86
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 246 times.
246 if (generation != m_loadGeneration)
87 ✗ return;
88
2/2
✓ Branch 4 → 5 taken 3 times.
✓ Branch 4 → 7 taken 243 times.
246 if (!result.ok) {
89 3 loadFailed(result);
90 3 return;
91 }
92 243 m_scripts = result.scripts;
93 243 emit scriptsChanged();
94 243 loadActive(result.scripts);
95 });
96 });
97 249 }
98
99 243 void SieveSession::loadActive(const QList<ManageSieve::ScriptEntry> &scripts) {
100 243 QString active;
101
2/2
✓ Branch 9 → 4 taken 171 times.
✓ Branch 9 → 10 taken 243 times.
414 for (const ManageSieve::ScriptEntry &entry : scripts)
102
2/2
✓ Branch 5 → 6 taken 165 times.
✓ Branch 5 → 7 taken 6 times.
171 if (entry.active)
103 165 active = entry.name;
104
2/2
✓ Branch 11 → 12 taken 78 times.
✓ Branch 11 → 31 taken 165 times.
243 if (active.isEmpty()) {
105 // D-2: no active script — the first save creates and activates "mailjd".
106
7/7
✓ Branch 12 → 13 taken 78 times.
✓ Branch 16 → 17 taken 4 times.
✓ Branch 17 → 18 taken 4 times.
✓ Branch 19 → 20 taken 4 times.
✓ Branch 20 → 21 taken 4 times.
✓ Branch 24 → 14 taken 4 times.
✓ Branch 24 → 25 taken 78 times.
82 qCInfo(lcSieveSession) << "No active script among" << scripts.size() << "scripts";
107
1/1
✓ Branch 27 → 28 taken 78 times.
78 publish(QByteArray(), QString(), false);
108 78 return;
109 }
110 165 const quint64 generation = m_loadGeneration;
111
2/2
✓ Branch 32 → 33 taken 165 times.
✓ Branch 33 → 34 taken 165 times.
165 m_client->getScript(active, [this, generation, active](const ManageSieveClient::Result &result) {
112
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 164 times.
164 if (generation != m_loadGeneration)
113 ✗ return;
114
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 7 taken 163 times.
164 if (!result.ok) {
115 1 loadFailed(result);
116 1 return;
117 }
118 163 publish(result.script, active, false);
119 });
120 243 }
121
122 241 void SieveSession::publish(const QByteArray &script, const QString &scriptName, bool offline) {
123 241 m_activeScript = scriptName;
124 241 m_base = script;
125
1/1
✓ Branch 4 → 5 taken 241 times.
241 m_loadedAt = QDateTime::currentDateTimeUtc();
126
5/6
✓ Branch 7 → 8 taken 241 times.
✗ Branch 7 → 11 not taken.
✓ Branch 9 → 10 taken 163 times.
✓ Branch 9 → 11 taken 78 times.
✓ Branch 12 → 13 taken 163 times.
✓ Branch 12 → 14 taken 78 times.
241 if (!offline && !scriptName.isEmpty())
127 163 m_history.writeLastDownloaded(scriptName, script, m_loadedAt);
128 241 const quint64 generation = m_loadGeneration;
129
2/4
✓ Branch 15 → 16 taken 241 times.
✓ Branch 16 → 17 taken 241 times.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
241 decodeScript(script, [this, generation, script, offline](Sieve::Document document) {
130
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 241 times.
241 if (generation != m_loadGeneration)
131 ✗ return;
132
1/1
✓ Branch 6 → 7 taken 241 times.
241 applyDocument(script, std::move(document));
133
2/3
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 241 times.
✓ Branch 12 → 13 taken 241 times.
241 setStatus(offline ? Status::Offline : Status::Ready);
134 });
135 241 }
136
137 // SR-11/G16: scripts above kOffThreadParseBytes are parsed on a worker
138 // thread; `done` runs on the UI thread. Every decode of a loaded, offline
139 // or just-saved script goes through here (S106-100).
140 410 void SieveSession::decodeScript(const QByteArray &script,
141 std::function<void(Sieve::Document)> done) {
142
2/2
✓ Branch 3 → 4 taken 406 times.
✓ Branch 3 → 8 taken 4 times.
410 if (script.size() <= kOffThreadParseBytes) {
143
2/2
✓ Branch 4 → 5 taken 406 times.
✓ Branch 5 → 6 taken 406 times.
406 done(Sieve::decode(script));
144 406 return;
145 }
146
3/6
✓ Branch 8 → 9 taken 4 times.
✓ Branch 9 → 10 taken 4 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 4 times.
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 32 not taken.
4 auto *watcher = new QFutureWatcher<Sieve::Document>(this);
147
1/1
✓ Branch 14 → 15 taken 4 times.
4 connect(watcher, &QFutureWatcher<Sieve::Document>::finished, this,
148 8 [watcher, done = std::move(done)] {
149 4 watcher->deleteLater();
150
2/2
✓ Branch 3 → 4 taken 4 times.
✓ Branch 4 → 5 taken 4 times.
4 done(watcher->result());
151 4 });
152
1/1
✓ Branch 17 → 18 taken 4 times.
4 const std::function<void()> probe = m_decodeProbe;
153
3/5
✓ Branch 19 → 20 taken 4 times.
✓ Branch 20 → 21 taken 4 times.
✓ Branch 21 → 22 taken 4 times.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
4 watcher->setFuture(QtConcurrent::run([script, probe] {
154
2/2
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 5 taken 1 time.
4 if (probe)
155 3 probe();
156 4 return Sieve::decode(script);
157 }));
158 4 }
159
160 410 void SieveSession::applyDocument(const QByteArray &script, Sieve::Document document) {
161 410 m_document = std::move(document);
162
9/9
✓ Branch 4 → 5 taken 410 times.
✓ Branch 8 → 9 taken 192 times.
✓ Branch 9 → 10 taken 192 times.
✓ Branch 11 → 12 taken 192 times.
✓ Branch 12 → 13 taken 192 times.
✓ Branch 13 → 14 taken 192 times.
✓ Branch 14 → 15 taken 192 times.
✓ Branch 24 → 6 taken 192 times.
✓ Branch 24 → 25 taken 410 times.
602 qCInfo(lcSieveSession) << "Script loaded:" << script.size() << "bytes, hash" << shortHash(script)
163
3/3
✓ Branch 15 → 16 taken 192 times.
✓ Branch 17 → 18 taken 192 times.
✓ Branch 18 → 19 taken 192 times.
192 << "," << m_document.blocks.size() << "blocks, read-only"
164
1/1
✓ Branch 19 → 20 taken 192 times.
192 << m_document.readOnly;
165 410 emit documentChanged();
166 410 }
167
168 5 void SieveSession::loadFailed(const ManageSieveClient::Result &result) {
169 5 m_lastError = result.error;
170
7/7
✓ Branch 2 → 3 taken 5 times.
✓ Branch 6 → 7 taken 5 times.
✓ Branch 7 → 8 taken 5 times.
✓ Branch 8 → 9 taken 5 times.
✓ Branch 9 → 10 taken 5 times.
✓ Branch 14 → 4 taken 5 times.
✓ Branch 14 → 15 taken 5 times.
10 qCInfo(lcSieveSession) << "Load failed, error" << static_cast<int>(result.error) << "code"
171
1/1
✓ Branch 10 → 11 taken 5 times.
5 << result.response.code;
172 5 QString scriptName;
173 5 QDateTime when;
174
1/1
✓ Branch 16 → 17 taken 5 times.
5 const QByteArray copy = m_history.lastDownloaded(&scriptName, &when);
175
6/7
✓ Branch 18 → 19 taken 2 times.
✓ Branch 18 → 21 taken 3 times.
✓ Branch 19 → 20 taken 2 times.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 2 times.
✓ Branch 23 → 24 taken 3 times.
✓ Branch 23 → 36 taken 2 times.
5 if (!copy.isEmpty() || m_history.hasLastDownloaded()) {
176 // S95.3.2: show the last downloaded version read-only.
177 3 m_activeScript = scriptName;
178 3 m_base = copy;
179 3 m_loadedAt = when;
180 3 const quint64 generation = m_loadGeneration;
181 3 const QString message = result.message;
182
2/4
✓ Branch 30 → 31 taken 3 times.
✓ Branch 31 → 32 taken 3 times.
✗ Branch 61 → 62 not taken.
✗ Branch 61 → 63 not taken.
3 decodeScript(copy, [this, generation, copy, message](Sieve::Document document) {
183
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3 times.
3 if (generation != m_loadGeneration)
184 ✗ return;
185
1/1
✓ Branch 6 → 7 taken 3 times.
3 applyDocument(copy, std::move(document));
186 3 setStatus(Status::Offline, message);
187 });
188 3 return;
189 3 }
190
1/1
✓ Branch 36 → 37 taken 2 times.
2 setStatus(Status::Error, result.message);
191 11 }
192
193 // --- upload pipeline ------------------------------------------------------------
194
195 190 void SieveSession::save(const QByteArray &script, const SaveOptions &options, SaveCallback callback) {
196 190 m_saveQueue.append({script, options, std::move(callback)});
197
4/4
✓ Branch 8 → 9 taken 188 times.
✓ Branch 8 → 11 taken 2 times.
✓ Branch 9 → 10 taken 187 times.
✓ Branch 9 → 11 taken 1 time.
190 if (!m_saving && !m_panelBusy)
198 187 startNextSave(); // else it starts when the scripts-panel change ends
199 380 }
200
201 378 void SieveSession::startNextSave() {
202
2/2
✓ Branch 3 → 4 taken 188 times.
✓ Branch 3 → 10 taken 190 times.
378 if (m_saveQueue.isEmpty()) {
203 188 m_saving = false;
204
2/2
✓ Branch 4 → 5 taken 186 times.
✓ Branch 4 → 9 taken 2 times.
188 if (m_status == Status::Saving)
205
1/1
✓ Branch 6 → 7 taken 186 times.
186 setStatus(Status::Ready);
206 190 return;
207 }
208 190 m_saving = true;
209
1/1
✓ Branch 10 → 11 taken 190 times.
190 auto upload = std::make_shared<Upload>();
210
1/1
✓ Branch 11 → 12 taken 190 times.
190 upload->job = m_saveQueue.takeFirst();
211 190 upload->bytes = upload->job.script;
212
1/1
✓ Branch 18 → 19 taken 190 times.
190 upload->scriptName = editedScriptName();
213 190 upload->newScript = m_activeScript.isEmpty();
214
4/4
✓ Branch 24 → 25 taken 4 times.
✓ Branch 24 → 37 taken 186 times.
✓ Branch 25 → 26 taken 2 times.
✓ Branch 25 → 37 taken 2 times.
190 if (m_status != Status::Ready && m_status != Status::Saving) {
215 // Also after a scripts-panel activation, which reloads another script:
216 // the queued edits belong to the script that was replaced (S106-101).
217 2 SaveOutcome outcome;
218 2 outcome.kind = Outcome::Kind::NotLoaded;
219 2 outcome.message = QStringLiteral("Rules are not loaded");
220
1/1
✓ Branch 33 → 34 taken 2 times.
2 finishSave(upload, outcome);
221 2 return;
222 2 }
223
1/1
✓ Branch 38 → 39 taken 188 times.
188 setStatus(Status::Saving);
224
7/7
✓ Branch 40 → 41 taken 188 times.
✓ Branch 44 → 45 taken 110 times.
✓ Branch 45 → 46 taken 110 times.
✓ Branch 47 → 48 taken 110 times.
✓ Branch 48 → 49 taken 110 times.
✓ Branch 60 → 42 taken 110 times.
✓ Branch 60 → 61 taken 188 times.
298 qCInfo(lcSieveSession) << "Upload started (" << upload->job.options.reason << "),"
225
4/4
✓ Branch 51 → 52 taken 110 times.
✓ Branch 52 → 53 taken 110 times.
✓ Branch 54 → 55 taken 110 times.
✓ Branch 55 → 56 taken 110 times.
110 << upload->bytes.size() << "bytes, hash" << shortHash(upload->bytes);
226
1/1
✓ Branch 61 → 62 taken 188 times.
188 QPointer<SieveSession> guard(this);
227
2/4
✓ Branch 64 → 65 taken 188 times.
✓ Branch 65 → 66 taken 188 times.
✗ Branch 102 → 103 not taken.
✗ Branch 102 → 104 not taken.
188 withCredentials([this, guard, upload](bool haveCredentials) {
228
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 188 times.
188 if (!guard)
229 ✗ return;
230
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 17 taken 187 times.
188 if (!haveCredentials) {
231 1 SaveOutcome outcome;
232 1 outcome.kind = Outcome::Kind::Failed;
233 1 outcome.transportError = ErrorKind::NoCredentials;
234 1 outcome.message = QStringLiteral("No credentials");
235
1/1
✓ Branch 13 → 14 taken 1 time.
1 finishSave(upload, outcome);
236 1 return;
237 1 }
238 // S106-098: capabilities are read after login(), i.e. from the
239 // connection that runs this upload.
240
2/2
✓ Branch 18 → 19 taken 187 times.
✓ Branch 19 → 20 taken 187 times.
187 m_client->login([this, upload](const ManageSieveClient::Result &result) {
241
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 7 taken 187 times.
187 if (!result.ok) {
242 ✗ finishSave(upload, failure(result));
243 ✗ return;
244 }
245 187 checkStep(upload);
246 });
247 });
248 190 }
249
250 190 void SieveSession::checkStep(const std::shared_ptr<Upload> &upload) {
251
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 7 taken 188 times.
190 if (!m_client->capabilities().hasCheckScript()) {
252 2 concurrencyStep(upload); // validated through a temporary name later
253 2 return;
254 }
255
2/2
✓ Branch 8 → 9 taken 188 times.
✓ Branch 10 → 11 taken 188 times.
188 m_client->checkScript(upload->bytes, [this, upload](const ManageSieveClient::Result &result) {
256
2/2
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 7 taken 185 times.
188 if (!result.ok) {
257
2/2
✓ Branch 3 → 4 taken 3 times.
✓ Branch 4 → 5 taken 3 times.
3 finishSave(upload, failure(result));
258 3 return;
259 }
260
2/2
✓ Branch 8 → 9 taken 3 times.
✓ Branch 8 → 11 taken 182 times.
185 if (upload->merged) {
261 3 spaceStep(upload); // merged bytes are checked; continue
262 3 return;
263 }
264 182 concurrencyStep(upload);
265 });
266 }
267
268 184 void SieveSession::concurrencyStep(const std::shared_ptr<Upload> &upload) {
269
2/2
✓ Branch 3 → 4 taken 75 times.
✓ Branch 3 → 6 taken 109 times.
184 if (upload->newScript) {
270 75 spaceStep(upload);
271 75 return;
272 }
273
2/2
✓ Branch 7 → 8 taken 109 times.
✓ Branch 9 → 10 taken 109 times.
109 m_client->getScript(upload->scriptName, [this, upload](const ManageSieveClient::Result &result) {
274
6/6
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 7 taken 107 times.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 1 time.
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 13 taken 108 times.
109 if (!result.ok && result.response.code != QLatin1String("NONEXISTENT")) {
275
2/2
✓ Branch 9 → 10 taken 1 time.
✓ Branch 10 → 11 taken 1 time.
1 finishSave(upload, failure(result));
276 1 return;
277 }
278
2/2
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 27 taken 107 times.
108 if (!result.ok) {
279 // S106-088: the script was deleted elsewhere since the load. The
280 // upload recreates it and activates it like a first script (D-2) —
281 // without SETACTIVE it would land on an inactive script while the
282 // outcome says Saved. There is nothing to merge with.
283
5/5
✓ Branch 14 → 15 taken 1 time.
✓ Branch 18 → 19 taken 1 time.
✓ Branch 19 → 20 taken 1 time.
✓ Branch 23 → 16 taken 1 time.
✓ Branch 23 → 24 taken 1 time.
2 qCInfo(lcSieveSession) << "Edited script no longer exists on the server; recreating it";
284 1 upload->newScript = true;
285 1 spaceStep(upload);
286 1 return;
287 }
288 107 upload->theirs = result.script;
289 107 const QByteArray &expected = upload->job.options.expectedServer;
290 const QByteArray &ancestor =
291
2/2
✓ Branch 32 → 33 taken 51 times.
✓ Branch 32 → 34 taken 56 times.
107 upload->job.options.renderedFrom.isNull() ? m_base : upload->job.options.renderedFrom;
292
6/6
✓ Branch 37 → 38 taken 22 times.
✓ Branch 37 → 44 taken 85 times.
✓ Branch 39 → 40 taken 17 times.
✓ Branch 39 → 44 taken 5 times.
✓ Branch 46 → 47 taken 92 times.
✓ Branch 46 → 49 taken 15 times.
124 if (result.script == ancestor || upload->job.options.keepMine ||
293
4/4
✓ Branch 41 → 42 taken 4 times.
✓ Branch 41 → 45 taken 13 times.
✓ Branch 43 → 44 taken 2 times.
✓ Branch 43 → 45 taken 2 times.
17 (!expected.isNull() && result.script == expected)) {
294 92 spaceStep(upload);
295 92 return;
296 }
297
7/7
✓ Branch 49 → 50 taken 15 times.
✓ Branch 53 → 54 taken 15 times.
✓ Branch 54 → 55 taken 15 times.
✓ Branch 55 → 56 taken 15 times.
✓ Branch 56 → 57 taken 15 times.
✓ Branch 65 → 51 taken 15 times.
✓ Branch 65 → 66 taken 15 times.
30 qCInfo(lcSieveSession) << "Server version changed since the edit's base: base" << shortHash(ancestor)
298
3/3
✓ Branch 57 → 58 taken 15 times.
✓ Branch 58 → 59 taken 15 times.
✓ Branch 59 → 60 taken 15 times.
15 << "server" << shortHash(result.script);
299
1/1
✓ Branch 67 → 68 taken 15 times.
15 upload->merge = Sieve::merge3(ancestor, upload->bytes, result.script);
300
2/2
✓ Branch 72 → 73 taken 9 times.
✓ Branch 72 → 87 taken 6 times.
15 if (!upload->merge.clean) {
301 9 SaveOutcome outcome;
302 9 outcome.kind = Outcome::Kind::Conflict;
303 9 outcome.merge = upload->merge;
304 9 outcome.theirs = result.script;
305 9 outcome.message = QStringLiteral("The rules were changed elsewhere");
306
1/1
✓ Branch 83 → 84 taken 9 times.
9 finishSave(upload, outcome);
307 9 return;
308 9 }
309
2/2
✓ Branch 88 → 89 taken 3 times.
✓ Branch 88 → 103 taken 3 times.
6 if (upload->job.options.reviewMerge) {
310 3 SaveOutcome outcome;
311 3 outcome.kind = Outcome::Kind::MergeReview;
312 3 outcome.merge = upload->merge;
313 3 outcome.theirs = result.script;
314 3 outcome.message = QStringLiteral("Merged with a version changed elsewhere; not uploaded before review");
315
1/1
✓ Branch 99 → 100 taken 3 times.
3 finishSave(upload, outcome);
316 3 return;
317 3 }
318 3 upload->bytes = upload->merge.merged;
319 3 upload->merged = true;
320
1/2
✓ Branch 109 → 110 taken 3 times.
✗ Branch 109 → 111 not taken.
3 if (m_client->capabilities().hasCheckScript())
321 3 checkStep(upload); // the merged bytes are checked before upload
322 else
323 ✗ spaceStep(upload);
324 });
325 }
326
327 171 void SieveSession::spaceStep(const std::shared_ptr<Upload> &upload) {
328
2/2
✓ Branch 3 → 4 taken 171 times.
✓ Branch 7 → 8 taken 171 times.
171 m_client->haveSpace(upload->scriptName, upload->bytes.size(),
329 342 [this, upload](const ManageSieveClient::Result &result) {
330
6/7
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 7 taken 169 times.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 13 taken 169 times.
171 if (!result.ok && result.response.code.startsWith(QLatin1String("QUOTA"))) {
331
2/2
✓ Branch 9 → 10 taken 2 times.
✓ Branch 10 → 11 taken 2 times.
2 finishSave(upload, failure(result));
332 2 return;
333 }
334
1/4
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 19 taken 169 times.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 19 not taken.
169 if (!result.ok && result.error != ErrorKind::Server) {
335 ✗ finishSave(upload, failure(result));
336 ✗ return;
337 }
338
2/2
✓ Branch 21 → 22 taken 167 times.
✓ Branch 21 → 23 taken 2 times.
169 if (m_client->capabilities().hasCheckScript())
339 167 putStep(upload);
340 else
341 2 tempValidationStep(upload);
342 });
343 171 }
344
345 2 void SieveSession::tempValidationStep(const std::shared_ptr<Upload> &upload) {
346 // Rule 3 for pre-1.0 servers: validate under a temporary name first.
347
1/1
✓ Branch 7 → 8 taken 2 times.
4 upload->tempName = QStringLiteral("mailjd-check-%1").arg(QDateTime::currentMSecsSinceEpoch());
348
2/2
✓ Branch 14 → 15 taken 2 times.
✓ Branch 17 → 18 taken 2 times.
2 m_client->putScript(upload->tempName, upload->bytes,
349 4 [this, upload](const ManageSieveClient::Result &result) {
350
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 7 taken 1 time.
2 if (!result.ok) {
351
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 4 → 5 taken 1 time.
1 finishSave(upload, failure(result));
352 1 return;
353 }
354
2/2
✓ Branch 8 → 9 taken 1 time.
✓ Branch 10 → 11 taken 1 time.
1 m_client->deleteScript(upload->tempName, [this, upload](const ManageSieveClient::Result &) {
355 1 putStep(upload);
356 1 });
357 });
358 2 }
359
360 168 void SieveSession::putStep(const std::shared_ptr<Upload> &upload) {
361
2/2
✓ Branch 3 → 4 taken 168 times.
✓ Branch 6 → 7 taken 168 times.
168 m_client->putScript(upload->scriptName, upload->bytes,
362 336 [this, upload](const ManageSieveClient::Result &result) {
363
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 7 taken 167 times.
168 if (!result.ok) {
364
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 4 → 5 taken 1 time.
1 finishSave(upload, failure(result));
365 1 return;
366 }
367
2/2
✓ Branch 9 → 10 taken 91 times.
✓ Branch 9 → 17 taken 76 times.
167 if (!upload->theirs.isEmpty())
368
2/2
✓ Branch 10 → 11 taken 91 times.
✓ Branch 13 → 14 taken 91 times.
91 m_history.add(upload->scriptName, upload->theirs);
369
2/6
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 22 taken 76 times.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 22 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 30 taken 76 times.
76 else if (!upload->newScript && !m_base.isEmpty())
370 ✗ m_history.add(upload->scriptName, m_base);
371
2/2
✓ Branch 31 → 32 taken 76 times.
✓ Branch 31 → 33 taken 91 times.
167 if (upload->newScript)
372 76 activateStep(upload);
373 else
374 91 verifyStep(upload);
375 });
376 168 }
377
378 76 void SieveSession::activateStep(const std::shared_ptr<Upload> &upload) {
379
2/2
✓ Branch 3 → 4 taken 76 times.
✓ Branch 5 → 6 taken 76 times.
76 m_client->setActive(upload->scriptName, [this, upload](const ManageSieveClient::Result &result) {
380
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 7 taken 75 times.
76 if (!result.ok) {
381
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 4 → 5 taken 1 time.
1 finishSave(upload, failure(result));
382 1 return;
383 }
384 75 m_activeScript = upload->scriptName;
385 75 verifyStep(upload);
386 });
387 76 }
388
389 166 void SieveSession::verifyStep(const std::shared_ptr<Upload> &upload) {
390
2/2
✓ Branch 3 → 4 taken 166 times.
✓ Branch 5 → 6 taken 166 times.
166 m_client->getScript(upload->scriptName, [this, upload](const ManageSieveClient::Result &result) {
391 166 SaveOutcome outcome;
392
2/2
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 5 taken 163 times.
166 outcome.kind = upload->merged ? Outcome::Kind::Merged : Outcome::Kind::Saved;
393 166 outcome.merge = upload->merge;
394 166 outcome.uploaded = upload->bytes;
395
2/4
✓ Branch 10 → 11 taken 166 times.
✗ Branch 10 → 15 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 166 times.
166 outcome.raceDetected = result.ok && result.script != upload->bytes;
396
1/2
✓ Branch 16 → 17 taken 166 times.
✗ Branch 16 → 18 not taken.
166 const QByteArray current = result.ok ? result.script : upload->bytes;
397 166 m_base = current;
398
1/1
✓ Branch 22 → 23 taken 166 times.
166 m_savedAt = QDateTime::currentDateTimeUtc();
399
1/1
✓ Branch 26 → 27 taken 166 times.
166 m_history.writeLastDownloaded(upload->scriptName, current, m_savedAt);
400
1/2
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 39 taken 166 times.
166 if (outcome.raceDetected)
401 ✗ qCInfo(lcSieveSession) << "Script changed again during the upload";
402 166 const quint64 generation = m_loadGeneration;
403
2/8
✓ Branch 42 → 43 taken 166 times.
✓ Branch 43 → 44 taken 166 times.
✗ Branch 59 → 60 not taken.
✗ Branch 59 → 61 not taken.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
✗ Branch 65 → 66 not taken.
✗ Branch 65 → 67 not taken.
166 decodeScript(current, [this, upload, outcome, current, generation](Sieve::Document document) {
404
1/2
✓ Branch 2 → 3 taken 166 times.
✗ Branch 2 → 8 not taken.
166 if (generation == m_loadGeneration)
405
1/1
✓ Branch 5 → 6 taken 166 times.
166 applyDocument(current, std::move(document));
406
1/1
✓ Branch 9 → 10 taken 166 times.
166 finishSave(upload, outcome);
407
2/2
✓ Branch 12 → 13 taken 75 times.
✓ Branch 12 → 19 taken 91 times.
166 if (upload->newScript)
408
1/1
✓ Branch 15 → 16 taken 75 times.
75 refreshScripts({}, true, {});
409 166 });
410 166 });
411 166 }
412
413 11 SieveSession::SaveOutcome SieveSession::failure(const ManageSieveClient::Result &result) const {
414 11 SaveOutcome outcome;
415
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 11 times.
11 outcome.message = result.message.isEmpty() ? result.response.text : result.message;
416 11 outcome.transportError = result.error;
417
2/2
✓ Branch 7 → 8 taken 8 times.
✓ Branch 7 → 17 taken 3 times.
11 if (result.error == ErrorKind::Server) {
418
3/3
✓ Branch 9 → 10 taken 8 times.
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 12 taken 6 times.
8 outcome.kind = result.response.code.startsWith(QLatin1String("QUOTA")) ? Outcome::Kind::Quota
419 : Outcome::Kind::Rejected;
420
1/1
✓ Branch 13 → 14 taken 8 times.
8 outcome.errors = ManageSieve::parseServerErrors(result.response.text);
421 } else {
422 3 outcome.kind = Outcome::Kind::Failed;
423 }
424 11 return outcome;
425 ✗ }
426
427 190 void SieveSession::finishSave(const std::shared_ptr<Upload> &upload, SaveOutcome outcome) {
428
7/7
✓ Branch 2 → 3 taken 190 times.
✓ Branch 6 → 7 taken 112 times.
✓ Branch 7 → 8 taken 112 times.
✓ Branch 9 → 10 taken 112 times.
✓ Branch 10 → 11 taken 112 times.
✓ Branch 22 → 4 taken 112 times.
✓ Branch 22 → 23 taken 190 times.
302 qCInfo(lcSieveSession) << "Upload finished (" << upload->job.options.reason << "): kind"
429
2/2
✓ Branch 11 → 12 taken 112 times.
✓ Branch 12 → 13 taken 112 times.
112 << static_cast<int>(outcome.kind) << "transport"
430
3/3
✓ Branch 13 → 14 taken 112 times.
✓ Branch 14 → 15 taken 112 times.
✓ Branch 16 → 17 taken 112 times.
112 << static_cast<int>(outcome.transportError) << "merged" << upload->merged
431
2/2
✓ Branch 17 → 18 taken 112 times.
✓ Branch 18 → 19 taken 112 times.
112 << "race" << outcome.raceDetected;
432
2/2
✓ Branch 23 → 24 taken 4 times.
✓ Branch 23 → 25 taken 186 times.
190 if (outcome.kind == Outcome::Kind::Failed)
433 4 m_lastError = outcome.transportError;
434
1/2
✓ Branch 27 → 28 taken 190 times.
✗ Branch 27 → 30 not taken.
190 if (upload->job.callback)
435 190 upload->job.callback(outcome);
436
2/2
✓ Branch 31 → 32 taken 166 times.
✓ Branch 31 → 34 taken 24 times.
190 if (outcome.ok())
437 166 emit saved(upload->job.options.reason);
438 190 startNextSave();
439 190 }
440
441 143 void SieveSession::check(const QByteArray &script, SaveCallback callback) {
442
1/1
✓ Branch 2 → 3 taken 143 times.
143 QPointer<SieveSession> guard(this);
443
3/7
✓ Branch 5 → 6 taken 143 times.
✓ Branch 6 → 7 taken 143 times.
✓ Branch 7 → 8 taken 143 times.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
143 withCredentials([this, guard, script, callback](bool haveCredentials) {
444
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 143 times.
143 if (!guard)
445 ✗ return;
446
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 9 taken 142 times.
143 if (!haveCredentials) {
447 1 SaveOutcome outcome;
448 1 outcome.transportError = ErrorKind::NoCredentials;
449
1/1
✓ Branch 6 → 7 taken 1 time.
1 callback(outcome);
450 1 return;
451 1 }
452
3/5
✓ Branch 10 → 11 taken 142 times.
✓ Branch 11 → 12 taken 142 times.
✓ Branch 12 → 13 taken 142 times.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 26 not taken.
142 m_client->login([this, script, callback](const ManageSieveClient::Result &login) {
453
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 7 taken 142 times.
142 if (!login.ok) {
454 ✗ callback(failure(login));
455 ✗ return;
456 }
457
2/2
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 19 taken 141 times.
142 if (!m_client->capabilities().hasCheckScript()) {
458 1 SaveOutcome outcome;
459 1 outcome.kind = Outcome::Kind::Saved;
460 1 outcome.message = QStringLiteral("unchecked");
461
1/1
✓ Branch 16 → 17 taken 1 time.
1 callback(outcome);
462 1 return;
463 1 }
464
3/3
✓ Branch 19 → 20 taken 141 times.
✓ Branch 20 → 21 taken 141 times.
✓ Branch 21 → 22 taken 141 times.
141 m_client->checkScript(script, [this, callback](const ManageSieveClient::Result &result) {
465
3/3
✓ Branch 2 → 3 taken 139 times.
✓ Branch 2 → 4 taken 2 times.
✓ Branch 4 → 5 taken 2 times.
141 SaveOutcome outcome = result.ok ? SaveOutcome{} : failure(result);
466
2/2
✓ Branch 5 → 6 taken 139 times.
✓ Branch 5 → 7 taken 2 times.
141 if (result.ok)
467 139 outcome.kind = Outcome::Kind::Saved;
468
1/1
✓ Branch 7 → 8 taken 141 times.
141 callback(outcome);
469 141 });
470 });
471 });
472 143 }
473
474 // --- scripts panel --------------------------------------------------------------
475
476 82 void SieveSession::refreshScripts(DoneCallback callback, bool ok, const QString &message) {
477
3/5
✓ Branch 2 → 3 taken 82 times.
✓ Branch 4 → 5 taken 82 times.
✓ Branch 5 → 6 taken 82 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
82 m_client->listScripts([this, callback, ok, message](const ManageSieveClient::Result &result) {
478
1/2
✓ Branch 2 → 3 taken 78 times.
✗ Branch 2 → 5 not taken.
78 if (result.ok) {
479 78 m_scripts = result.scripts;
480 78 emit scriptsChanged();
481 }
482
2/2
✓ Branch 6 → 7 taken 7 times.
✓ Branch 6 → 8 taken 71 times.
78 if (callback)
483 7 callback(ok, message);
484 78 });
485 82 }
486
487 // S106-101: scripts-panel changes and uploads exclude each other. A change
488 // is refused while a save or load runs; a save requested during a change
489 // waits for it (and then finds the reloaded state, see startNextSave()).
490 9 bool SieveSession::beginPanelChange(const DoneCallback &callback) {
491
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 24 taken 8 times.
9 if (busy()) {
492
5/5
✓ Branch 4 → 5 taken 1 time.
✓ Branch 8 → 9 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 13 → 6 taken 1 time.
✓ Branch 13 → 14 taken 1 time.
2 qCInfo(lcSieveSession) << "Scripts-panel change refused: a save or load is running";
493
1/2
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 23 not taken.
1 if (callback)
494
1/1
✓ Branch 19 → 20 taken 1 time.
1 callback(false, QStringLiteral("A save is running"));
495 1 return false;
496 }
497 8 m_panelBusy = true;
498 8 emit statusChanged();
499 8 return true;
500 }
501
502 8 SieveSession::DoneCallback SieveSession::endPanelChange(DoneCallback callback) {
503
1/1
✓ Branch 2 → 3 taken 8 times.
8 QPointer<SieveSession> guard(this);
504
0/2
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 16 not taken.
16 return [this, guard, callback = std::move(callback)](bool ok, const QString &message) {
505
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 8 times.
8 if (!guard)
506 ✗ return;
507 8 m_panelBusy = false;
508 8 emit statusChanged();
509
1/2
✓ Branch 7 → 8 taken 8 times.
✗ Branch 7 → 9 not taken.
8 if (callback)
510 8 callback(ok, message);
511
5/6
✓ Branch 9 → 10 taken 8 times.
✗ Branch 9 → 13 not taken.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 7 times.
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 16 taken 7 times.
8 if (!m_saving && !m_saveQueue.isEmpty())
512 1 startNextSave();
513
1/1
✓ Branch 6 → 7 taken 8 times.
16 };
514 8 }
515
516 5 void SieveSession::activateScript(const QString &name, DoneCallback callback) {
517
3/3
✓ Branch 2 → 3 taken 5 times.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 4 times.
5 if (!beginPanelChange(callback))
518 1 return;
519
1/1
✓ Branch 7 → 8 taken 4 times.
4 DoneCallback done = endPanelChange(std::move(callback));
520
3/5
✓ Branch 10 → 11 taken 4 times.
✓ Branch 11 → 12 taken 4 times.
✓ Branch 12 → 13 taken 4 times.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
4 withCredentials([this, name, done](bool) {
521
3/5
✓ Branch 3 → 4 taken 4 times.
✓ Branch 4 → 5 taken 4 times.
✓ Branch 5 → 6 taken 4 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
4 m_client->setActive(name, [this, name, done](const ManageSieveClient::Result &result) {
522
2/2
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 11 taken 1 time.
4 if (result.ok) {
523 // Another script is active now: reload its rules.
524
3/3
✓ Branch 4 → 5 taken 3 times.
✓ Branch 5 → 6 taken 3 times.
✓ Branch 6 → 7 taken 3 times.
3 refreshScripts([this, done](bool ok, const QString &message) {
525 3 load();
526 3 done(ok, message);
527 3 }, true, {});
528 3 return;
529 }
530 1 done(false, result.message);
531 });
532 4 });
533 4 }
534
535 2 void SieveSession::renameScript(const QString &from, const QString &to, DoneCallback callback) {
536
2/3
✓ Branch 2 → 3 taken 2 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 if (!beginPanelChange(callback))
537 ✗ return;
538
1/1
✓ Branch 7 → 8 taken 2 times.
2 DoneCallback done = endPanelChange(std::move(callback));
539
3/7
✓ Branch 11 → 12 taken 2 times.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 13 → 14 taken 2 times.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
2 withCredentials([this, from, to, done](bool) {
540
3/7
✓ Branch 4 → 5 taken 2 times.
✓ Branch 5 → 6 taken 2 times.
✓ Branch 6 → 7 taken 2 times.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 16 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 19 not taken.
2 m_client->renameScript(from, to, [this, from, to, done](const ManageSieveClient::Result &result) {
541
5/6
✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 6 not taken.
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 1 time.
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 1 time.
2 if (result.ok && m_activeScript == from)
542 1 m_activeScript = to;
543
2/2
✓ Branch 9 → 10 taken 2 times.
✓ Branch 10 → 11 taken 2 times.
2 refreshScripts(done, result.ok, result.message);
544 2 });
545 2 });
546 2 }
547
548 2 void SieveSession::deleteScript(const QString &name, DoneCallback callback) {
549
2/3
✓ Branch 2 → 3 taken 2 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 if (!beginPanelChange(callback))
550 ✗ return;
551
1/1
✓ Branch 7 → 8 taken 2 times.
2 DoneCallback done = endPanelChange(std::move(callback));
552
3/5
✓ Branch 10 → 11 taken 2 times.
✓ Branch 11 → 12 taken 2 times.
✓ Branch 12 → 13 taken 2 times.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
2 withCredentials([this, name, done](bool) {
553
3/5
✓ Branch 3 → 4 taken 2 times.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
2 m_client->getScript(name, [this, name, done](const ManageSieveClient::Result &fetched) {
554
1/2
✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 8 not taken.
2 if (fetched.ok)
555
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 4 → 5 taken 2 times.
2 m_history.add(name, fetched.script); // deleted scripts stay restorable
556
3/5
✓ Branch 9 → 10 taken 2 times.
✓ Branch 10 → 11 taken 2 times.
✓ Branch 11 → 12 taken 2 times.
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 25 not taken.
2 m_client->deleteScript(name, [this, name, done](const ManageSieveClient::Result &result) {
557 // S106-088: a server that deleted the edited (active) script leaves
558 // nothing behind the loaded rules — reload instead of keeping a base
559 // and a script name that no longer exist.
560
3/4
✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 6 not taken.
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 1 time.
2 const bool reload = result.ok && name == m_activeScript;
561
2/2
✓ Branch 8 → 9 taken 2 times.
✓ Branch 9 → 10 taken 2 times.
2 refreshScripts([this, reload, done](bool ok, const QString &message) {
562
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 1 time.
2 if (reload)
563 1 load();
564 2 done(ok, message);
565
1/1
✓ Branch 7 → 8 taken 2 times.
2 }, result.ok, result.message);
566 2 });
567 2 });
568 2 });
569 2 }
570