MailJDTourDownloadDocsQualityTestsCoverage

Which source lines and branches the tests execute. Lines 91.5 %, branches 75.0 % (minimum 85 % and 72 %). main @ 399ffe3d · 21 October 2026 · pipeline 7238.

GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 96.3% 181 / 0 / 188
Functions: 100.0% 29 / 0 / 29
Branches: 81.9% 244 / 0 / 298

service/MailPayloadExportService.cpp
Line Branch Exec Source
1 #include "MailPayloadExportService.h"
2 #include "util/AttachmentFileSecurity.h"
3 #include <QBuffer>
4 #include <QCoreApplication>
5 #include <QFutureWatcher>
6 #include <QTemporaryFile>
7 #include <QCryptographicHash>
8 #include "data/DatabaseSecurity.h"
9 #include <QtConcurrent>
10
11
1/1
✓ Branch 3 → 4 taken 32 times.
32 MailPayloadExportService::MailPayloadExportService(QObject *parent) : QObject(parent) {
12
1/1
✓ Branch 5 → 6 taken 32 times.
32 m_pool.setMaxThreadCount(1);
13
1/1
✓ Branch 6 → 7 taken 32 times.
32 m_pool.setExpiryTimeout(1000);
14
1/1
✓ Branch 8 → 9 taken 32 times.
33 connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, [this] { stop(); });
15 32 }
16 53 MailPayloadExportService::~MailPayloadExportService() { stop(); }
17 3055 MailPayloadExportService *MailPayloadExportService::shared() {
18
3/4
✓ Branch 2 → 3 taken 19 times.
✓ Branch 2 → 7 taken 3036 times.
✓ Branch 4 → 5 taken 19 times.
✗ Branch 4 → 7 not taken.
3055 static QPointer<MailPayloadExportService> service;
19
5/8
✓ Branch 8 → 9 taken 20 times.
✓ Branch 8 → 16 taken 3035 times.
✓ Branch 11 → 12 taken 20 times.
✓ Branch 12 → 13 taken 20 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 20 times.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 20 not taken.
3055 if (!service) service = new MailPayloadExportService(QCoreApplication::instance());
20 3055 return service;
21 }
22 34 void MailPayloadExportService::stop() {
23 34 m_stopping = true;
24
4/4
✓ Branch 2 → 3 taken 34 times.
✓ Branch 3 → 4 taken 34 times.
✓ Branch 10 → 5 taken 1 time.
✓ Branch 10 → 11 taken 34 times.
35 for (const auto &request : m_requests) request->cancelled.store(true);
25
1/1
✓ Branch 12 → 13 taken 34 times.
34 m_pool.waitForDone();
26 34 }
27 3031 void MailPayloadExportService::cancel(QObject *owner) {
28
4/4
✓ Branch 2 → 3 taken 3031 times.
✓ Branch 3 → 4 taken 3031 times.
✓ Branch 19 → 5 taken 11 times.
✓ Branch 19 → 20 taken 3031 times.
3042 for (const auto &request : m_requests)
29
5/6
✓ Branch 8 → 9 taken 11 times.
✗ Branch 8 → 12 not taken.
✓ Branch 11 → 12 taken 3 times.
✓ Branch 11 → 13 taken 8 times.
✓ Branch 14 → 15 taken 3 times.
✓ Branch 14 → 17 taken 8 times.
11 if (!request->owner || request->owner == owner) request->cancelled.store(true);
30 3031 }
31 22 void MailPayloadExportService::save(QObject *owner, MailPayloadRequest identity,
32 QString destination, bool overwriteExisting,
33 Callback callback) {
34
1/1
✓ Branch 9 → 10 taken 22 times.
44 enqueue(owner, std::move(identity), std::move(destination), overwriteExisting,
35 22 std::move(callback), {});
36 22 }
37 4 void MailPayloadExportService::saveInDirectory(QObject *owner, MailPayloadRequest identity,
38 QString directory, QString suggestedName,
39 Callback callback) {
40
1/1
✓ Branch 10 → 11 taken 4 times.
8 enqueue(owner, std::move(identity), std::move(directory), false,
41 8 std::move(callback), std::move(suggestedName));
42 4 }
43 26 void MailPayloadExportService::enqueue(QObject *owner, MailPayloadRequest identity,
44 QString destination, bool overwriteExisting,
45 Callback callback, std::optional<QString> suggestedName) {
46
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 25 times.
26 if (!owner) return;
47
6/6
✓ Branch 6 → 7 taken 23 times.
✓ Branch 6 → 23 taken 1 time.
✓ Branch 8 → 9 taken 22 times.
✓ Branch 8 → 23 taken 1 time.
✓ Branch 10 → 11 taken 21 times.
✓ Branch 10 → 23 taken 1 time.
46 if (m_stopping || m_requests.size() >= MaximumRequests || destination.isEmpty() ||
48
5/5
✓ Branch 12 → 13 taken 21 times.
✓ Branch 13 → 14 taken 20 times.
✓ Branch 13 → 23 taken 1 time.
✓ Branch 15 → 16 taken 19 times.
✓ Branch 15 → 23 taken 1 time.
63 destination.size() > 4096 || destination.contains(QChar(0)) ||
49
8/8
✓ Branch 4 → 5 taken 24 times.
✓ Branch 4 → 23 taken 1 time.
✓ Branch 17 → 18 taken 18 times.
✓ Branch 17 → 23 taken 1 time.
✓ Branch 19 → 20 taken 4 times.
✓ Branch 19 → 24 taken 14 times.
✓ Branch 25 → 26 taken 8 times.
✓ Branch 25 → 38 taken 17 times.
91 identity.databasePath.size() > 4096 || identity.mailId.accountId.size() > 4096 ||
50
2/2
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 24 taken 3 times.
22 (suggestedName && suggestedName->size() > 4096)) {
51 8 MailPayloadReadResult result;
52 8 result.error = QStringLiteral("Payload export queue is unavailable or exceeds its metadata budget");
53
1/1
✓ Branch 34 → 35 taken 8 times.
8 callback(std::move(result));
54 8 return;
55 8 }
56
3/7
✓ Branch 40 → 41 taken 17 times.
✓ Branch 47 → 48 taken 17 times.
✓ Branch 48 → 49 taken 17 times.
✗ Branch 66 → 67 not taken.
✗ Branch 66 → 68 not taken.
✗ Branch 69 → 70 not taken.
✗ Branch 69 → 71 not taken.
34 submit(owner, [identity = std::move(identity), destination = std::move(destination),
57 17 overwriteExisting, suggestedName = std::move(suggestedName)](const std::atomic_bool &cancelled) {
58 17 MailPayloadReadResult result;
59
2/2
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 12 taken 14 times.
17 if (cancelled.load()) {
60 3 result.error = QStringLiteral("Payload export cancelled");
61 3 return Result{result, {}};
62 }
63 14 const QString outputPath = suggestedName
64
6/8
✓ Branch 13 → 14 taken 3 times.
✓ Branch 13 → 17 taken 11 times.
✓ Branch 15 → 16 taken 3 times.
✓ Branch 16 → 18 taken 3 times.
✓ Branch 18 → 19 taken 3 times.
✓ Branch 18 → 20 taken 11 times.
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 40 not taken.
14 ? AttachmentFileSecurity::uniqueSavePath(QDir(destination), *suggestedName) : destination;
65 14 QString error;
66
2/2
✓ Branch 20 → 21 taken 3 times.
✓ Branch 20 → 22 taken 11 times.
14 const auto policy = overwriteExisting ? AttachmentFileSecurity::ExistingFilePolicy::Replace
67 : AttachmentFileSecurity::ExistingFilePolicy::FailIfExists;
68
1/1
✓ Branch 25 → 26 taken 14 times.
14 const bool installed = AttachmentFileSecurity::writeStreamAtomically(outputPath,
69
1/1
✓ Branch 24 → 25 taken 14 times.
28 [&](QIODevice &output, QString *error) {
70
1/1
✓ Branch 2 → 3 taken 14 times.
14 result = MailPayloadReader::copy(identity, output, &cancelled);
71
3/4
✓ Branch 5 → 6 taken 7 times.
✓ Branch 5 → 8 taken 7 times.
✓ Branch 6 → 7 taken 7 times.
✗ Branch 6 → 8 not taken.
14 if (!result.completed && error) *error = result.error;
72 14 return result.completed;
73 35 }, &error, policy, [&] { return cancelled.load(); });
74
2/2
✓ Branch 28 → 29 taken 8 times.
✓ Branch 28 → 31 taken 6 times.
14 if (!installed) {
75 8 result.completed = false;
76
1/1
✓ Branch 29 → 30 taken 8 times.
8 result.sha256.clear();
77 8 result.error = error;
78 }
79 14 return Result{result, {}};
80
1/1
✓ Branch 4 → 5 taken 14 times.
65 }, [callback = std::move(callback)](Result result) { callback(std::move(result.read)); });
81 }
82
83 19 void MailPayloadExportService::readWindow(QObject *owner, MailPayloadRequest request,
84 BytesCallback callback) {
85
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 19 times.
19 if (!owner) return;
86
1/2
✓ Branch 6 → 7 taken 19 times.
✗ Branch 6 → 19 not taken.
19 if (m_stopping || m_requests.size() >= MaximumRequests ||
87
3/4
✓ Branch 7 → 8 taken 19 times.
✗ Branch 7 → 19 not taken.
✓ Branch 8 → 9 taken 18 times.
✓ Branch 8 → 19 taken 1 time.
19 request.skipBytes < 0 || request.maxBytes <= 0 ||
88
3/4
✓ Branch 9 → 10 taken 17 times.
✓ Branch 9 → 19 taken 1 time.
✓ Branch 11 → 12 taken 17 times.
✗ Branch 11 → 19 not taken.
35 request.maxBytes > MaximumWindowBytes ||
89
1/2
✓ Branch 13 → 14 taken 17 times.
✗ Branch 13 → 19 not taken.
34 request.databasePath.isEmpty() || request.databasePath.size() > 4096 ||
90
6/9
✓ Branch 4 → 5 taken 19 times.
✗ Branch 4 → 19 not taken.
✓ Branch 15 → 16 taken 17 times.
✓ Branch 16 → 17 taken 17 times.
✗ Branch 16 → 19 not taken.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 17 times.
✓ Branch 21 → 22 taken 2 times.
✓ Branch 21 → 36 taken 17 times.
38 request.databasePath.contains(QChar(0)) || request.mailId.accountId.size() > 4096) {
91 2 MailPayloadReadResult result;
92 2 result.error = QStringLiteral("Invalid source window request");
93
1/1
✓ Branch 31 → 32 taken 2 times.
2 callback({}, std::move(result));
94 2 return;
95 2 }
96
3/3
✓ Branch 38 → 39 taken 17 times.
✓ Branch 41 → 42 taken 17 times.
✓ Branch 42 → 43 taken 17 times.
17 submit(owner, [request = std::move(request)](const std::atomic_bool &cancelled) {
97 17 QByteArray bytes;
98
1/1
✓ Branch 3 → 4 taken 17 times.
17 bytes.reserve(int(qMin<qint64>(request.maxBytes, MailPayloadReader::ChunkBytes)));
99
1/1
✓ Branch 4 → 5 taken 17 times.
17 QBuffer buffer(&bytes);
100
1/1
✓ Branch 6 → 7 taken 17 times.
17 buffer.open(QIODevice::WriteOnly);
101
1/1
✓ Branch 7 → 8 taken 17 times.
17 auto result = MailPayloadReader::copy(request, buffer, &cancelled);
102
1/1
✓ Branch 8 → 9 taken 17 times.
17 buffer.close();
103
3/3
✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 11 taken 15 times.
✓ Branch 10 → 11 taken 2 times.
17 if (!result.completed) bytes.clear();
104 34 return Result{result, {}, bytes};
105 51 }, [callback = std::move(callback)](Result result) {
106
1/1
✓ Branch 6 → 7 taken 17 times.
17 callback(std::move(result.bytes), std::move(result.read));
107 17 });
108 }
109
110 48 void MailPayloadExportService::submit(QObject *owner, Work work, std::function<void(Result)> callback) {
111
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 48 times.
50 if (!owner) return;
112
6/6
✓ Branch 4 → 5 taken 47 times.
✓ Branch 4 → 7 taken 1 time.
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 46 times.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 22 taken 46 times.
48 if (m_stopping || m_requests.size() >= MaximumRequests) {
113 2 Result result;
114 2 result.read.error = QStringLiteral("Payload queue is unavailable");
115
1/1
✓ Branch 18 → 19 taken 2 times.
2 callback(std::move(result));
116 2 return;
117 2 }
118
1/1
✓ Branch 22 → 23 taken 46 times.
46 auto request = std::make_shared<Request>();
119
1/1
✓ Branch 24 → 25 taken 46 times.
46 request->owner = owner;
120 46 const std::weak_ptr<Request> weak = request;
121
1/1
✓ Branch 27 → 28 taken 46 times.
92 request->destroyed = connect(owner, &QObject::destroyed, this, [weak] {
122
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 7 not taken.
1 if (const auto request = weak.lock()) request->cancelled.store(true);
123 46 });
124
1/1
✓ Branch 32 → 33 taken 46 times.
46 m_requests.append(request);
125
3/6
✓ Branch 33 → 34 taken 46 times.
✓ Branch 34 → 35 taken 46 times.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 46 times.
✗ Branch 65 → 66 not taken.
✗ Branch 65 → 67 not taken.
46 auto *watcher = new QFutureWatcher<Result>(this);
126 46 connect(watcher, &QFutureWatcher<Result>::finished, this,
127
1/3
✓ Branch 40 → 41 taken 46 times.
✗ Branch 70 → 71 not taken.
✗ Branch 70 → 72 not taken.
92 [this, request, watcher, callback = std::move(callback)]() mutable {
128
1/1
✓ Branch 2 → 3 taken 45 times.
45 auto result = watcher->result();
129
1/1
✓ Branch 3 → 4 taken 45 times.
45 watcher->deleteLater();
130
1/1
✓ Branch 4 → 5 taken 45 times.
45 m_requests.removeOne(request);
131
8/9
✓ Branch 5 → 6 taken 45 times.
✗ Branch 5 → 13 not taken.
✓ Branch 8 → 9 taken 44 times.
✓ Branch 8 → 13 taken 1 time.
✓ Branch 11 → 12 taken 41 times.
✓ Branch 11 → 13 taken 3 times.
✓ Branch 14 → 15 taken 41 times.
✓ Branch 14 → 20 taken 4 times.
✓ Branch 17 → 18 taken 41 times.
45 if (!m_stopping && request->owner && !request->cancelled.load()) callback(std::move(result));
132 45 });
133
2/4
✓ Branch 46 → 47 taken 46 times.
✓ Branch 47 → 48 taken 46 times.
✗ Branch 79 → 80 not taken.
✗ Branch 79 → 81 not taken.
46 watcher->setFuture(QtConcurrent::run(&m_pool, [request, work = std::move(work)] {
134 46 return work(request->cancelled);
135 }));
136 46 }
137
138 5 MailPayloadPreview::~MailPayloadPreview() { QFile::remove(m_path); }
139
140 16 void MailPayloadExportService::preparePreview(QObject *owner, MailPayloadRequest identity,
141 QString cacheDirectory, PreviewCallback callback) {
142
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 15 times.
16 if (!owner) return;
143
7/7
✓ Branch 7 → 8 taken 13 times.
✓ Branch 7 → 15 taken 1 time.
✓ Branch 9 → 10 taken 13 times.
✓ Branch 10 → 11 taken 12 times.
✓ Branch 10 → 15 taken 1 time.
✓ Branch 12 → 13 taken 11 times.
✓ Branch 12 → 15 taken 1 time.
41 if (cacheDirectory.isEmpty() || cacheDirectory.size() > 4096 || cacheDirectory.contains(QChar(0)) ||
144
6/6
✓ Branch 5 → 6 taken 14 times.
✓ Branch 5 → 15 taken 1 time.
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 16 taken 10 times.
✓ Branch 17 → 18 taken 5 times.
✓ Branch 17 → 31 taken 10 times.
41 identity.databasePath.size() > 4096 || identity.mailId.accountId.size() > 4096) {
145 5 MailPayloadReadResult result;
146 5 result.error = QStringLiteral("Invalid preview metadata");
147
1/1
✓ Branch 26 → 27 taken 5 times.
5 callback({}, result);
148 5 return;
149 5 }
150
3/5
✓ Branch 33 → 34 taken 10 times.
✓ Branch 38 → 39 taken 10 times.
✓ Branch 39 → 40 taken 10 times.
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 62 not taken.
10 submit(owner, [identity = std::move(identity), cacheDirectory = std::move(cacheDirectory)]
151 (const std::atomic_bool &cancelled) {
152 8 Result result;
153
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 8 times.
8 if (cancelled.load()) return result;
154
1/1
✓ Branch 5 → 6 taken 8 times.
8 QFileInfo directory(cacheDirectory);
155
7/8
✓ Branch 6 → 7 taken 8 times.
✓ Branch 7 → 8 taken 5 times.
✓ Branch 7 → 11 taken 3 times.
✓ Branch 8 → 9 taken 5 times.
✓ Branch 9 → 10 taken 5 times.
✗ Branch 9 → 11 not taken.
✓ Branch 12 → 13 taken 5 times.
✓ Branch 12 → 19 taken 3 times.
8 if (!directory.exists() && !directory.isSymLink()) {
156
2/2
✓ Branch 14 → 15 taken 5 times.
✓ Branch 15 → 16 taken 5 times.
5 QDir().mkpath(cacheDirectory);
157
1/1
✓ Branch 18 → 19 taken 5 times.
5 directory.refresh();
158 }
159
4/4
✓ Branch 19 → 20 taken 8 times.
✓ Branch 21 → 22 taken 7 times.
✓ Branch 22 → 23 taken 6 times.
✓ Branch 22 → 31 taken 1 time.
15 if (!directory.isDir() || directory.isSymLink() ||
160
2/3
✓ Branch 23 → 24 taken 6 times.
✓ Branch 24 → 25 taken 6 times.
✗ Branch 24 → 31 not taken.
6 !DatabaseSecurity::isOwnedByCurrentUser(directory) ||
161
4/5
✓ Branch 20 → 21 taken 7 times.
✓ Branch 20 → 31 taken 1 time.
✓ Branch 25 → 26 taken 6 times.
✓ Branch 26 → 27 taken 6 times.
✗ Branch 26 → 31 not taken.
21 DatabaseSecurity::isSharedSystemDirectory(cacheDirectory) ||
162
4/5
✓ Branch 29 → 30 taken 6 times.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 6 times.
✓ Branch 33 → 34 taken 2 times.
✓ Branch 33 → 41 taken 6 times.
14 !QFile::setPermissions(cacheDirectory, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner)) {
163 2 result.read.error = QStringLiteral("Cannot create private preview directory");
164 2 return result;
165 }
166
3/3
✓ Branch 41 → 42 taken 6 times.
✓ Branch 45 → 46 taken 6 times.
✓ Branch 46 → 47 taken 6 times.
12 QTemporaryFile file(QDir(cacheDirectory).filePath(QStringLiteral("mailjd-preview-XXXXXX.pdf")));
167
2/3
✓ Branch 51 → 52 taken 6 times.
✗ Branch 52 → 53 not taken.
✓ Branch 52 → 57 taken 6 times.
6 if (!file.open()) {
168 ✗ result.read.error = file.errorString();
169 ✗ return result;
170 }
171
1/1
✓ Branch 57 → 58 taken 6 times.
6 result.read = MailPayloadReader::copy(identity, file, &cancelled);
172
2/2
✓ Branch 60 → 61 taken 1 time.
✓ Branch 60 → 62 taken 5 times.
6 if (!result.read.completed) return result;
173
4/7
✓ Branch 62 → 63 taken 5 times.
✓ Branch 63 → 64 taken 5 times.
✗ Branch 63 → 66 not taken.
✗ Branch 65 → 66 not taken.
✓ Branch 65 → 67 taken 5 times.
✗ Branch 68 → 69 not taken.
✓ Branch 68 → 77 taken 5 times.
5 if (!file.flush() || cancelled.load()) {
174 ✗ result.read.completed = false;
175 ✗ result.read.sha256.clear();
176 ✗ result.read.error = QStringLiteral("Preview flush failed or was cancelled");
177 ✗ return result;
178 }
179
1/1
✓ Branch 77 → 78 taken 5 times.
5 file.close();
180
4/7
✓ Branch 78 → 79 taken 5 times.
✓ Branch 80 → 81 taken 5 times.
✓ Branch 82 → 83 taken 5 times.
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 5 times.
✗ Branch 122 → 123 not taken.
✗ Branch 122 → 124 not taken.
5 result.preview = Preview(new MailPayloadPreview(file.fileName(), result.read));
181
1/1
✓ Branch 89 → 90 taken 5 times.
5 file.setAutoRemove(false);
182 5 return result;
183 28 }, [callback = std::move(callback)](Result result) {
184
1/1
✓ Branch 6 → 7 taken 8 times.
8 callback(std::move(result.preview), std::move(result.read));
185 8 });
186 }
187
188 9 void MailPayloadExportService::savePreview(QObject *owner, Preview preview, QString destination,
189 Callback callback) {
190
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 8 times.
9 if (!owner) return;
191
11/11
✓ Branch 5 → 6 taken 7 times.
✓ Branch 5 → 13 taken 1 time.
✓ Branch 7 → 8 taken 6 times.
✓ Branch 7 → 13 taken 1 time.
✓ Branch 9 → 10 taken 5 times.
✓ Branch 9 → 13 taken 1 time.
✓ Branch 11 → 12 taken 5 times.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 14 taken 4 times.
✓ Branch 15 → 16 taken 4 times.
✓ Branch 15 → 27 taken 4 times.
8 if (!preview || destination.isEmpty() || destination.size() > 4096 || destination.contains(QChar(0))) {
192 4 MailPayloadReadResult result;
193 4 result.error = QStringLiteral("Invalid preview export");
194
1/1
✓ Branch 23 → 24 taken 4 times.
4 callback(result);
195 4 return;
196 4 }
197
3/5
✓ Branch 29 → 30 taken 4 times.
✓ Branch 34 → 35 taken 4 times.
✓ Branch 35 → 36 taken 4 times.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 55 not taken.
4 submit(owner, [preview = std::move(preview), destination = std::move(destination)]
198 (const std::atomic_bool &cancelled) {
199 4 Result result;
200 4 QString error;
201
1/1
✓ Branch 4 → 5 taken 4 times.
4 result.read.completed = AttachmentFileSecurity::writeStreamAtomically(destination,
202
1/1
✓ Branch 3 → 4 taken 4 times.
8 [&](QIODevice &output, QString *error) {
203
1/1
✓ Branch 4 → 5 taken 4 times.
4 QFile input(preview->path());
204
4/4
✓ Branch 6 → 7 taken 4 times.
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 12 taken 3 times.
✓ Branch 8 → 9 taken 1 time.
4 if (!input.open(QIODevice::ReadOnly)) { *error = input.errorString(); return false; }
205
1/1
✓ Branch 12 → 13 taken 3 times.
3 QCryptographicHash hash(QCryptographicHash::Sha256);
206
1/1
✓ Branch 13 → 14 taken 3 times.
3 QByteArray buffer(MailPayloadReader::ChunkBytes, Qt::Uninitialized);
207
1/2
✓ Branch 37 → 15 taken 10 times.
✗ Branch 37 → 38 not taken.
10 while (!cancelled.load()) {
208
2/2
✓ Branch 16 → 17 taken 10 times.
✓ Branch 17 → 18 taken 10 times.
10 const qint64 bytes = input.read(buffer.data(), buffer.size());
209
1/3
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 23 taken 10 times.
✗ Branch 19 → 20 not taken.
10 if (bytes < 0) { *error = input.errorString(); return false; }
210
2/2
✓ Branch 23 → 24 taken 3 times.
✓ Branch 23 → 25 taken 7 times.
10 if (!bytes) break;
211
2/3
✓ Branch 26 → 27 taken 7 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 32 taken 7 times.
7 if (output.write(buffer.constData(), bytes) != bytes) {
212 ✗ *error = output.errorString(); return false;
213 }
214 7 hash.addData(QByteArrayView(buffer.constData(), bytes));
215 7 result.read.bytes += bytes;
216 }
217
2/2
✓ Branch 38 → 39 taken 3 times.
✓ Branch 39 → 40 taken 3 times.
3 result.read.sha256 = hash.result().toHex();
218
5/6
✓ Branch 44 → 45 taken 3 times.
✗ Branch 44 → 52 not taken.
✓ Branch 47 → 48 taken 2 times.
✓ Branch 47 → 52 taken 1 time.
✓ Branch 54 → 55 taken 2 times.
✓ Branch 54 → 62 taken 1 time.
5 if (cancelled.load() || result.read.bytes != preview->result().bytes ||
219
2/2
✓ Branch 51 → 52 taken 1 time.
✓ Branch 51 → 53 taken 1 time.
2 result.read.sha256 != preview->result().sha256) {
220 2 *error = QStringLiteral("Preview changed or export was cancelled");
221 2 return false;
222 }
223 1 return true;
224 4 }, &error, AttachmentFileSecurity::ExistingFilePolicy::Replace,
225 9 [&] { return cancelled.load(); });
226
3/3
✓ Branch 7 → 8 taken 3 times.
✓ Branch 7 → 10 taken 1 time.
✓ Branch 8 → 9 taken 3 times.
4 if (!result.read.completed) { result.read.sha256.clear(); result.read.error = error; }
227 4 return result;
228
1/1
✓ Branch 4 → 5 taken 4 times.
16 }, [callback = std::move(callback)](Result result) { callback(std::move(result.read)); });
229 }
230