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: 92.6% 88 / 0 / 95
Functions: 100.0% 11 / 0 / 11
Branches: 64.9% 183 / 0 / 282

data/SieveHistory.cpp
Line Branch Exec Source
1 #include "SieveHistory.h"
2
3 #include <QDir>
4 #include <QFile>
5 #include <QFileInfo>
6 #include <QJsonDocument>
7 #include <QJsonObject>
8 #include <QLoggingCategory>
9 #include <QSaveFile>
10 #include <QTimeZone>
11
12
4/7
✓ Branch 2 → 3 taken 6 times.
✓ Branch 2 → 8 taken 87 times.
✓ Branch 4 → 5 taken 6 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 6 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
93 Q_LOGGING_CATEGORY(lcSieveHistory, "mailjd.sieve.history")
13
14 namespace {
15 constexpr auto kStamp = "yyyyMMdd'T'HHmmsszzz";
16 }
17
18 234 SieveHistory::SieveHistory(const QString &directory, int limit)
19 234 : m_directory(directory), m_limit(qMax(1, limit)) {}
20
21 412 QString SieveHistory::historyDirectory() const {
22
2/2
✓ Branch 2 → 3 taken 412 times.
✓ Branch 6 → 7 taken 412 times.
824 return QDir(m_directory).filePath(QStringLiteral("history"));
23 }
24
25 515 bool SieveHistory::ensureDirectory(const QString &path) const {
26
1/1
✓ Branch 2 → 3 taken 515 times.
515 QDir dir(path);
27
11/17
✓ Branch 3 → 4 taken 515 times.
✓ Branch 4 → 5 taken 40 times.
✓ Branch 4 → 10 taken 475 times.
✓ Branch 6 → 7 taken 40 times.
✓ Branch 7 → 8 taken 40 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 40 times.
✓ Branch 11 → 12 taken 40 times.
✓ Branch 11 → 13 taken 475 times.
✓ Branch 13 → 14 taken 40 times.
✓ Branch 13 → 15 taken 475 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 515 times.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 26 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 28 → 30 not taken.
515 if (!dir.exists() && !QDir().mkpath(path))
28 ✗ return false;
29
1/1
✓ Branch 19 → 20 taken 515 times.
515 QFile::setPermissions(path, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
30 515 return true;
31 515 }
32
33 844 bool SieveHistory::writePrivate(const QString &path, const QByteArray &bytes) {
34
1/1
✓ Branch 2 → 3 taken 844 times.
844 QSaveFile file(path);
35
1/1
✓ Branch 4 → 5 taken 844 times.
844 return file.open(QIODevice::WriteOnly) &&
36
3/5
✓ Branch 7 → 8 taken 844 times.
✓ Branch 8 → 9 taken 844 times.
✗ Branch 8 → 15 not taken.
✓ Branch 11 → 12 taken 844 times.
✗ Branch 11 → 15 not taken.
1688 file.setPermissions(QFile::ReadOwner | QFile::WriteOwner) &&
37
4/6
✓ Branch 5 → 6 taken 844 times.
✗ Branch 5 → 15 not taken.
✓ Branch 9 → 10 taken 844 times.
✓ Branch 12 → 13 taken 844 times.
✓ Branch 13 → 14 taken 844 times.
✗ Branch 13 → 15 not taken.
3376 file.write(bytes) == bytes.size() && file.commit();
38 844 }
39
40 93 QString SieveHistory::add(const QString &scriptName, const QByteArray &script, const QDateTime &when) {
41
8/15
✓ Branch 3 → 4 taken 93 times.
✗ Branch 3 → 9 not taken.
✓ Branch 4 → 5 taken 93 times.
✓ Branch 5 → 6 taken 93 times.
✗ Branch 5 → 9 not taken.
✓ Branch 6 → 7 taken 93 times.
✓ Branch 7 → 8 taken 93 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 93 times.
✓ Branch 11 → 12 taken 93 times.
✗ Branch 11 → 13 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 93 times.
✗ Branch 164 → 165 not taken.
✗ Branch 164 → 166 not taken.
93 if (script.isEmpty() || !ensureDirectory(m_directory) || !ensureDirectory(historyDirectory()))
42 ✗ return {};
43
3/3
✓ Branch 15 → 16 taken 93 times.
✓ Branch 17 → 18 taken 93 times.
✓ Branch 18 → 19 taken 93 times.
93 const QString stamp = when.toUTC().toString(QString::fromLatin1(kStamp));
44 93 QString id;
45
1/2
✓ Branch 48 → 22 taken 93 times.
✗ Branch 48 → 49 not taken.
93 for (int n = 0; n < 1000; ++n) {
46
3/3
✓ Branch 22 → 23 taken 93 times.
✓ Branch 25 → 26 taken 93 times.
✓ Branch 26 → 27 taken 93 times.
93 id = stamp + QLatin1Char('-') + QString::number(n);
47
6/7
✓ Branch 31 → 32 taken 93 times.
✓ Branch 32 → 33 taken 93 times.
✓ Branch 36 → 37 taken 93 times.
✓ Branch 37 → 38 taken 93 times.
✓ Branch 38 → 39 taken 93 times.
✓ Branch 45 → 46 taken 93 times.
✗ Branch 45 → 47 not taken.
186 if (!QFile::exists(QDir(historyDirectory()).filePath(id + QStringLiteral(".sieve"))))
48 93 break;
49 }
50
2/2
✓ Branch 49 → 50 taken 93 times.
✓ Branch 50 → 51 taken 93 times.
93 const QDir dir(historyDirectory());
51 186 QJsonObject meta{{QStringLiteral("script"), scriptName},
52
4/6
✓ Branch 59 → 60 taken 93 times.
✓ Branch 60 → 61 taken 93 times.
✓ Branch 64 → 65 taken 186 times.
✓ Branch 64 → 66 taken 93 times.
✗ Branch 206 → 207 not taken.
✗ Branch 206 → 208 not taken.
372 {QStringLiteral("savedAt"), when.toUTC().toString(Qt::ISODateWithMs)}};
53
8/21
✓ Branch 75 → 76 taken 93 times.
✓ Branch 76 → 77 taken 93 times.
✓ Branch 77 → 78 taken 93 times.
✓ Branch 78 → 79 taken 93 times.
✗ Branch 78 → 88 not taken.
✓ Branch 104 → 105 taken 93 times.
✗ Branch 104 → 106 not taken.
✓ Branch 106 → 107 taken 93 times.
✗ Branch 106 → 108 not taken.
✓ Branch 108 → 109 taken 93 times.
✗ Branch 108 → 110 not taken.
✗ Branch 110 → 111 not taken.
✓ Branch 110 → 112 taken 93 times.
✗ Branch 257 → 258 not taken.
✗ Branch 257 → 259 not taken.
✗ Branch 261 → 262 not taken.
✗ Branch 261 → 263 not taken.
✗ Branch 265 → 266 not taken.
✗ Branch 265 → 267 not taken.
✗ Branch 269 → 270 not taken.
✗ Branch 269 → 271 not taken.
465 if (!writePrivate(dir.filePath(id + QStringLiteral(".sieve")), script) ||
54
13/33
✓ Branch 79 → 80 taken 93 times.
✓ Branch 80 → 81 taken 93 times.
✓ Branch 84 → 85 taken 93 times.
✓ Branch 85 → 86 taken 93 times.
✓ Branch 86 → 87 taken 93 times.
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 93 times.
✓ Branch 90 → 91 taken 93 times.
✗ Branch 90 → 92 not taken.
✓ Branch 92 → 93 taken 93 times.
✗ Branch 92 → 94 not taken.
✓ Branch 94 → 95 taken 93 times.
✗ Branch 94 → 96 not taken.
✓ Branch 96 → 97 taken 93 times.
✗ Branch 96 → 98 not taken.
✓ Branch 98 → 99 taken 93 times.
✗ Branch 98 → 100 not taken.
✓ Branch 100 → 101 taken 93 times.
✗ Branch 100 → 102 not taken.
✓ Branch 102 → 103 taken 93 times.
✗ Branch 102 → 104 not taken.
✗ Branch 233 → 234 not taken.
✗ Branch 233 → 235 not taken.
✗ Branch 237 → 238 not taken.
✗ Branch 237 → 239 not taken.
✗ Branch 241 → 242 not taken.
✗ Branch 241 → 243 not taken.
✗ Branch 245 → 246 not taken.
✗ Branch 245 → 247 not taken.
✗ Branch 249 → 250 not taken.
✗ Branch 249 → 251 not taken.
✗ Branch 253 → 254 not taken.
✗ Branch 253 → 255 not taken.
279 !writePrivate(dir.filePath(id + QStringLiteral(".json")), QJsonDocument(meta).toJson()))
55 ✗ return {};
56
1/1
✓ Branch 112 → 113 taken 93 times.
93 const QList<Entry> all = entries();
57
2/2
✓ Branch 138 → 114 taken 5 times.
✓ Branch 138 → 139 taken 93 times.
98 for (int i = m_limit; i < all.size(); ++i) {
58
3/3
✓ Branch 118 → 119 taken 5 times.
✓ Branch 119 → 120 taken 5 times.
✓ Branch 120 → 121 taken 5 times.
5 QFile::remove(dir.filePath(all.at(i).id + QStringLiteral(".sieve")));
59
3/3
✓ Branch 129 → 130 taken 5 times.
✓ Branch 130 → 131 taken 5 times.
✓ Branch 131 → 132 taken 5 times.
5 QFile::remove(dir.filePath(all.at(i).id + QStringLiteral(".json")));
60 }
61
7/7
✓ Branch 139 → 140 taken 93 times.
✓ Branch 143 → 144 taken 88 times.
✓ Branch 144 → 145 taken 88 times.
✓ Branch 146 → 147 taken 88 times.
✓ Branch 147 → 148 taken 88 times.
✓ Branch 155 → 141 taken 88 times.
✓ Branch 155 → 156 taken 93 times.
181 qCInfo(lcSieveHistory) << "Stored replaced version," << script.size() << "bytes; history has"
62
2/2
✓ Branch 150 → 151 taken 88 times.
✓ Branch 151 → 152 taken 88 times.
88 << qMin(all.size(), m_limit) << "versions";
63 93 return id;
64 372 }
65
66 101 QList<SieveHistory::Entry> SieveHistory::entries() const {
67 101 QList<Entry> result;
68
2/2
✓ Branch 2 → 3 taken 101 times.
✓ Branch 3 → 4 taken 101 times.
101 const QDir dir(historyDirectory());
69 const QStringList files =
70
3/5
✓ Branch 11 → 12 taken 101 times.
✓ Branch 14 → 15 taken 101 times.
✓ Branch 14 → 16 taken 101 times.
✗ Branch 104 → 105 not taken.
✗ Branch 104 → 106 not taken.
303 dir.entryList({QStringLiteral("*.sieve")}, QDir::Files, QDir::Name | QDir::Reversed);
71
2/2
✓ Branch 93 → 19 taken 482 times.
✓ Branch 93 → 94 taken 101 times.
583 for (const QString &file : files) {
72 482 Entry entry;
73
1/1
✓ Branch 21 → 22 taken 482 times.
482 entry.id = file.chopped(6);
74
3/3
✓ Branch 24 → 25 taken 482 times.
✓ Branch 25 → 26 taken 482 times.
✓ Branch 26 → 27 taken 482 times.
482 entry.size = QFileInfo(dir.filePath(file)).size();
75
3/3
✓ Branch 32 → 33 taken 482 times.
✓ Branch 33 → 34 taken 482 times.
✓ Branch 34 → 35 taken 482 times.
482 QFile meta(dir.filePath(entry.id + QStringLiteral(".json")));
76
2/3
✓ Branch 40 → 41 taken 482 times.
✓ Branch 41 → 42 taken 482 times.
✗ Branch 41 → 71 not taken.
482 if (meta.open(QIODevice::ReadOnly)) {
77
3/3
✓ Branch 42 → 43 taken 482 times.
✓ Branch 43 → 44 taken 482 times.
✓ Branch 44 → 45 taken 482 times.
482 const QJsonObject object = QJsonDocument::fromJson(meta.readAll()).object();
78
2/2
✓ Branch 50 → 51 taken 482 times.
✓ Branch 51 → 52 taken 482 times.
482 entry.scriptName = object.value(QStringLiteral("script")).toString();
79
3/3
✓ Branch 60 → 61 taken 482 times.
✓ Branch 61 → 62 taken 482 times.
✓ Branch 62 → 63 taken 482 times.
964 entry.savedAt = QDateTime::fromString(object.value(QStringLiteral("savedAt")).toString(),
80 482 Qt::ISODateWithMs);
81 482 }
82
2/3
✓ Branch 71 → 72 taken 482 times.
✗ Branch 72 → 73 not taken.
✓ Branch 72 → 85 taken 482 times.
482 if (!entry.savedAt.isValid())
83 ✗ entry.savedAt = QDateTime::fromString(entry.id.section(QLatin1Char('-'), 0, 0),
84 ✗ QString::fromLatin1(kStamp));
85
1/1
✓ Branch 86 → 87 taken 482 times.
482 entry.savedAt.setTimeZone(QTimeZone::UTC);
86
1/1
✓ Branch 88 → 89 taken 482 times.
482 result.append(entry);
87 482 }
88 101 return result;
89 303 }
90
91 32 QByteArray SieveHistory::read(const QString &id) const {
92
5/8
✓ Branch 4 → 5 taken 32 times.
✓ Branch 5 → 6 taken 32 times.
✗ Branch 5 → 9 not taken.
✓ Branch 7 → 8 taken 32 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 32 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 32 times.
32 if (id.contains(QLatin1Char('/')) || id.contains(QLatin1String("..")))
93 ✗ return {};
94
5/5
✓ Branch 13 → 14 taken 32 times.
✓ Branch 14 → 15 taken 32 times.
✓ Branch 18 → 19 taken 32 times.
✓ Branch 19 → 20 taken 32 times.
✓ Branch 20 → 21 taken 32 times.
64 QFile file(QDir(historyDirectory()).filePath(id + QStringLiteral(".sieve")));
95
4/4
✓ Branch 28 → 29 taken 32 times.
✓ Branch 29 → 30 taken 30 times.
✓ Branch 29 → 31 taken 2 times.
✓ Branch 30 → 32 taken 30 times.
32 return file.open(QIODevice::ReadOnly) ? file.readAll() : QByteArray();
96 32 }
97
98 329 bool SieveHistory::writeLastDownloaded(const QString &scriptName, const QByteArray &script,
99 const QDateTime &when) {
100
2/3
✓ Branch 2 → 3 taken 329 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 329 times.
329 if (!ensureDirectory(m_directory))
101 ✗ return false;
102
1/1
✓ Branch 5 → 6 taken 329 times.
329 const QDir dir(m_directory);
103 658 const QJsonObject meta{{QStringLiteral("script"), scriptName},
104
4/6
✓ Branch 13 → 14 taken 329 times.
✓ Branch 14 → 15 taken 329 times.
✓ Branch 18 → 19 taken 658 times.
✓ Branch 18 → 20 taken 329 times.
✗ Branch 63 → 64 not taken.
✗ Branch 63 → 65 not taken.
1316 {QStringLiteral("downloadedAt"), when.toUTC().toString(Qt::ISODateWithMs)}};
105
5/14
✓ Branch 29 → 30 taken 329 times.
✓ Branch 30 → 31 taken 329 times.
✓ Branch 31 → 32 taken 329 times.
✗ Branch 31 → 41 not taken.
✓ Branch 54 → 55 taken 329 times.
✗ Branch 54 → 56 not taken.
✓ Branch 56 → 57 taken 329 times.
✗ Branch 56 → 58 not taken.
✗ Branch 110 → 111 not taken.
✗ Branch 110 → 112 not taken.
✗ Branch 114 → 115 not taken.
✗ Branch 114 → 116 not taken.
✗ Branch 118 → 119 not taken.
✗ Branch 118 → 120 not taken.
1645 return writePrivate(dir.filePath(QStringLiteral("last.sieve")), script) &&
106
11/28
✓ Branch 32 → 33 taken 329 times.
✓ Branch 33 → 34 taken 329 times.
✓ Branch 37 → 38 taken 329 times.
✓ Branch 38 → 39 taken 329 times.
✓ Branch 39 → 40 taken 329 times.
✗ Branch 39 → 41 not taken.
✓ Branch 42 → 43 taken 329 times.
✗ Branch 42 → 44 not taken.
✓ Branch 44 → 45 taken 329 times.
✗ Branch 44 → 46 not taken.
✓ Branch 46 → 47 taken 329 times.
✗ Branch 46 → 48 not taken.
✓ Branch 48 → 49 taken 329 times.
✗ Branch 48 → 50 not taken.
✓ Branch 50 → 51 taken 329 times.
✗ Branch 50 → 52 not taken.
✓ Branch 52 → 53 taken 329 times.
✗ Branch 52 → 54 not taken.
✗ Branch 90 → 91 not taken.
✗ Branch 90 → 92 not taken.
✗ Branch 94 → 95 not taken.
✗ Branch 94 → 96 not taken.
✗ Branch 98 → 99 not taken.
✗ Branch 98 → 100 not taken.
✗ Branch 102 → 103 not taken.
✗ Branch 102 → 104 not taken.
✗ Branch 106 → 107 not taken.
✗ Branch 106 → 108 not taken.
987 writePrivate(dir.filePath(QStringLiteral("last.json")), QJsonDocument(meta).toJson());
107 1316 }
108
109 3 bool SieveHistory::hasLastDownloaded() const {
110
3/3
✓ Branch 2 → 3 taken 3 times.
✓ Branch 6 → 7 taken 3 times.
✓ Branch 7 → 8 taken 3 times.
6 return QFile::exists(QDir(m_directory).filePath(QStringLiteral("last.json")));
111 }
112
113 5 QByteArray SieveHistory::lastDownloaded(QString *scriptName, QDateTime *when) const {
114
1/1
✓ Branch 2 → 3 taken 5 times.
5 const QDir dir(m_directory);
115
2/2
✓ Branch 6 → 7 taken 5 times.
✓ Branch 7 → 8 taken 5 times.
5 QFile meta(dir.filePath(QStringLiteral("last.json")));
116
3/3
✓ Branch 12 → 13 taken 5 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 3 times.
5 if (!meta.open(QIODevice::ReadOnly))
117 2 return {};
118
3/3
✓ Branch 15 → 16 taken 3 times.
✓ Branch 16 → 17 taken 3 times.
✓ Branch 17 → 18 taken 3 times.
3 const QJsonObject object = QJsonDocument::fromJson(meta.readAll()).object();
119
1/2
✓ Branch 20 → 21 taken 3 times.
✗ Branch 20 → 32 not taken.
3 if (scriptName)
120
2/2
✓ Branch 24 → 25 taken 3 times.
✓ Branch 25 → 26 taken 3 times.
3 *scriptName = object.value(QStringLiteral("script")).toString();
121
1/2
✓ Branch 32 → 33 taken 3 times.
✗ Branch 32 → 46 not taken.
3 if (when)
122
3/3
✓ Branch 36 → 37 taken 3 times.
✓ Branch 37 → 38 taken 3 times.
✓ Branch 38 → 39 taken 3 times.
6 *when = QDateTime::fromString(object.value(QStringLiteral("downloadedAt")).toString(),
123 3 Qt::ISODateWithMs);
124
2/2
✓ Branch 49 → 50 taken 3 times.
✓ Branch 50 → 51 taken 3 times.
3 QFile file(dir.filePath(QStringLiteral("last.sieve")));
125
3/4
✓ Branch 55 → 56 taken 3 times.
✓ Branch 56 → 57 taken 3 times.
✗ Branch 56 → 58 not taken.
✓ Branch 57 → 59 taken 3 times.
3 return file.open(QIODevice::ReadOnly) ? file.readAll() : QByteArray();
126 5 }
127