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: 98.2% 54 / 0 / 55
Functions: 100.0% 10 / 0 / 10
Branches: 73.4% 69 / 0 / 94

app/core/DavPushSecrets.cpp
Line Branch Exec Source
1 #include "DavPushSecrets.h"
2
3 #include "data/CredentialStore.h"
4
5 #include <QJsonDocument>
6 #include <QJsonObject>
7 #include <QPointer>
8 #include <QRandomGenerator>
9 #include <QRegularExpression>
10
11 namespace {
12 const QString kService = QStringLiteral("davpush");
13 } // namespace
14
15 3 void KeyringDavPushSecretStore::read(QObject *owner, const QString &key, ReadCallback callback) {
16
3/6
✓ Branch 2 → 3 taken 3 times.
✓ Branch 3 → 4 taken 3 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 3 times.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
3 auto *store = new CredentialStore(owner);
17
1/1
✓ Branch 6 → 7 taken 3 times.
3 QPointer<QObject> guard(owner);
18
3/5
✓ Branch 8 → 9 taken 3 times.
✓ Branch 9 → 10 taken 3 times.
✓ Branch 10 → 11 taken 3 times.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
3 store->readPassword(kService, key, [store, guard, callback](bool success, const QByteArray &password) {
19 2 store->deleteLater();
20
5/7
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 14 not taken.
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 10 taken 1 time.
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 10 not taken.
✓ Branch 11 → 12 taken 2 times.
2 if (guard) callback(success && !password.isEmpty(), password);
21 2 });
22 3 }
23
24 2 void KeyringDavPushSecretStore::write(QObject *owner, const QString &key, const QByteArray &value,
25 DoneCallback callback) {
26
3/6
✓ Branch 2 → 3 taken 2 times.
✓ Branch 3 → 4 taken 2 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 2 times.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
2 auto *store = new CredentialStore(owner);
27
1/1
✓ Branch 6 → 7 taken 2 times.
2 QPointer<QObject> guard(owner);
28
3/5
✓ Branch 8 → 9 taken 2 times.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 10 → 11 taken 2 times.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
2 store->writePassword(kService, key, value, [store, guard, callback](bool success, const QString &) {
29 2 store->deleteLater();
30
1/2
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 6 not taken.
2 if (guard) callback(success);
31 2 });
32 2 }
33
34 1 void KeyringDavPushSecretStore::remove(QObject *owner, const QString &key, DoneCallback callback) {
35
3/6
✓ Branch 2 → 3 taken 1 time.
✓ Branch 3 → 4 taken 1 time.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1 time.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
1 auto *store = new CredentialStore(owner);
36
1/1
✓ Branch 6 → 7 taken 1 time.
1 QPointer<QObject> guard(owner);
37
1/1
✓ Branch 10 → 11 taken 1 time.
1 store->deletePassword(
38 kService, key,
39
2/4
✓ Branch 8 → 9 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
2 [store, guard, callback](bool success) {
40 1 store->deleteLater();
41
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 6 not taken.
1 if (guard) callback(success);
42 1 },
43 true);
44 1 }
45
46 36 QByteArray DavPushReceiverSecret::serialize() const {
47 const QJsonObject object{
48 72 {QStringLiteral("v"), 1},
49 72 {QStringLiteral("channel"), channel},
50
2/2
✓ Branch 14 → 15 taken 36 times.
✓ Branch 15 → 16 taken 36 times.
72 {QStringLiteral("private"), QString::fromLatin1(keys.privateKeyDer.toBase64())},
51
1/1
✓ Branch 20 → 21 taken 36 times.
72 {QStringLiteral("public"), WebPushPayloadCodec::encodeBase64Url(keys.publicKey)},
52
3/5
✓ Branch 25 → 26 taken 36 times.
✓ Branch 29 → 30 taken 180 times.
✓ Branch 29 → 31 taken 36 times.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 55 not taken.
252 {QStringLiteral("auth"), WebPushPayloadCodec::encodeBase64Url(keys.authSecret)}};
53
2/2
✓ Branch 45 → 46 taken 36 times.
✓ Branch 46 → 47 taken 36 times.
72 return QJsonDocument(object).toJson(QJsonDocument::Compact);
54 252 }
55
56 16 std::optional<DavPushReceiverSecret> DavPushReceiverSecret::parse(const QByteArray &json) {
57
2/2
✓ Branch 2 → 3 taken 16 times.
✓ Branch 3 → 4 taken 16 times.
16 const QJsonObject object = QJsonDocument::fromJson(json).object();
58
4/4
✓ Branch 8 → 9 taken 16 times.
✓ Branch 9 → 10 taken 16 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 14 times.
16 if (object.value(QStringLiteral("v")).toInt() != 1) return std::nullopt;
59 14 DavPushReceiverSecret secret;
60
2/2
✓ Branch 18 → 19 taken 14 times.
✓ Branch 19 → 20 taken 14 times.
14 secret.channel = object.value(QStringLiteral("channel")).toString();
61
4/4
✓ Branch 29 → 30 taken 14 times.
✓ Branch 30 → 31 taken 14 times.
✓ Branch 31 → 32 taken 14 times.
✓ Branch 32 → 33 taken 14 times.
14 secret.keys.privateKeyDer = QByteArray::fromBase64(object.value(QStringLiteral("private")).toString().toLatin1());
62 secret.keys.publicKey =
63
3/3
✓ Branch 43 → 44 taken 14 times.
✓ Branch 44 → 45 taken 14 times.
✓ Branch 45 → 46 taken 14 times.
14 WebPushPayloadCodec::decodeBase64Url(object.value(QStringLiteral("public")).toString()).value_or(QByteArray());
64 secret.keys.authSecret =
65
3/3
✓ Branch 59 → 60 taken 14 times.
✓ Branch 60 → 61 taken 14 times.
✓ Branch 61 → 62 taken 14 times.
14 WebPushPayloadCodec::decodeBase64Url(object.value(QStringLiteral("auth")).toString()).value_or(QByteArray());
66
2/2
✓ Branch 73 → 74 taken 1 time.
✓ Branch 73 → 75 taken 13 times.
14 if (!secret.isValid()) return std::nullopt;
67 13 return secret;
68 16 }
69
70 35 QString DavPushReceiverSecret::newChannel() {
71 // ntfy recognises UnifiedPush topics only as "up" + 12 characters; with
72 // subscriber-based rate limiting (used for UnifiedPush) any other name is
73 // refused with 507. 12 uniform characters of [A-Za-z0-9] ≈ 71 bits from
74 // the system CSPRNG (rejection sampling keeps the distribution uniform).
75 static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
76 35 QString channel = QStringLiteral("up");
77
2/2
✓ Branch 16 → 7 taken 432 times.
✓ Branch 16 → 17 taken 35 times.
467 while (channel.size() < kChannelLength) {
78
2/3
✓ Branch 7 → 8 taken 432 times.
✗ Branch 7 → 21 not taken.
✓ Branch 8 → 9 taken 432 times.
432 const quint32 value = QRandomGenerator::system()->generate() & 0xFF;
79
2/2
✓ Branch 9 → 10 taken 12 times.
✓ Branch 9 → 11 taken 420 times.
432 if (value >= 248) continue; // 248 = 4 × 62
80
1/1
✓ Branch 13 → 14 taken 420 times.
420 channel.append(QLatin1Char(alphabet[value % 62]));
81 }
82 35 return channel;
83 ✗ }
84
85 4 bool DavPushReceiverSecret::hasProviderChannel() const {
86
4/7
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 15 taken 3 times.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 15 not taken.
✓ Branch 9 → 10 taken 1 time.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
5 static const QRegularExpression pattern(QStringLiteral("^up[A-Za-z0-9]{12}$"));
87
2/2
✓ Branch 16 → 17 taken 4 times.
✓ Branch 17 → 18 taken 4 times.
4 return pattern.match(channel).hasMatch();
88 }
89